]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4834 Add technical debt in issues changelog
authorJulien Lancelot <julien.lancelot@gmail.com>
Sun, 3 Nov 2013 21:35:21 +0000 (22:35 +0100)
committerJulien Lancelot <julien.lancelot@gmail.com>
Sun, 3 Nov 2013 21:35:21 +0000 (22:35 +0100)
17 files changed:
sonar-core/src/test/java/org/sonar/core/technicaldebt/TechnicalDebtConverterTest.java
sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/FieldDiffs.java
sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/TechnicalDebt.java
sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogDiffFormat.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/technicalDebt/InternalRubyTechnicalDebtService.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/technicalDebt/TechnicalDebtFormatter.java [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/models/internal.rb
sonar-server/src/main/webapp/WEB-INF/app/models/issue.rb
sonar-server/src/main/webapp/WEB-INF/app/views/issue/_issue.html.erb
sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java
sonar-server/src/test/java/org/sonar/server/technicaldebt/InternalRubyTechnicalDebtServiceTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/technicaldebt/TechnicalDebtFormatterTest.java [new file with mode: 0644]
sonar-ws-client/src/test/java/org/sonar/wsclient/issue/internal/IssueJsonParserTest.java
sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/changelog-with-technical-debt.json [new file with mode: 0644]

index 1c521975f32f970cf923efc76349f79216b8f587..68ea3739d3b2fdec7e6f5eaceca006f781cadedf 100644 (file)
@@ -55,16 +55,16 @@ public class TechnicalDebtConverterTest {
 
   @Test
   public void convert_simple_values() {
-    checkValues(converter.fromMinutes(15L), 15L, 0L, 0L);
-    checkValues(converter.fromMinutes(120L), 0L, 2L, 0L);
-    checkValues(converter.fromMinutes(720L), 0L, 0L, 1L);
+    checkValues(converter.fromMinutes(15L), 15, 0, 0);
+    checkValues(converter.fromMinutes(120L), 0, 2, 0);
+    checkValues(converter.fromMinutes(720L), 0, 0, 1);
   }
 
   @Test
   public void convert_complex_values() {
-    checkValues(converter.fromMinutes(70L), 10L, 1L, 0L);
-    checkValues(converter.fromMinutes(730L), 10L, 0L, 1L);
-    checkValues(converter.fromMinutes(790L), 10L, 1L, 1L);
+    checkValues(converter.fromMinutes(70L), 10, 1, 0);
+    checkValues(converter.fromMinutes(730L), 10, 0, 1);
+    checkValues(converter.fromMinutes(790L), 10, 1, 1);
   }
 
   @Test
@@ -77,7 +77,7 @@ public class TechnicalDebtConverterTest {
     assertThat(converter.toDays(TechnicalDebt.of(45, 6, 1))).isEqualTo(1.5625);
   }
   
-  private void checkValues(TechnicalDebt technicalDebt, Long expectedMinutes, Long expectedHours, Long expectedDays) {
+  private void checkValues(TechnicalDebt technicalDebt, int expectedMinutes, int expectedHours, int expectedDays) {
     assertThat(technicalDebt.minutes()).isEqualTo(expectedMinutes);
     assertThat(technicalDebt.hours()).isEqualTo(expectedHours);
     assertThat(technicalDebt.days()).isEqualTo(expectedDays);
index 386de94e09eac23d958168b2072ae304a6003114..2be7b500f598ac18285f5a36a79df143b68ad8bd 100644 (file)
@@ -141,10 +141,12 @@ public class FieldDiffs implements Serializable {
       this.newValue = newValue;
     }
 
+    @CheckForNull
     public T oldValue() {
       return oldValue;
     }
 
+    @CheckForNull
     public T newValue() {
       return newValue;
     }
index 5fdc21e3d79b6bc6c4c03c7ab89847f9d2f74768..6c0509294e20b1e3f48c8bd7038c63f76b5e4b4b 100644 (file)
@@ -69,15 +69,15 @@ public class TechnicalDebt implements Serializable {
     return days * DAY + hours * HOUR + minutes * MINUTE;
   }
 
-  public long days() {
+  public int days() {
     return days;
   }
 
-  public long hours() {
+  public int hours() {
     return hours;
   }
 
-  public long minutes() {
+  public int minutes() {
     return minutes;
   }
 
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogDiffFormat.java b/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogDiffFormat.java
new file mode 100644 (file)
index 0000000..214f85e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.issue;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class IssueChangelogDiffFormat {
+
+  private String oldValue, newValue;
+
+  public IssueChangelogDiffFormat(@Nullable String oldValue, @Nullable String newValue) {
+    this.oldValue = oldValue;
+    this.newValue = newValue;
+  }
+
+  @CheckForNull
+  public String oldValue() {
+    return oldValue;
+  }
+
+  @CheckForNull
+  public String newValue() {
+    return newValue;
+  }
+
+}
index 1923c026ac449ef73013370441a2fbde85462c5c..ac9586fd14b5abf68c0f6118304a6c4720d21026 100644 (file)
@@ -21,8 +21,12 @@ package org.sonar.server.issue;
 
 import org.sonar.api.ServerComponent;
 import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.api.technicaldebt.TechnicalDebt;
 import org.sonar.core.i18n.I18nManager;
+import org.sonar.core.issue.IssueUpdater;
+import org.sonar.server.technicaldebt.TechnicalDebtFormatter;
 
+import java.io.Serializable;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -32,9 +36,11 @@ import static com.google.common.collect.Lists.newArrayList;
 public class IssueChangelogFormatter implements ServerComponent {
 
   private final I18nManager i18nManager;
+  private final TechnicalDebtFormatter technicalDebtFormatter;
 
-  public IssueChangelogFormatter(I18nManager i18nManager) {
+  public IssueChangelogFormatter(I18nManager i18nManager, TechnicalDebtFormatter technicalDebtFormatter) {
     this.i18nManager = i18nManager;
+    this.technicalDebtFormatter = technicalDebtFormatter;
   }
 
   public List<String> format(Locale locale, FieldDiffs diffs) {
@@ -42,15 +48,15 @@ public class IssueChangelogFormatter implements ServerComponent {
     for (Map.Entry<String, FieldDiffs.Diff> entry : diffs.diffs().entrySet()) {
       StringBuilder message = new StringBuilder();
       String key = entry.getKey();
-      FieldDiffs.Diff diff = entry.getValue();
-      if (diff.newValue() != null && !diff.newValue().equals("")) {
-        message.append(i18nManager.message(locale, "issue.changelog.changed_to", null, i18nManager.message(locale, "issue.changelog.field." + key, null), diff.newValue()));
+      IssueChangelogDiffFormat diffFormat = format(locale, key, entry.getValue());
+      if (diffFormat.newValue() != null) {
+        message.append(i18nManager.message(locale, "issue.changelog.changed_to", null, i18nManager.message(locale, "issue.changelog.field." + key, null), diffFormat.newValue()));
       } else {
         message.append(i18nManager.message(locale, "issue.changelog.removed", null, i18nManager.message(locale, "issue.changelog.field." + key, null)));
       }
-      if (diff.oldValue() != null && !diff.oldValue().equals("")) {
+      if (diffFormat.oldValue() != null) {
         message.append(" (");
-        message.append(i18nManager.message(locale, "issue.changelog.was", null, diff.oldValue()));
+        message.append(i18nManager.message(locale, "issue.changelog.was", null, diffFormat.oldValue()));
         message.append(")");
       }
       result.add(message.toString());
@@ -58,4 +64,21 @@ public class IssueChangelogFormatter implements ServerComponent {
     return result;
   }
 
+  private IssueChangelogDiffFormat format(Locale locale, String key, FieldDiffs.Diff diff) {
+    Serializable newValue = diff.newValue();
+    Serializable oldValue = diff.oldValue();
+
+    String newValueString = newValue != null && !newValue.equals("") ? diff.newValue().toString() : null;
+    String oldValueString = oldValue != null && !oldValue.equals("") ? diff.oldValue().toString() : null;
+    if (IssueUpdater.TECHNICAL_DEBT.equals(key)) {
+      if (newValueString != null) {
+        newValueString = technicalDebtFormatter.format(locale, TechnicalDebt.fromLong(Long.parseLong(newValueString)));
+      }
+      if (oldValueString != null) {
+        oldValueString = technicalDebtFormatter.format(locale, TechnicalDebt.fromLong(Long.parseLong(oldValueString)));
+      }
+    }
+    return new IssueChangelogDiffFormat(oldValueString, newValueString);
+  }
+
 }
index 9bd3b2071ad7c3f8360ec1e09d6e4dc5ac52da4c..5af173d2839890bc31821fb52c6bca9c9ba7426b 100644 (file)
@@ -94,6 +94,8 @@ import org.sonar.server.rule.RubyRuleService;
 import org.sonar.server.rules.ProfilesConsole;
 import org.sonar.server.rules.RulesConsole;
 import org.sonar.server.startup.*;
+import org.sonar.server.technicaldebt.InternalRubyTechnicalDebtService;
+import org.sonar.server.technicaldebt.TechnicalDebtFormatter;
 import org.sonar.server.text.MacroInterpreter;
 import org.sonar.server.text.RubyTextService;
 import org.sonar.server.ui.*;
@@ -300,10 +302,12 @@ public final class Platform {
     servicesContainer.addSingleton(RubyRuleService.class);
 
     // technical debt
+    servicesContainer.addSingleton(InternalRubyTechnicalDebtService.class);
     servicesContainer.addSingleton(TechnicalDebtManager.class);
     servicesContainer.addSingleton(TechnicalDebtModelRepository.class);
     servicesContainer.addSingleton(TechnicalDebtXMLImporter.class);
     servicesContainer.addSingleton(TechnicalDebtConverter.class);
+    servicesContainer.addSingleton(TechnicalDebtFormatter.class);
 
     // text
     servicesContainer.addSingleton(MacroInterpreter.class);
diff --git a/sonar-server/src/main/java/org/sonar/server/technicalDebt/InternalRubyTechnicalDebtService.java b/sonar-server/src/main/java/org/sonar/server/technicalDebt/InternalRubyTechnicalDebtService.java
new file mode 100644 (file)
index 0000000..60a9f14
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.technicaldebt;
+
+import org.sonar.api.ServerComponent;
+import org.sonar.api.technicaldebt.TechnicalDebt;
+import org.sonar.server.user.UserSession;
+
+public class InternalRubyTechnicalDebtService implements ServerComponent {
+
+  private final TechnicalDebtFormatter technicalDebtFormatter;
+
+  public InternalRubyTechnicalDebtService(TechnicalDebtFormatter technicalDebtFormatter) {
+    this.technicalDebtFormatter = technicalDebtFormatter;
+  }
+
+  public String format(TechnicalDebt technicalDebt){
+    return technicalDebtFormatter.format(UserSession.get().locale(), technicalDebt);
+  }
+
+  public TechnicalDebt toTechnicalDebt(String technicalDebtInLong){
+    return TechnicalDebt.fromLong(Long.parseLong(technicalDebtInLong));
+  }
+
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/technicalDebt/TechnicalDebtFormatter.java b/sonar-server/src/main/java/org/sonar/server/technicalDebt/TechnicalDebtFormatter.java
new file mode 100644 (file)
index 0000000..7e7f467
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.technicaldebt;
+
+import org.sonar.api.ServerComponent;
+import org.sonar.api.technicaldebt.TechnicalDebt;
+import org.sonar.core.i18n.I18nManager;
+
+import java.util.Locale;
+
+public class TechnicalDebtFormatter implements ServerComponent {
+
+  private final I18nManager i18nManager;
+
+  public TechnicalDebtFormatter(I18nManager i18nManager) {
+    this.i18nManager = i18nManager;
+  }
+
+  public String format(Locale locale, TechnicalDebt technicalDebt) {
+    StringBuilder message = new StringBuilder();
+    if (technicalDebt.days() > 0) {
+      message.append(i18nManager.message(locale, "issue.technical_debt.x_days", null, technicalDebt.days()));
+    }
+    if (technicalDebt.hours() > 0) {
+      if (message.length() > 0) {
+        message.append(" ");
+      }
+      message.append(i18nManager.message(locale, "issue.technical_debt.x_hours", null, technicalDebt.hours()));
+    }
+    // Do not display minutes if days is not null to not have too much information
+    if (technicalDebt.minutes() > 0 && technicalDebt.days() == 0) {
+      if (message.length() > 0) {
+        message.append(" ");
+      }
+      message.append(i18nManager.message(locale, "issue.technical_debt.x_minutes", null, technicalDebt.minutes()));
+    }
+    return message.toString();
+  }
+}
index 7070f829c6e4dc33f45031ba1102f406b9700ea2..c43de64b4d77e5b394ef5a285afeb7d8385d779a 100644 (file)
@@ -54,6 +54,9 @@ class Internal
     component(Java::OrgSonarServerPermission::InternalPermissionTemplateService.java_class)
   end
 
+  def self.technical_debt
+    component(Java::OrgSonarServerTechnicaldebt::InternalRubyTechnicalDebtService.java_class)
+  end
 
   private
 
index 095321ac3d401bb0c2c8ee5377dc0f99f0a9d65a..3c5d561803deccca8f3b277e7f8830c30869be73 100644 (file)
@@ -73,8 +73,13 @@ class Issue
         diff = entry.getValue()
         hash_diff = {}
         hash_diff[:key] = key
-        hash_diff[:newValue] = diff.newValue() if diff.newValue.present?
-        hash_diff[:oldValue] = diff.oldValue() if diff.oldValue.present?
+        if key == 'technicalDebt'
+          hash_diff[:newValue] = technical_debt_to_hash(Internal.technical_debt.toTechnicalDebt(diff.newValue())) if diff.newValue.present?
+          hash_diff[:oldValue] = technical_debt_to_hash(Internal.technical_debt.toTechnicalDebt(diff.oldValue())) if diff.oldValue.present?
+        else
+          hash_diff[:newValue] = diff.newValue() if diff.newValue.present?
+          hash_diff[:oldValue] = diff.oldValue() if diff.oldValue.present?
+        end
         hash_change[:diffs] << hash_diff
       end
 
index 77bbe090616e7fe36eae4ce4b868bd73851244a2..d73587fc55ea02319ca8143f8129ea1b6009d23b 100644 (file)
     <% if issue.technicalDebt %>
       <img src="<%= ApplicationController.root_context -%>/images/sep12.png"/>
       &nbsp;
-      <% technical_debt = issue.technicalDebt
-           days = technical_debt.days
-           hours = technical_debt.hours
-           minutes = technical_debt.minutes
-           message = ''
-           message += message('issue.technical_debt.x_days', :params => days) + ' ' if days > 0
-           message += message('issue.technical_debt.x_hours', :params => hours) + ' ' if hours > 0
-           # Do not display minutes if days is not null to not have too much information
-           message += message('issue.technical_debt.x_minutes', :params => minutes) if minutes > 0 && days == 0
-      %>
       <%= message('issue.technical_debt') -%>&nbsp;<a href="#" onclick="return toggleTechnicalDebt(this)" class="gray issue-technicaldebt-link"
-           id="toggle-issue-technicaldebt"><%= message -%></a>
+           id="toggle-issue-technicaldebt"><%= Internal.technical_debt.format(issue.technicalDebt) -%></a>
         &nbsp;
     <% end %>
   </div>
index 9a8865a5dcc47e9a51c5da73c2a627500f2243b4..850d665e4b3790ea2e3f6eae09a5de94db9413b1 100644 (file)
@@ -39,6 +39,7 @@ import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.user.UserSession;
 
 import java.util.Collections;
+import java.util.Locale;
 import java.util.Map;
 
 import static com.google.common.collect.Lists.newArrayList;
@@ -573,6 +574,13 @@ public class InternalRubyIssueServiceTest {
     verify(issueBulkChangeService).execute(any(IssueBulkChangeQuery.class), any(UserSession.class));
   }
 
+  @Test
+  public void format_changelog() {
+    FieldDiffs fieldDiffs = new FieldDiffs();
+    service.formatChangelog(fieldDiffs);
+    verify(issueChangelogFormatter).format(any(Locale.class), eq(fieldDiffs));
+  }
+
   private void checkBadRequestException(Exception e, String key, Object... params) {
     BadRequestException exception = (BadRequestException) e;
     assertThat(exception.l10nKey()).isEqualTo(key);
index 6de106c78b8a190e7641e068c511818d0e95e6d1..c1e65a0af78fbf451655bde7a1a78c943a797660 100644 (file)
@@ -25,7 +25,9 @@ import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.api.technicaldebt.TechnicalDebt;
 import org.sonar.core.i18n.I18nManager;
+import org.sonar.server.technicaldebt.TechnicalDebtFormatter;
 
 import java.util.List;
 import java.util.Locale;
@@ -41,11 +43,14 @@ public class IssueChangelogFormatterTest {
   @Mock
   private I18nManager i18nManager;
 
+  @Mock
+  private TechnicalDebtFormatter technicalDebtFormatter;
+
   private IssueChangelogFormatter formatter;
 
   @Before
   public void before(){
-    formatter = new IssueChangelogFormatter(i18nManager);
+    formatter = new IssueChangelogFormatter(i18nManager, technicalDebtFormatter);
   }
 
   @Test
@@ -120,4 +125,52 @@ public class IssueChangelogFormatterTest {
     assertThat(message).isEqualTo("Severity removed");
   }
 
+  @Test
+  public void format_technical_debt_with_old_and_new_value(){
+    FieldDiffs diffs = new FieldDiffs();
+    diffs.setDiff("technicalDebt", "500", "10000");
+
+    when(technicalDebtFormatter.format(DEFAULT_LOCALE, TechnicalDebt.of(0, 5, 0))).thenReturn("5 hours");
+    when(technicalDebtFormatter.format(DEFAULT_LOCALE, TechnicalDebt.of(0, 0, 1))).thenReturn("1 days");
+
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt");
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days");
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.changelog.was", null, "5 hours")).thenReturn("was 5 hours");
+
+    List<String> result = formatter.format(DEFAULT_LOCALE, diffs);
+    assertThat(result).hasSize(1);
+    String message = result.get(0);
+    assertThat(message).isEqualTo("Technical Debt changed to 1 days (was 5 hours)");
+  }
+
+  @Test
+  public void format_technical_debt_with_new_value_only(){
+    FieldDiffs diffs = new FieldDiffs();
+    diffs.setDiff("technicalDebt", null, "10000");
+
+    when(technicalDebtFormatter.format(DEFAULT_LOCALE, TechnicalDebt.of(0, 0, 1))).thenReturn("1 days");
+
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt");
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days");
+
+    List<String> result = formatter.format(DEFAULT_LOCALE, diffs);
+    assertThat(result).hasSize(1);
+    String message = result.get(0);
+    assertThat(message).isEqualTo("Technical Debt changed to 1 days");
+  }
+
+  @Test
+  public void format_technical_debt_without_value(){
+    FieldDiffs diffs = new FieldDiffs();
+    diffs.setDiff("technicalDebt", null, null);
+
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt");
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.changelog.removed", null, "Technical Debt")).thenReturn("Technical Debt removed");
+
+    List<String> result = formatter.format(DEFAULT_LOCALE, diffs);
+    assertThat(result).hasSize(1);
+    String message = result.get(0);
+    assertThat(message).isEqualTo("Technical Debt removed");
+  }
+
 }
diff --git a/sonar-server/src/test/java/org/sonar/server/technicaldebt/InternalRubyTechnicalDebtServiceTest.java b/sonar-server/src/test/java/org/sonar/server/technicaldebt/InternalRubyTechnicalDebtServiceTest.java
new file mode 100644 (file)
index 0000000..5cfa4b3
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.technicaldebt;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.technicaldebt.TechnicalDebt;
+
+import java.util.Locale;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(MockitoJUnitRunner.class)
+public class InternalRubyTechnicalDebtServiceTest {
+
+  @Mock
+  private TechnicalDebtFormatter technicalDebtFormatter;
+
+  private InternalRubyTechnicalDebtService service;
+
+  @Before
+  public void before() {
+    service = new InternalRubyTechnicalDebtService(technicalDebtFormatter);
+  }
+
+  @Test
+  public void format() {
+    TechnicalDebt technicalDebt = TechnicalDebt.of(5, 0, 0);
+    service.format(technicalDebt);
+    verify(technicalDebtFormatter).format(any(Locale.class), eq(technicalDebt));
+  }
+
+  @Test
+  public void to_technical_debt() {
+    assertThat(service.toTechnicalDebt("500")).isEqualTo(TechnicalDebt.of(0, 5, 0));
+  }
+}
diff --git a/sonar-server/src/test/java/org/sonar/server/technicaldebt/TechnicalDebtFormatterTest.java b/sonar-server/src/test/java/org/sonar/server/technicaldebt/TechnicalDebtFormatterTest.java
new file mode 100644 (file)
index 0000000..a9d2c4a
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.technicaldebt;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.technicaldebt.TechnicalDebt;
+import org.sonar.core.i18n.I18nManager;
+
+import java.util.Locale;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TechnicalDebtFormatterTest {
+
+  private static final Locale DEFAULT_LOCALE = Locale.getDefault();
+
+  @Mock
+  private I18nManager i18nManager;
+
+  private TechnicalDebtFormatter formatter;
+
+  @Before
+  public void before() {
+    formatter = new TechnicalDebtFormatter(i18nManager);
+  }
+
+  @Test
+  public void format() {
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.technical_debt.x_days", null, 5)).thenReturn("5 days");
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.technical_debt.x_hours", null, 2)).thenReturn("2 hours");
+    when(i18nManager.message(DEFAULT_LOCALE, "issue.technical_debt.x_minutes", null, 1)).thenReturn("1 minutes");
+
+    assertThat(formatter.format(DEFAULT_LOCALE, TechnicalDebt.of(0, 0, 5))).isEqualTo("5 days");
+    assertThat(formatter.format(DEFAULT_LOCALE, TechnicalDebt.of(0, 2, 0))).isEqualTo("2 hours");
+    assertThat(formatter.format(DEFAULT_LOCALE, TechnicalDebt.of(1, 0, 0))).isEqualTo("1 minutes");
+
+    assertThat(formatter.format(DEFAULT_LOCALE, TechnicalDebt.of(0, 2, 5))).isEqualTo("5 days 2 hours");
+    assertThat(formatter.format(DEFAULT_LOCALE, TechnicalDebt.of(1, 2, 0))).isEqualTo("2 hours 1 minutes");
+    assertThat(formatter.format(DEFAULT_LOCALE, TechnicalDebt.of(1, 2, 5))).isEqualTo("5 days 2 hours");
+  }
+
+
+}
index d7c5e22a07b7d034a55952c2c3ee3308eb7251f5..87f86d26c4e2bb1d8c582a227ed6c4380e486c1d 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.wsclient.issue.internal;
 
 import org.apache.commons.io.IOUtils;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.sonar.wsclient.base.Paging;
 import org.sonar.wsclient.component.Component;
@@ -240,6 +241,25 @@ public class IssueJsonParserTest {
     assertThat(diff2Change2.oldValue()).isEqualTo("RESOLVED");
   }
 
+  @Test
+  @Ignore
+  public void should_parse_changelog_with_technical_debt() throws Exception {
+    String json = IOUtils.toString(getClass().getResourceAsStream("/org/sonar/wsclient/issue/internal/IssueJsonParserTest/changelog-with-technical-debt.json"));
+    List<IssueChange> changes = new IssueJsonParser().parseChangelog(json);
+
+    assertThat(changes).hasSize(1);
+    IssueChange change = changes.get(0);
+    assertThat(change.user()).isEqualTo("julien");
+    assertThat(change.createdAt().getTime()).isEqualTo(1383202235000l);
+    assertThat(change.updatedAt().getTime()).isEqualTo(1383202235000l);
+
+    assertThat(change.diffs()).hasSize(1);
+    IssueChangeDiff diffChange1 = change.diffs().get(0);
+    assertThat(diffChange1.key()).isEqualTo("technicalDebt");
+    assertThat(diffChange1.newValue()).isEqualTo("1.0");
+    assertThat(diffChange1.oldValue()).isNull();
+  }
+
   @Test
   public void should_parse_bulk_change() throws Exception {
     String json = IOUtils.toString(getClass().getResourceAsStream("/org/sonar/wsclient/issue/internal/IssueJsonParserTest/bulk-change.json"));
diff --git a/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/changelog-with-technical-debt.json b/sonar-ws-client/src/test/resources/org/sonar/wsclient/issue/internal/IssueJsonParserTest/changelog-with-technical-debt.json
new file mode 100644 (file)
index 0000000..cb1acd7
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "changelog": [
+    {
+      "user": "julien",
+      "createdAt": "2013-10-31T07:50:35+0100",
+      "updatedAt": "2013-10-31T07:50:35+0100",
+      "diffs": [
+        {
+          "key": "technicalDebt",
+          "oldValue": {
+            "days": 3,
+            "hours": 0,
+            "minutes": 10
+          },
+          "newValue": {
+            "days": 2,
+            "hours": 1,
+            "minutes": 0
+          }
+        }
+      ]
+    }
+  ]
+}
+