]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4996 Update work duration message and move code to i18n API
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 3 Mar 2014 10:49:18 +0000 (11:49 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 3 Mar 2014 10:49:28 +0000 (11:49 +0100)
24 files changed:
plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/issues.html.erb
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/technical_debt_pyramid.html.erb
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java
sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java
sonar-core/src/main/java/org/sonar/core/i18n/WorkDurationFormatter.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/i18n/DefaultI18nTest.java
sonar-core/src/test/java/org/sonar/core/i18n/WorkDurationFormatterTest.java [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/i18n/corePlugin/org/sonar/l10n/core.properties
sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java
sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java
sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowWsHandler.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/technicaldebt/DebtService.java
sonar-server/src/main/java/org/sonar/server/ui/JRubyI18n.java
sonar-server/src/main/java/org/sonar/server/ui/WorkDurationFormatter.java [deleted file]
sonar-server/src/main/webapp/WEB-INF/app/models/internal.rb
sonar-server/src/main/webapp/WEB-INF/app/models/project_measure.rb
sonar-server/src/main/webapp/WEB-INF/app/views/issue/_issue.html.erb
sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java
sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowWsHandlerTest.java
sonar-server/src/test/java/org/sonar/server/technicaldebt/DebtServiceTest.java
sonar-server/src/test/java/org/sonar/server/ui/JRubyI18nTest.java
sonar-server/src/test/java/org/sonar/server/ui/WorkDurationFormatterTest.java [deleted file]

index b5081cf1c548b1dd8b481c0cb070dc4bd5afbbc3..723a04ccbfeccd5ff5c15d6e5b783f49907b90ec 100644 (file)
@@ -249,12 +249,9 @@ since_previous_version.short=\u0394 version
 since_previous_version_detailed=since previous version ({0} - {1})
 since_previous_version_detailed.short=\u0394 version ({0})
 time_changes=Time changes
-work_duration.x_days={0} days
-work_duration.x_days.short={0}d
-work_duration.x_hours={0} hours
-work_duration.x_hours.short={0}h
-work_duration.x_minutes={0} minutes
-work_duration.x_minutes.short={0}min
+work_duration.x_days={0}d
+work_duration.x_hours={0}h
+work_duration.x_minutes={0}min
 
 #------------------------------------------------------------------------------
 #
index 9b297d38804b2f58bee7dee5522148e04e9772f1..db627c8cdd8d67ce47f676849adba3770ee5feb0 100644 (file)
@@ -71,7 +71,7 @@
                 <br/>
                 <span style="font-weight: bold">
                   <%= message('widget.rules.removed') -%>&nbsp;
-                  <span class="varb"><%= Internal.work_duration_formatter.format(estimated_cleared_technical_debt, 'SHORT') -%></span>
+                  <span class="varb"><%= Internal.i18n.formatWorkDuration(estimated_cleared_technical_debt) -%></span>
                 </span>
               <% end %>
             <% end %>
index 8831a94def9aafaf5ab326957c2acfffc3a592f7..6ee8536cdfcbd696f8414358a5d7a693b6c86c84 100644 (file)
           </a>
         </td>
         <td class="val value-debt-<%= h(characteristic.key) -%>">
-          <a href="<%= drilldown_url -%>" class="link-debt-<%= h(characteristic.key) -%>"><%= Internal.work_duration_formatter.format(value.to_i, 'SHORT') -%></a>
+          <a href="<%= drilldown_url -%>" class="link-debt-<%= h(characteristic.key) -%>"><%= Internal.i18n.formatWorkDuration(value.to_i) -%></a>
           <% if should_display_diff_measures %>
             <% if diff_by_characteristic_id[characteristic.id] %>
               <%= format_variation(measure) -%>
           <% end %>
         </td>
         <td class="val value-total-<%= h(characteristic.key) -%>">
-          <%= Internal.work_duration_formatter.format(cumulated.to_i, 'SHORT') -%>
+          <%= Internal.i18n.formatWorkDuration(cumulated.to_i) -%>
           <%
              if should_display_diff_measures
                css_style = 'var'
                css_style += 'b' if total_diff < 0
                css_style += 'w' if total_diff > 0
-               diff_to_display = (total_diff < 0 ? '' : '+') + Internal.work_duration_formatter.format(total_diff.to_i, 'SHORT')
+               diff_to_display = (total_diff < 0 ? '' : '+') + Internal.i18n.formatWorkDuration(total_diff.to_i)
           %>
             <span class="<%= css_style -%>"><b>(<%= diff_to_display -%>)</b></span>
           <%
index a30a49bfb25c16f9baae3c490f8368f213e1e9d5..da73f5f7e95d4a40da4baaab7f8b25b1cc8c22c3 100644 (file)
@@ -26,11 +26,13 @@ import org.sonar.api.platform.ComponentContainer;
 import org.sonar.api.platform.PluginMetadata;
 import org.sonar.api.utils.HttpDownloader;
 import org.sonar.api.utils.UriReader;
+import org.sonar.api.utils.WorkDurationFactory;
 import org.sonar.api.utils.internal.TempFolderCleaner;
 import org.sonar.batch.components.*;
 import org.sonar.core.config.Logback;
 import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.core.i18n.RuleI18nManager;
+import org.sonar.core.i18n.WorkDurationFormatter;
 import org.sonar.core.metric.CacheMetricFinder;
 import org.sonar.core.persistence.*;
 import org.sonar.core.purge.PurgeProfiler;
@@ -123,7 +125,10 @@ public class BootstrapContainer extends ComponentContainer {
       PastSnapshotFinderByVersion.class,
       PastSnapshotFinderByPreviousVersion.class,
       PastMeasuresLoader.class,
-      PastSnapshotFinder.class);
+      PastSnapshotFinder.class,
+      WorkDurationFormatter.class,
+      WorkDurationFactory.class
+    );
   }
 
   @Override
index abf12bc0890e493b54e295e4326541123785c6fb..8737323a147eee4c16ddb8c8dcd6588d90c967b8 100644 (file)
@@ -43,9 +43,11 @@ import java.text.MessageFormat;
 import java.util.*;
 
 public class DefaultI18n implements I18n, ServerExtension, BatchExtension, Startable {
+
   private static final Logger LOG = LoggerFactory.getLogger(DefaultI18n.class);
 
   public static final String BUNDLE_PACKAGE = "org.sonar.l10n.";
+  private final WorkDurationFormatter workDurationFormatter;
 
   private PluginRepository pluginRepository;
   private I18nClassloader i18nClassloader;
@@ -53,13 +55,14 @@ public class DefaultI18n implements I18n, ServerExtension, BatchExtension, Start
   private final ResourceBundle.Control control;
   private final System2 system2;
 
-  public DefaultI18n(PluginRepository pluginRepository) {
-    this(pluginRepository, System2.INSTANCE);
+  public DefaultI18n(PluginRepository pluginRepository, WorkDurationFormatter workDurationFormatter) {
+    this(pluginRepository, workDurationFormatter, System2.INSTANCE);
   }
 
   @VisibleForTesting
-  DefaultI18n(PluginRepository pluginRepository, System2 system2) {
+  DefaultI18n(PluginRepository pluginRepository, WorkDurationFormatter workDurationFormatter, System2 system2) {
     this.pluginRepository = pluginRepository;
+    this.workDurationFormatter = workDurationFormatter;
     this.system2 = system2;
     // SONAR-2927
     this.control = new ResourceBundle.Control() {
@@ -144,6 +147,23 @@ public class DefaultI18n implements I18n, ServerExtension, BatchExtension, Start
     return DateFormat.getDateInstance(DateFormat.DEFAULT, locale).format(date);
   }
 
+  @Override
+  public String formatWorkDuration(Locale locale, long duration) {
+    if (duration == 0) {
+      return "0";
+    }
+    List<WorkDurationFormatter.Result> results = workDurationFormatter.format(duration);
+    StringBuilder message = new StringBuilder();
+    for (WorkDurationFormatter.Result result : results) {
+      if (result.key().equals(" ")) {
+        message.append(" ");
+      } else {
+        message.append(message(locale, result.key(), null, result.value()));
+      }
+    }
+    return message.toString();
+  }
+
   /**
    * Only the given locale is searched. Contrary to java.util.ResourceBundle, no strategy for locating the bundle is implemented in
    * this method.
diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/WorkDurationFormatter.java b/sonar-core/src/main/java/org/sonar/core/i18n/WorkDurationFormatter.java
new file mode 100644 (file)
index 0000000..16e0713
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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.core.i18n;
+
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.utils.WorkDuration;
+import org.sonar.api.utils.WorkDurationFactory;
+
+import javax.annotation.CheckForNull;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class WorkDurationFormatter implements ServerComponent, BatchExtension {
+
+  private final WorkDurationFactory workDurationFactory;
+
+  public WorkDurationFormatter(WorkDurationFactory workDurationFactory) {
+    this.workDurationFactory = workDurationFactory;
+  }
+
+  public List<Result> format(long durationInSeconds) {
+    List<Result> results = newArrayList();
+    if (durationInSeconds == 0) {
+      return newArrayList(new Result("0", null));
+    }
+    boolean isNegative = durationInSeconds < 0;
+    Long absDuration = Math.abs(durationInSeconds);
+    WorkDuration workDuration = workDurationFactory.createFromMinutes(absDuration);
+    if (workDuration.days() > 0) {
+      results.add(message("work_duration.x_days", isNegative ? -1 * workDuration.days() : workDuration.days()));
+    }
+    if (workDuration.hours() > 0 && workDuration.days() < 10) {
+      if (!results.isEmpty()) {
+        results.add(new Result(" ", null));
+      }
+      results.add(message("work_duration.x_hours", isNegative && results.isEmpty() ? -1 * workDuration.hours() : workDuration.hours()));
+    }
+    if (workDuration.minutes() > 0 && workDuration.hours() < 10 && workDuration.days() == 0) {
+      if (!results.isEmpty()) {
+        results.add(new Result(" ", null));
+      }
+      results.add(message("work_duration.x_minutes", isNegative && results.isEmpty() ? -1 * workDuration.minutes() : workDuration.minutes()));
+    }
+    return results;
+  }
+
+  private Result message(String key, @CheckForNull Object parameter) {
+    return new Result(key, parameter);
+  }
+
+  static class Result {
+    private String key;
+    private Object value;
+
+    Result(String key, Object value) {
+      this.key = key;
+      this.value = value;
+    }
+
+    String key() {
+      return key;
+    }
+
+    Object value() {
+      return value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+
+      Result result = (Result) o;
+
+      if (key != null ? !key.equals(result.key) : result.key != null) {
+        return false;
+      }
+      if (value != null ? !value.equals(result.value) : result.value != null) {
+        return false;
+      }
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      int result = key != null ? key.hashCode() : 0;
+      result = 31 * result + (value != null ? value.hashCode() : 0);
+      return result;
+    }
+
+    @Override
+    public String toString() {
+      return new ReflectionToStringBuilder(this, ToStringStyle.SIMPLE_STYLE).toString();
+    }
+  }
+}
index 3c5bc0d6d48510e1780b66322e59372397d5a1ce..f225a1b0e4024e0f02d758a61dc3df1690b3e444 100644 (file)
@@ -35,6 +35,7 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
+import static com.google.common.collect.Lists.newArrayList;
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -45,6 +46,10 @@ public class DefaultI18nTest {
   @Mock
   System2 system2;
 
+  @Mock
+  WorkDurationFormatter workDurationFormatter;
+
+
   DefaultI18n manager;
 
   @Before
@@ -56,7 +61,7 @@ public class DefaultI18nTest {
     I18nClassloader i18nClassloader = new I18nClassloader(new ClassLoader[]{
       newCoreClassloader(), newFrenchPackClassloader(), newSqaleClassloader(), newCheckstyleClassloader()
     });
-    manager = new DefaultI18n(pluginRepository, system2);
+    manager = new DefaultI18n(pluginRepository, workDurationFormatter, system2);
     manager.doStart(i18nClassloader);
   }
 
@@ -194,6 +199,23 @@ public class DefaultI18nTest {
     assertThat(manager.formatDate(Locale.ENGLISH, DateUtils.parseDate("2014-01-22"))).isEqualTo("Jan 22, 2014");
   }
 
+  @Test
+  public void format_work_duration() {
+    when(workDurationFormatter.format(10)).thenReturn(newArrayList(
+      new WorkDurationFormatter.Result("work_duration.x_days", 5),
+      new WorkDurationFormatter.Result(" ", null),
+      new WorkDurationFormatter.Result("work_duration.x_hours", 2),
+      new WorkDurationFormatter.Result(" ", null),
+      new WorkDurationFormatter.Result("work_duration.x_minutes", 1)
+    ));
+    assertThat(manager.formatWorkDuration(Locale.ENGLISH, 10)).isEqualTo("5d 2h 1min");
+  }
+
+  @Test
+  public void format_work_duration_when_0() {
+    assertThat(manager.formatWorkDuration(Locale.ENGLISH, 0)).isEqualTo("0");
+  }
+
   static URLClassLoader newCoreClassloader() {
     return newClassLoader("/org/sonar/core/i18n/corePlugin/");
   }
diff --git a/sonar-core/src/test/java/org/sonar/core/i18n/WorkDurationFormatterTest.java b/sonar-core/src/test/java/org/sonar/core/i18n/WorkDurationFormatterTest.java
new file mode 100644 (file)
index 0000000..5185afd
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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.core.i18n;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.WorkDurationFactory;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+
+public class WorkDurationFormatterTest {
+
+  static final int HOURS_IN_DAY = 8;
+
+  static final long ONE_MINUTE = 1L;
+  static final long ONE_HOUR = ONE_MINUTE * 60;
+  static final long ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
+
+  Settings settings;
+
+  WorkDurationFormatter formatter;
+
+  @Before
+  public void setUp() throws Exception {
+    settings = new Settings();
+    settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(HOURS_IN_DAY));
+    formatter = new WorkDurationFormatter(new WorkDurationFactory(settings));
+  }
+
+  @Test
+  public void format() {
+    assertThat(formatter.format(5 * ONE_DAY)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_days", 5)));
+    assertThat(formatter.format(2 * ONE_HOUR)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_hours", 2)));
+    assertThat(formatter.format(ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_minutes", 1)));
+
+    assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR)).isEqualTo(newArrayList(
+      new WorkDurationFormatter.Result("work_duration.x_days", 5),
+      new WorkDurationFormatter.Result(" ", null),
+      new WorkDurationFormatter.Result("work_duration.x_hours", 2)
+    ));
+
+    assertThat(formatter.format(2 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(
+      new WorkDurationFormatter.Result("work_duration.x_hours", 2),
+      new WorkDurationFormatter.Result(" ", null),
+      new WorkDurationFormatter.Result("work_duration.x_minutes", 1)
+    ));
+
+    assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(
+      new WorkDurationFormatter.Result("work_duration.x_days", 5),
+      new WorkDurationFormatter.Result(" ", null),
+      new WorkDurationFormatter.Result("work_duration.x_hours", 2)
+    ));
+  }
+
+  @Test
+  public void not_display_following_element_when_bigger_than_ten() {
+    int hoursInDay = 15;
+    settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(hoursInDay));
+
+    assertThat(formatter.format(15 * hoursInDay * ONE_HOUR + 2 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_days", 15)));
+
+    assertThat(formatter.format(12 * ONE_HOUR + ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_hours", 12)));
+  }
+
+  @Test
+  public void display_zero_without_unit() {
+    assertThat(formatter.format(0)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("0", null)));
+  }
+
+  @Test
+  public void display_negative_duration() {
+    assertThat(formatter.format(-5 * ONE_DAY)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_days", -5)));
+    assertThat(formatter.format(-2 * ONE_HOUR)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_hours", -2)));
+    assertThat(formatter.format(-1 * ONE_MINUTE)).isEqualTo(newArrayList(new WorkDurationFormatter.Result("work_duration.x_minutes", -1)));
+  }
+
+}
index 4c8b337a136de6d9c1435f80553c7c97fc23ffdd..d185db89e91fa62eb263b9410bd8519ff48353a7 100644 (file)
@@ -4,3 +4,6 @@ with.parameters=First is {0} and second is {1}
 only.in.english=Missing in French bundle
 duration.seconds=less than a minute
 duration.day=a day
+work_duration.x_days={0}d
+work_duration.x_hours={0}h
+work_duration.x_minutes={0}min
index 7c7bdd3495f8563afde3d00fc1722be4e08e201d..c11356bd7c09d90d4462d9e777a33bae686c41c4 100644 (file)
@@ -102,4 +102,13 @@ public interface I18n extends ServerComponent, BatchComponent {
    */
   String formatDate(Locale locale, Date date);
 
+  /**
+   * Return the formatted work duration.
+   * <br>
+   * Example : format(Locale.ENGLISH, WorkDuration.create(10, 2, 0, 8)) -> 10d 2h
+   *
+   * @since 4.3
+   */
+  String formatWorkDuration(Locale locale, long duration);
+
 }
index 84e7d6bad20706d159b176265aaf08033f7f6076..fde05c3068fb0d9d6d2348b17037d5aca6305c4c 100644 (file)
 package org.sonar.server.issue;
 
 import org.sonar.api.ServerComponent;
+import org.sonar.api.i18n.I18n;
 import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.core.issue.IssueUpdater;
-import org.sonar.server.ui.WorkDurationFormatter;
+import org.sonar.server.user.UserSession;
 
 import java.io.Serializable;
 import java.util.List;
@@ -36,12 +36,10 @@ public class IssueChangelogFormatter implements ServerComponent {
 
   private static final String ISSUE_CHANGELOG_FIELD = "issue.changelog.field.";
 
-  private final DefaultI18n defaultI18n;
-  private final WorkDurationFormatter workDurationFormatter;
+  private final I18n i18n;
 
-  public IssueChangelogFormatter(DefaultI18n defaultI18n, WorkDurationFormatter workDurationFormatter) {
-    this.defaultI18n = defaultI18n;
-    this.workDurationFormatter = workDurationFormatter;
+  public IssueChangelogFormatter(I18n i18n) {
+    this.i18n = i18n;
   }
 
   public List<String> format(Locale locale, FieldDiffs diffs) {
@@ -51,13 +49,13 @@ public class IssueChangelogFormatter implements ServerComponent {
       String key = entry.getKey();
       IssueChangelogDiffFormat diffFormat = format(locale, key, entry.getValue());
       if (diffFormat.newValue() != null) {
-        message.append(defaultI18n.message(locale, "issue.changelog.changed_to", null, defaultI18n.message(locale, ISSUE_CHANGELOG_FIELD + key, null), diffFormat.newValue()));
+        message.append(i18n.message(locale, "issue.changelog.changed_to", null, i18n.message(locale, ISSUE_CHANGELOG_FIELD + key, null), diffFormat.newValue()));
       } else {
-        message.append(defaultI18n.message(locale, "issue.changelog.removed", null, defaultI18n.message(locale, ISSUE_CHANGELOG_FIELD + key, null)));
+        message.append(i18n.message(locale, "issue.changelog.removed", null, i18n.message(locale, ISSUE_CHANGELOG_FIELD + key, null)));
       }
       if (diffFormat.oldValue() != null) {
         message.append(" (");
-        message.append(defaultI18n.message(locale, "issue.changelog.was", null, diffFormat.oldValue()));
+        message.append(i18n.message(locale, "issue.changelog.was", null, diffFormat.oldValue()));
         message.append(")");
       }
       result.add(message.toString());
@@ -73,10 +71,11 @@ public class IssueChangelogFormatter implements ServerComponent {
     String oldValueString = oldValue != null && !"".equals(oldValue) ? oldValue.toString() : null;
     if (IssueUpdater.TECHNICAL_DEBT.equals(key)) {
       if (newValueString != null) {
-        newValueString = workDurationFormatter.format(Long.parseLong(newValueString), WorkDurationFormatter.Format.SHORT);
+        newValueString = i18n.formatWorkDuration(UserSession.get().locale(), Long.parseLong(newValueString));
       }
       if (oldValueString != null) {
-        oldValueString = workDurationFormatter.format(Long.parseLong(oldValueString), WorkDurationFormatter.Format.SHORT);
+        // TODO use i18n API
+        oldValueString = i18n.formatWorkDuration(UserSession.get().locale(), Long.parseLong(oldValueString));
       }
     }
     return new IssueChangelogDiffFormat(oldValueString, newValueString);
index f74135f2e720d96d18fdc61f8b1634d8ead90dc8..dcf82862e1d75a2df8631baee9acaa432fbbce4f 100644 (file)
@@ -44,7 +44,6 @@ import org.sonar.server.issue.ActionService;
 import org.sonar.server.issue.IssueChangelog;
 import org.sonar.server.issue.IssueChangelogService;
 import org.sonar.server.issue.IssueService;
-import org.sonar.server.ui.WorkDurationFormatter;
 import org.sonar.server.user.UserSession;
 
 import javax.annotation.CheckForNull;
@@ -62,17 +61,15 @@ public class IssueShowWsHandler implements RequestHandler {
   private final IssueService issueService;
   private final IssueChangelogService issueChangelogService;
   private final ActionService actionService;
-  private final WorkDurationFormatter workDurationFormatter;
   private final DefaultTechnicalDebtManager technicalDebtManager;
   private final I18n i18n;
 
   public IssueShowWsHandler(IssueFinder issueFinder, IssueService issueService, IssueChangelogService issueChangelogService, ActionService actionService,
-                            WorkDurationFormatter workDurationFormatter, DefaultTechnicalDebtManager technicalDebtManager, I18n i18n) {
+                            DefaultTechnicalDebtManager technicalDebtManager, I18n i18n) {
     this.issueFinder = issueFinder;
     this.issueService = issueService;
     this.issueChangelogService = issueChangelogService;
     this.actionService = actionService;
-    this.workDurationFormatter = workDurationFormatter;
     this.technicalDebtManager = technicalDebtManager;
     this.i18n = i18n;
   }
@@ -129,7 +126,7 @@ public class IssueShowWsHandler implements RequestHandler {
       .prop("author", issue.authorLogin())
       .prop("actionPlan", actionPlanKey)
       .prop("actionPlanName", actionPlan != null ? actionPlan.name() : null)
-      .prop("debt", technicalDebt != null ? workDurationFormatter.format(technicalDebt, WorkDurationFormatter.Format.SHORT) : null)
+      .prop("debt", technicalDebt != null ? i18n.formatWorkDuration(UserSession.get().locale(), technicalDebt) : null)
       .prop("creationDate", DateUtils.formatDateTime(issue.creationDate()))
       .prop("fCreationDate", formatDate(issue.creationDate()))
       .prop("updateDate", updateDate != null ? DateUtils.formatDateTime(updateDate) : null)
index 916e9f1f78464a8b096ac3965f19b7d195536a73..9417925697a0df844708e32a843c30f37bc77656 100644 (file)
@@ -43,6 +43,7 @@ import org.sonar.core.config.Logback;
 import org.sonar.core.i18n.DefaultI18n;
 import org.sonar.core.i18n.GwtI18n;
 import org.sonar.core.i18n.RuleI18nManager;
+import org.sonar.core.i18n.WorkDurationFormatter;
 import org.sonar.core.issue.IssueFilterSerializer;
 import org.sonar.core.issue.IssueNotifications;
 import org.sonar.core.issue.IssueUpdater;
@@ -115,7 +116,10 @@ import org.sonar.server.startup.*;
 import org.sonar.server.technicaldebt.DebtService;
 import org.sonar.server.text.MacroInterpreter;
 import org.sonar.server.text.RubyTextService;
-import org.sonar.server.ui.*;
+import org.sonar.server.ui.JRubyI18n;
+import org.sonar.server.ui.JRubyProfiling;
+import org.sonar.server.ui.PageDecorations;
+import org.sonar.server.ui.Views;
 import org.sonar.server.user.*;
 import org.sonar.server.util.*;
 import org.sonar.server.ws.ListingWs;
@@ -216,6 +220,9 @@ public final class Platform {
     rootContainer.addSingleton(DefaultI18n.class);
     rootContainer.addSingleton(RuleI18nManager.class);
     rootContainer.addSingleton(GwtI18n.class);
+    rootContainer.addSingleton(WorkDurationFormatter.class);
+    rootContainer.addSingleton(WorkDurationFactory.class);
+
     rootContainer.addSingleton(PreviewDatabaseFactory.class);
     rootContainer.addSingleton(SemaphoreUpdater.class);
     rootContainer.addSingleton(SemaphoresImpl.class);
@@ -381,9 +388,7 @@ public final class Platform {
     servicesContainer.addSingleton(TechnicalDebtModelSynchronizer.class);
     servicesContainer.addSingleton(TechnicalDebtModelRepository.class);
     servicesContainer.addSingleton(TechnicalDebtXMLImporter.class);
-    servicesContainer.addSingleton(WorkDurationFormatter.class);
     servicesContainer.addSingleton(DefaultTechnicalDebtManager.class);
-    servicesContainer.addSingleton(WorkDurationFactory.class);
 
     // source
     servicesContainer.addSingleton(HtmlSourceDecorator.class);
index f473dd0e7b0162ba31a2042da4cb7071888c30a0..540193cd19fecde9cb6d5fbc14f032b95bc73764 100644 (file)
@@ -25,27 +25,21 @@ import org.sonar.api.technicaldebt.server.Characteristic;
 import org.sonar.api.utils.WorkDuration;
 import org.sonar.api.utils.WorkDurationFactory;
 import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
-import org.sonar.server.ui.WorkDurationFormatter;
 
 import javax.annotation.CheckForNull;
+
 import java.util.List;
 
 public class DebtService implements ServerComponent {
 
-  private final WorkDurationFormatter workDurationFormatter;
   private final DefaultTechnicalDebtManager finder;
   private final WorkDurationFactory workDurationFactory;
 
-  public DebtService(WorkDurationFormatter workDurationFormatter, DefaultTechnicalDebtManager finder, WorkDurationFactory workDurationFactory) {
-    this.workDurationFormatter = workDurationFormatter;
+  public DebtService(DefaultTechnicalDebtManager finder, WorkDurationFactory workDurationFactory) {
     this.finder = finder;
     this.workDurationFactory = workDurationFactory;
   }
 
-  public String format(long debt) {
-    return workDurationFormatter.format(debt, WorkDurationFormatter.Format.SHORT);
-  }
-
   public WorkDuration toWorkDuration(long debt) {
     return workDurationFactory.createFromMinutes(debt);
   }
index fc5b66bcb42ad81378e3f455dacd46491f435aa5..3931567b6fb75a2ce15a968dfd1aed20bc68d408 100644 (file)
@@ -86,4 +86,8 @@ public class JRubyI18n implements ServerComponent {
     return i18n.ageFromNow(UserSession.get().locale(), date);
   }
 
+  public String formatWorkDuration(long duration) {
+    return i18n.formatWorkDuration(UserSession.get().locale(), duration);
+  }
+
 }
diff --git a/sonar-server/src/main/java/org/sonar/server/ui/WorkDurationFormatter.java b/sonar-server/src/main/java/org/sonar/server/ui/WorkDurationFormatter.java
deleted file mode 100644 (file)
index d070abd..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.ui;
-
-import org.sonar.api.ServerComponent;
-import org.sonar.api.utils.WorkDuration;
-import org.sonar.api.utils.WorkDurationFactory;
-import org.sonar.core.i18n.DefaultI18n;
-import org.sonar.server.user.UserSession;
-
-import java.util.Locale;
-
-public class WorkDurationFormatter implements ServerComponent {
-
-  public enum Format {
-    SHORT, LONG
-  }
-
-  private final DefaultI18n defaultI18n;
-  private final WorkDurationFactory workDurationFactory;
-
-  public WorkDurationFormatter(DefaultI18n defaultI18n, WorkDurationFactory workDurationFactory) {
-    this.defaultI18n = defaultI18n;
-    this.workDurationFactory = workDurationFactory;
-  }
-
-  /**
-   * Used by rails
-   */
-  public String format(long durationInSeconds, String stringFormat) {
-    return format(durationInSeconds, Format.valueOf(stringFormat));
-  }
-
-  public String format(long durationInSeconds, Format format) {
-    return formatWorkDuration(UserSession.get().locale(), durationInSeconds, format);
-  }
-
-  private String formatWorkDuration(Locale locale, long durationInSeconds, Format format) {
-    if (durationInSeconds == 0) {
-      return "0";
-    }
-    Long absDuration = Math.abs(durationInSeconds);
-    WorkDuration workDuration =  workDurationFactory.createFromMinutes(absDuration);
-    boolean shortLabel = Format.SHORT.equals(format);
-    StringBuilder message = new StringBuilder();
-    if (workDuration.days() > 0) {
-      message.append(message(locale, "work_duration.x_days", shortLabel, workDuration.days()));
-    }
-    if (workDuration.hours() > 0) {
-      if (message.length() > 0) {
-        message.append(" ");
-      }
-      message.append(message(locale, "work_duration.x_hours", shortLabel, workDuration.hours()));
-    }
-    if (workDuration.minutes() > 0) {
-      if (message.length() > 0) {
-        message.append(" ");
-      }
-      message.append(message(locale, "work_duration.x_minutes", shortLabel, workDuration.minutes()));
-    }
-    if (durationInSeconds < 0) {
-      message.insert(0, "-");
-    }
-    return message.toString();
-  }
-
-  private String message(Locale locale, String key, boolean shortLabel, Object... parameters) {
-    String msgKey = key;
-    if (shortLabel) {
-      msgKey += ".short";
-    }
-    return defaultI18n.message(locale, msgKey, null, parameters);
-  }
-}
index a8961cac95f472b0c45f48db73f0d1be4b3f1fd1..b169243bb19682c74968dab48dd6865f553c9735 100644 (file)
@@ -90,10 +90,6 @@ class Internal
     component(Java::OrgSonarServerUi::JRubyI18n.java_class)
   end
 
-  def self.work_duration_formatter
-    component(Java::OrgSonarServerUi::WorkDurationFormatter.java_class)
-  end
-
   private
 
   def self.component(component_java_class)
index 096cc08c20385da52deb873fd83f51969a50e4a8..f600e43430d087e60342573018731ee77c34587f 100644 (file)
@@ -164,7 +164,7 @@ class ProjectMeasure < ActiveRecord::Base
   end
 
   def work_duration_formatted_value(value)
-    Internal.work_duration_formatter.format(value.to_i, 'SHORT')
+    Internal.i18n.formatWorkDuration(value.to_i)
   end
 
   def color
index efd6b582ccc93c3825abb2f0f4cfa90e764b8e32..304bf918f5b63043a06241d0f164b8908bbbf7b3 100644 (file)
@@ -96,7 +96,7 @@
       <% end %>
     <% end %>
     <% if issue.debt %>
-      <li><%= message('issue.debt') -%>&nbsp;<%= Internal.technical_debt.format(issue.debt) -%></li>
+      <li><%= message('issue.debt') -%>&nbsp;<%= Internal.i18n.formatWorkDuration(issue.debt) -%></li>
     <% end %>
     <% if issue.authorLogin %>
       <li><%= message('issue.authorLogin') -%>&nbsp;<%= issue.authorLogin -%></li>
index 42ce6868862d87a7f43b8b14eee62ce1a900e44c..1dcb81c46c71fb945c86a7d0f245c25fe6b0f412 100644 (file)
@@ -24,14 +24,15 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.i18n.I18n;
 import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.core.i18n.DefaultI18n;
-import org.sonar.server.ui.WorkDurationFormatter;
 
 import java.util.List;
 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.when;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -39,19 +40,14 @@ public class IssueChangelogFormatterTest {
 
   private static final Locale DEFAULT_LOCALE = Locale.getDefault();
 
-  private static final int HOURS_IN_DAY = 8;
-
-  @Mock
-  private DefaultI18n i18n;
-
   @Mock
-  private WorkDurationFormatter workDurationFormatter;
+  private I18n i18n;
 
   private IssueChangelogFormatter formatter;
 
   @Before
   public void before() {
-    formatter = new IssueChangelogFormatter(i18n, workDurationFormatter);
+    formatter = new IssueChangelogFormatter(i18n);
   }
 
   @Test
@@ -131,8 +127,8 @@ public class IssueChangelogFormatterTest {
     FieldDiffs diffs = new FieldDiffs();
     diffs.setDiff("technicalDebt", "18000", "28800");
 
-    when(workDurationFormatter.format(18000, WorkDurationFormatter.Format.SHORT)).thenReturn("5 hours");
-    when(workDurationFormatter.format(28800, WorkDurationFormatter.Format.SHORT)).thenReturn("1 days");
+    when(i18n.formatWorkDuration(any(Locale.class), eq(18000L))).thenReturn("5 hours");
+    when(i18n.formatWorkDuration(any(Locale.class), eq(28800L))).thenReturn("1 days");
 
     when(i18n.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt");
     when(i18n.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days");
@@ -149,7 +145,7 @@ public class IssueChangelogFormatterTest {
     FieldDiffs diffs = new FieldDiffs();
     diffs.setDiff("technicalDebt", null, "28800");
 
-    when(workDurationFormatter.format(28800, WorkDurationFormatter.Format.SHORT)).thenReturn("1 days");
+    when(i18n.formatWorkDuration(any(Locale.class), eq(28800L))).thenReturn("1 days");
 
     when(i18n.message(DEFAULT_LOCALE, "issue.changelog.field.technicalDebt", null)).thenReturn("Technical Debt");
     when(i18n.message(DEFAULT_LOCALE, "issue.changelog.changed_to", null, "Technical Debt", "1 days")).thenReturn("Technical Debt changed to 1 days");
index 174e1e65ef0b33efa6e6f25813d82b2dc88e8fc3..a17a9069587d5ff1edabca59f646542425fd0ae4 100644 (file)
@@ -53,7 +53,6 @@ import org.sonar.server.issue.ActionService;
 import org.sonar.server.issue.IssueChangelog;
 import org.sonar.server.issue.IssueChangelogService;
 import org.sonar.server.issue.IssueService;
-import org.sonar.server.ui.WorkDurationFormatter;
 import org.sonar.server.user.MockUserSession;
 import org.sonar.server.user.UserSession;
 
@@ -83,9 +82,6 @@ public class IssueShowWsHandlerTest {
   @Mock
   ActionService actionService;
 
-  @Mock
-  WorkDurationFormatter workDurationFormatter;
-
   @Mock
   DefaultTechnicalDebtManager technicalDebtManager;
 
@@ -113,7 +109,7 @@ public class IssueShowWsHandlerTest {
 
     when(i18n.message(any(Locale.class), eq("created"), eq((String) null))).thenReturn("Created");
 
-    tester = new WsTester(new IssuesWs(new IssueShowWsHandler(issueFinder, issueService, issueChangelogService, actionService, workDurationFormatter, technicalDebtManager, i18n)));
+    tester = new WsTester(new IssuesWs(new IssueShowWsHandler(issueFinder, issueService, issueChangelogService, actionService, technicalDebtManager, i18n)));
   }
 
   @Test
@@ -264,7 +260,7 @@ public class IssueShowWsHandlerTest {
     Issue issue = createStandardIssue().setDebt(technicalDebt);
     issues.add(issue);
 
-    when(workDurationFormatter.format(eq(technicalDebt), any(WorkDurationFormatter.Format.class))).thenReturn("2 hours 1 minutes");
+    when(i18n.formatWorkDuration(any(Locale.class), eq(technicalDebt))).thenReturn("2 hours 1 minutes");
 
     MockUserSession.set();
     WsTester.TestRequest request = tester.newRequest("show").setParam("key", issue.key());
index 9e29013e887016ac349dc520545245ace7b9b4c0..d5292636a74fc680c15e6414c1cfd9786dca6624 100644 (file)
@@ -28,19 +28,16 @@ import org.sonar.api.technicaldebt.server.internal.DefaultCharacteristic;
 import org.sonar.api.utils.WorkDuration;
 import org.sonar.api.utils.WorkDurationFactory;
 import org.sonar.core.technicaldebt.DefaultTechnicalDebtManager;
-import org.sonar.server.ui.WorkDurationFormatter;
 
 import java.util.List;
 
 import static com.google.common.collect.Lists.newArrayList;
 import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.*;
 
 public class DebtServiceTest {
 
   private static final int HOURS_IN_DAY = 8;
-  WorkDurationFormatter workDurationFormatter = mock(WorkDurationFormatter.class);
   DefaultTechnicalDebtManager finder = mock(DefaultTechnicalDebtManager.class);
 
   DebtService service;
@@ -49,13 +46,7 @@ public class DebtServiceTest {
   public void setUp() throws Exception {
     Settings settings = new Settings();
     settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
-    service = new DebtService(workDurationFormatter, finder, new WorkDurationFactory(settings));
-  }
-
-  @Test
-  public void format() {
-    service.format(10L);
-    verify(workDurationFormatter).format(eq(10L), eq(WorkDurationFormatter.Format.SHORT));
+    service = new DebtService(finder, new WorkDurationFactory(settings));
   }
 
   @Test
index 031be00e869437cc6d159cf1900d12fee44a0988..1060704e1198e6f0c124536553b3ce9ab4db0720 100644 (file)
@@ -92,4 +92,10 @@ public class JRubyI18nTest {
     verify(i18n).ageFromNow(any(Locale.class), eq(date));
   }
 
+  @Test
+  public void format_work_duration() throws Exception {
+    jRubyI18n.formatWorkDuration(10L);
+    verify(i18n).formatWorkDuration(any(Locale.class), eq(10L));
+  }
+
 }
diff --git a/sonar-server/src/test/java/org/sonar/server/ui/WorkDurationFormatterTest.java b/sonar-server/src/test/java/org/sonar/server/ui/WorkDurationFormatterTest.java
deleted file mode 100644 (file)
index a973425..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.ui;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
-import org.sonar.api.utils.WorkDurationFactory;
-import org.sonar.core.i18n.DefaultI18n;
-
-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.mock;
-import static org.mockito.Mockito.when;
-
-public class WorkDurationFormatterTest {
-
-  static final int HOURS_IN_DAY = 8;
-
-  static final long ONE_MINUTE = 1L;
-  static final long ONE_HOUR = ONE_MINUTE * 60;
-  static final long ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
-
-  DefaultI18n i18n = mock(DefaultI18n.class);
-  WorkDurationFormatter formatter;
-
-  @Before
-  public void setUp() throws Exception {
-    Settings settings = new Settings();
-    settings.setProperty(CoreProperties.HOURS_IN_DAY, Integer.toString(HOURS_IN_DAY));
-    formatter = new WorkDurationFormatter(i18n, new WorkDurationFactory(settings));
-
-    when(i18n.message(any(Locale.class), eq("work_duration.x_days"), eq((String) null), eq(5))).thenReturn("5 days");
-    when(i18n.message(any(Locale.class), eq("work_duration.x_days.short"), eq((String) null), eq(5))).thenReturn("5d");
-    when(i18n.message(any(Locale.class), eq("work_duration.x_hours"), eq((String) null), eq(2))).thenReturn("2 hours");
-    when(i18n.message(any(Locale.class), eq("work_duration.x_hours.short"), eq((String) null), eq(2))).thenReturn("2h");
-    when(i18n.message(any(Locale.class), eq("work_duration.x_minutes"), eq((String) null), eq(1))).thenReturn("1 minutes");
-    when(i18n.message(any(Locale.class), eq("work_duration.x_minutes.short"), eq((String) null), eq(1))).thenReturn("1min");
-  }
-
-  @Test
-  public void long_format() {
-    assertThat(formatter.format(5 * ONE_DAY, WorkDurationFormatter.Format.LONG)).isEqualTo("5 days");
-    assertThat(formatter.format(2 * ONE_HOUR, WorkDurationFormatter.Format.LONG)).isEqualTo("2 hours");
-    assertThat(formatter.format(ONE_MINUTE, WorkDurationFormatter.Format.LONG)).isEqualTo("1 minutes");
-
-    assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR, WorkDurationFormatter.Format.LONG)).isEqualTo("5 days 2 hours");
-    assertThat(formatter.format(2 * ONE_HOUR + ONE_MINUTE, WorkDurationFormatter.Format.LONG)).isEqualTo("2 hours 1 minutes");
-
-    assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, WorkDurationFormatter.Format.LONG)).isEqualTo("5 days 2 hours 1 minutes");
-  }
-
-  @Test
-  public void short_format() {
-    assertThat(formatter.format(5 * ONE_DAY, WorkDurationFormatter.Format.SHORT)).isEqualTo("5d");
-    assertThat(formatter.format(2 * ONE_HOUR, WorkDurationFormatter.Format.SHORT)).isEqualTo("2h");
-    assertThat(formatter.format(ONE_MINUTE, WorkDurationFormatter.Format.SHORT)).isEqualTo("1min");
-
-    assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR, WorkDurationFormatter.Format.SHORT)).isEqualTo("5d 2h");
-    assertThat(formatter.format(2 * ONE_HOUR + ONE_MINUTE, WorkDurationFormatter.Format.SHORT)).isEqualTo("2h 1min");
-
-    assertThat(formatter.format(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, WorkDurationFormatter.Format.SHORT)).isEqualTo("5d 2h 1min");
-  }
-
-  @Test
-  public void format_with_string_parameter() {
-    assertThat(formatter.format(5 * ONE_DAY, "LONG")).isEqualTo("5 days");
-    assertThat(formatter.format(5 * ONE_DAY, "SHORT")).isEqualTo("5d");
-  }
-
-  @Test
-  public void display_zero_without_unit() {
-    assertThat(formatter.format(0, WorkDurationFormatter.Format.SHORT)).isEqualTo("0");
-  }
-
-  @Test
-  public void display_negative_duration() {
-    assertThat(formatter.format(-5 * ONE_DAY, WorkDurationFormatter.Format.SHORT)).isEqualTo("-5d");
-    assertThat(formatter.format(-2 * ONE_HOUR, WorkDurationFormatter.Format.SHORT)).isEqualTo("-2h");
-    assertThat(formatter.format(-1 * ONE_MINUTE, WorkDurationFormatter.Format.SHORT)).isEqualTo("-1min");
-  }
-
-}