From: Julien Lancelot Date: Thu, 23 Jan 2014 15:33:50 +0000 (+0100) Subject: Add a ago and instant method in i18n X-Git-Tag: 4.2~514 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=453366ae42491d3927249f44da40f6e947f30613;p=sonarqube.git Add a ago and instant method in i18n --- diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties index 0cf780a937b..a454337b21d 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties @@ -2518,3 +2518,31 @@ errors.type.notInOptions=Value '{0}' must be one of : {1}. # #------------------------------------------------------------------------------ markdown.helplink=Markdown Help + +#------------------------------------------------------------------------------ +# +# DURATION +# +#------------------------------------------------------------------------------ +duration.seconds=less than a minute +duration.seconds.ago=less than a minute ago +duration.minute=about a minute +duration.minute.ago=about a minute ago +duration.minutes={0} minutes +duration.minutes.ago={0} minutes ago +duration.hour=about an hour +duration.hour.ago=about an hour ago +duration.hours={0} hours +duration.hours.ago={0} hours ago +duration.day=a day +duration.day.ago=a day ago +duration.days={0} days +duration.days.ago={0} days ago +duration.month=about a month +duration.month.ago=about a month ago +duration.months={0} months +duration.months.ago={0} months ago +duration.year=about a year +duration.year.ago=about a year ago +duration.years={0} years +duration.years.ago={0} years ago diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/DurationLabel.java b/sonar-batch/src/main/java/org/sonar/batch/scan/DurationLabel.java deleted file mode 100644 index 06549d089e9..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/DurationLabel.java +++ /dev/null @@ -1,136 +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.batch.scan; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang.StringUtils; - -import java.text.MessageFormat; - -class DurationLabel { - - private String suffixAgo = "ago"; - private String seconds = "less than a minute"; - private String minute = "about a minute"; - private String minutes = "{0} minutes"; - private String hour = "about an hour"; - private String hours = "{0} hours"; - private String day = "a day"; - private String days = "{0} days"; - private String month = "about a month"; - private String months = "{0} months"; - private String year = "about a year"; - private String years = "{0} years"; - - String label(long durationInMillis) { - double nbSeconds = durationInMillis / 1000.0; - double nbMinutes = nbSeconds / 60; - double nbHours = nbMinutes / 60; - double nbDays = nbHours / 24; - double nbYears = nbDays / 365; - String message = getMessage(nbSeconds, nbMinutes, nbHours, nbDays, nbYears); - return join(message, suffixAgo); - } - - private String getMessage(double nbSeconds, double nbMinutes, double nbHours, double nbDays, double nbYears) { - String message = MessageFormat.format(this.years, Math.floor(nbYears)); - if (nbSeconds < 45) { - message = this.seconds; - } else if (nbSeconds < 90) { - message = this.minute; - } else if (nbMinutes < 45) { - message = MessageFormat.format(this.minutes, Math.round(nbMinutes)); - } else if (nbMinutes < 90) { - message = this.hour; - } else if (nbHours < 24) { - message = MessageFormat.format(this.hours, Math.round(nbHours)); - } else if (nbHours < 48) { - message = this.day; - } else if (nbDays < 30) { - message = MessageFormat.format(this.days, Math.floor(nbDays)); - } else if (nbDays < 60) { - message = this.month; - } else if (nbDays < 365) { - message = MessageFormat.format(this.months, Math.floor(nbDays / 30)); - } else if (nbYears < 2) { - message = this.year; - } - return message; - } - - @VisibleForTesting - String join(String time, String suffix) { - StringBuilder joined = new StringBuilder(); - joined.append(time); - if (StringUtils.isNotBlank(suffix)) { - joined.append(' ').append(suffix); - } - return joined.toString(); - } - - String getSuffixAgo() { - return suffixAgo; - } - - String getSeconds() { - return seconds; - } - - String getMinute() { - return minute; - } - - String getMinutes() { - return minutes; - } - - String getHour() { - return hour; - } - - String getHours() { - return hours; - } - - String getDay() { - return day; - } - - String getDays() { - return days; - } - - String getMonth() { - return month; - } - - String getMonths() { - return months; - } - - String getYear() { - return year; - } - - String getYears() { - return years; - } - -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java index a21b1bbd93b..6bef8ed3073 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java @@ -22,12 +22,15 @@ package org.sonar.batch.scan; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.i18n.I18n; import org.sonar.api.resources.Project; import org.sonar.api.utils.Semaphores; import org.sonar.api.utils.SonarException; import org.sonar.batch.ProjectTree; import org.sonar.batch.bootstrap.AnalysisMode; +import java.util.Locale; + public class ProjectLock { private static final Logger LOG = LoggerFactory.getLogger(ProjectLock.class); @@ -35,11 +38,13 @@ public class ProjectLock { private final Semaphores semaphores; private final ProjectTree projectTree; private final AnalysisMode analysisMode; + private final I18n i18n; - public ProjectLock(Semaphores semaphores, ProjectTree projectTree, AnalysisMode analysisMode) { + public ProjectLock(Semaphores semaphores, ProjectTree projectTree, AnalysisMode analysisMode, I18n i18n) { this.semaphores = semaphores; this.projectTree = projectTree; this.analysisMode = analysisMode; + this.i18n = i18n; } public void start() { @@ -54,8 +59,7 @@ public class ProjectLock { private String getErrorMessage(Semaphores.Semaphore semaphore) { long duration = semaphore.getDurationSinceLocked(); - DurationLabel durationLabel = new DurationLabel(); - String durationDisplay = durationLabel.label(duration); + String durationDisplay = i18n.ago(Locale.ENGLISH, duration); return "It looks like an analysis of '" + getProject().getName() + "' is already running (started " + durationDisplay + ")."; } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/DurationLabelTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/DurationLabelTest.java deleted file mode 100644 index 4cbf61959b3..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/DurationLabelTest.java +++ /dev/null @@ -1,152 +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.batch.scan; - -import org.junit.Test; - -import java.text.MessageFormat; - -import static org.fest.assertions.Assertions.assertThat; - -public class DurationLabelTest { - - // One second in milliseconds - private static final long SECOND = 1000; - - // One minute in milliseconds - private static final long MINUTE = 60 * SECOND; - - // One hour in milliseconds - private static final long HOUR = 60 * MINUTE; - - // One day in milliseconds - private static final long DAY = 24 * HOUR; - - // 30 days in milliseconds - private static final long MONTH = 30 * DAY; - - // 365 days in milliseconds - private static final long YEAR = 365 * DAY; - - @Test - public void testAgoSeconds() { - DurationLabel durationLabel = new DurationLabel(); - long now = System.currentTimeMillis(); - String label = durationLabel.label(now - System.currentTimeMillis()); - String expected = durationLabel.join(durationLabel.getSeconds(), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testAgoMinute() { - DurationLabel durationLabel = new DurationLabel(); - String label = durationLabel.label(now() - ago(MINUTE)); - String expected = durationLabel.join(durationLabel.getMinute(), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testAgoMinutes() { - DurationLabel durationlabel = new DurationLabel(); - int minutes = 2; - String label = durationlabel.label(now() - ago(minutes * MINUTE)); - String expected = durationlabel.join( - MessageFormat.format(durationlabel.getMinutes(), minutes), durationlabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testAgoHour() { - DurationLabel durationLabel = new DurationLabel(); - String label = durationLabel.label(now() - ago(HOUR)); - String expected = durationLabel.join(durationLabel.getHour(), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testAgoHours() { - DurationLabel durationLabel = new DurationLabel(); - long hours = 3; - String label = durationLabel.label(now() - ago(hours * HOUR)); - String expected = durationLabel.join(MessageFormat.format(durationLabel.getHours(), hours), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testAgoDay() { - DurationLabel durationLabel = new DurationLabel(); - String label = durationLabel.label(now() - ago(30 * HOUR)); - String expected = durationLabel.join(durationLabel.getDay(), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testAgoDays() { - DurationLabel durationLabel = new DurationLabel(); - long days = 4; - String label = durationLabel.label(now() - ago(days * DAY)); - String expected = durationLabel.join(MessageFormat.format(durationLabel.getDays(), days), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testAgoMonth() { - DurationLabel durationLabel = new DurationLabel(); - String label = durationLabel.label(now() - ago(35 * DAY)); - String expected = durationLabel.join(durationLabel.getMonth(), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testAgoMonths() { - DurationLabel durationLabel = new DurationLabel(); - long months = 2; - String label = durationLabel.label(now() - ago(months * MONTH)); - String expected = durationLabel.join(MessageFormat.format(durationLabel.getMonths(), months), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testYearAgo() { - DurationLabel durationLabel = new DurationLabel(); - String label = durationLabel.label(now() - ago(14 * MONTH)); - String expected = durationLabel.join(durationLabel.getYear(), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - @Test - public void testYearsAgo() { - DurationLabel durationLabel = new DurationLabel(); - long years = 7; - String label = durationLabel.label(now() - ago(years * YEAR)); - String expected = durationLabel.join(MessageFormat.format(durationLabel.getYears(), years), durationLabel.getSuffixAgo()); - assertThat(label).isEqualTo(expected); - } - - private long ago(long offset) { - return System.currentTimeMillis() - offset; - } - - private long now() { - // Add 5 seconds in order to have zero false positive - return System.currentTimeMillis() + 5000; - } - -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectLockTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectLockTest.java index 30a5c367ef8..9f14256e4e4 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectLockTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectLockTest.java @@ -21,26 +21,28 @@ package org.sonar.batch.scan; import org.junit.Before; import org.junit.Test; -import org.sonar.api.config.Settings; +import org.sonar.api.i18n.I18n; import org.sonar.api.resources.Project; import org.sonar.api.utils.Semaphores; import org.sonar.api.utils.SonarException; import org.sonar.batch.ProjectTree; import org.sonar.batch.bootstrap.AnalysisMode; +import java.util.Locale; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; public class ProjectLockTest { ProjectLock projectLock; Semaphores semaphores = mock(Semaphores.class); ProjectTree projectTree = mock(ProjectTree.class); - Settings settings; + I18n i18n = mock(I18n.class); Project project; private AnalysisMode mode; @@ -51,7 +53,7 @@ public class ProjectLockTest { project = new Project("my-project-key"); when(projectTree.getRootProject()).thenReturn(project); - projectLock = new ProjectLock(semaphores, projectTree, mode); + projectLock = new ProjectLock(semaphores, projectTree, mode, i18n); } @Test @@ -62,10 +64,16 @@ public class ProjectLockTest { verify(semaphores).acquire("batch-my-project-key", 15, 10); } - @Test(expected = SonarException.class) + @Test public void shouldNotAcquireSemaphoreIfTheProjectIsAlreadyBeenAnalysing() { when(semaphores.acquire(anyString(), anyInt(), anyInt())).thenReturn(new Semaphores.Semaphore().setLocked(false).setDurationSinceLocked(1234L)); + try { projectLock.start(); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(SonarException.class); + } + verify(i18n).ago(eq(Locale.ENGLISH), eq(1234L)); } @Test diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/DurationLabel.java b/sonar-core/src/main/java/org/sonar/core/i18n/DurationLabel.java new file mode 100644 index 00000000000..f560a26a54f --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/i18n/DurationLabel.java @@ -0,0 +1,123 @@ +/* + * 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 com.google.common.annotations.VisibleForTesting; +import org.apache.commons.lang.StringUtils; + +import javax.annotation.CheckForNull; + +class DurationLabel { + + private static String durationPreffix = "duration."; + private static String suffixAgo = ".ago"; + private static String seconds = "seconds"; + private static String minute = "minute"; + private static String minutes = "minutes"; + private static String hour = "hour"; + private static String hours = "hours"; + private static String day = "day"; + private static String days = "days"; + private static String month = "month"; + private static String months = "months"; + private static String year = "year"; + private static String years = "years"; + + public static Result instant(long durationInMillis) { + return label(durationInMillis, false); + } + + public static Result ago(long durationInMillis) { + return label(durationInMillis, true); + } + + public static Result label(long durationInMillis, boolean addAgoSuffix) { + double nbSeconds = durationInMillis / 1000.0; + double nbMinutes = nbSeconds / 60; + double nbHours = nbMinutes / 60; + double nbDays = nbHours / 24; + double nbYears = nbDays / 365; + return getMessage(addAgoSuffix, nbSeconds, nbMinutes, nbHours, nbDays, nbYears); + } + + private static Result message(boolean addAgoSuffix, String key) { + return message(addAgoSuffix, key, null); + } + + private static Result message(boolean addAgoSuffix, String key, Long value) { + return new Result(join(durationPreffix, key, addAgoSuffix ? suffixAgo : null), value); + } + + private static Result getMessage(boolean addAgoSuffix, double nbSeconds, double nbMinutes, double nbHours, double nbDays, double nbYears) { + if (nbSeconds < 45) { + return message(addAgoSuffix, DurationLabel.seconds); + } else if (nbSeconds < 90) { + return message(addAgoSuffix, DurationLabel.minute); + } else if (nbMinutes < 45) { + return message(addAgoSuffix, DurationLabel.minutes, Math.round(nbMinutes)); + } else if (nbMinutes < 90) { + return message(addAgoSuffix, DurationLabel.hour); + } else if (nbHours < 24) { + return message(addAgoSuffix, DurationLabel.hours, Math.round(nbHours)); + } else if (nbHours < 48) { + return message(addAgoSuffix, DurationLabel.day); + } else if (nbDays < 30) { + return message(addAgoSuffix, DurationLabel.days, Double.valueOf(Math.floor(nbDays)).longValue()); + } else if (nbDays < 60) { + return message(addAgoSuffix, DurationLabel.month); + } else if (nbDays < 365) { + return message(addAgoSuffix, DurationLabel.months, Double.valueOf(Math.floor(nbDays / 30)).longValue()); + } else if (nbYears < 2) { + return message(addAgoSuffix, DurationLabel.year); + } + return message(addAgoSuffix, DurationLabel.years, Double.valueOf(Math.floor(nbYears)).longValue()); + } + + @VisibleForTesting + static String join(String first, String second, String last) { + StringBuilder joined = new StringBuilder(); + joined.append(first); + joined.append(second); + if (StringUtils.isNotBlank(last)) { + joined.append(last); + } + return joined.toString(); + } + + static class Result { + private String key; + private Long value; + + public Result(String key, Long value) { + this.key = key; + this.value = value; + } + + public String key() { + return key; + } + + @CheckForNull + public Long value() { + return value; + } + } + +} diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/I18nManager.java b/sonar-core/src/main/java/org/sonar/core/i18n/I18nManager.java index 8ae6feeea41..720fbd40cc8 100644 --- a/sonar-core/src/main/java/org/sonar/core/i18n/I18nManager.java +++ b/sonar-core/src/main/java/org/sonar/core/i18n/I18nManager.java @@ -38,12 +38,7 @@ import javax.annotation.Nullable; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; -import java.util.Enumeration; -import java.util.Locale; -import java.util.Map; -import java.util.MissingResourceException; -import java.util.ResourceBundle; -import java.util.Set; +import java.util.*; public class I18nManager implements I18n, ServerExtension, BatchExtension, Startable { private static final Logger LOG = LoggerFactory.getLogger(I18nManager.class); @@ -119,6 +114,16 @@ public class I18nManager implements I18n, ServerExtension, BatchExtension, Start return formatMessage(value, parameters); } + public String instant(Locale locale, long durationInMillis) { + DurationLabel.Result duration = DurationLabel.instant(durationInMillis); + return message(locale, duration.key(), null, duration.value()); + } + + public String ago(Locale locale, long durationInMillis) { + DurationLabel.Result duration = DurationLabel.ago(durationInMillis); + return message(locale, duration.key(), null, duration.value()); + } + /** * 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/test/java/org/sonar/core/i18n/DurationLabelTest.java b/sonar-core/src/test/java/org/sonar/core/i18n/DurationLabelTest.java new file mode 100644 index 00000000000..583fb7fb200 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/i18n/DurationLabelTest.java @@ -0,0 +1,150 @@ +/* + * 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.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.fest.assertions.Assertions.assertThat; + +@RunWith(MockitoJUnitRunner.class) +public class DurationLabelTest { + + // One second in milliseconds + private static final long SECOND = 1000; + + // One minute in milliseconds + private static final long MINUTE = 60 * SECOND; + + // One hour in milliseconds + private static final long HOUR = 60 * MINUTE; + + // One day in milliseconds + private static final long DAY = 24 * HOUR; + + // 30 days in milliseconds + private static final long MONTH = 30 * DAY; + + // 365 days in milliseconds + private static final long YEAR = 365 * DAY; + + @Test + public void instant_seconds() { + long now = System.currentTimeMillis(); + DurationLabel.Result result = DurationLabel.instant(now - System.currentTimeMillis()); + assertThat(result.key()).isEqualTo("duration.seconds"); + assertThat(result.value()).isNull(); + } + + @Test + public void ago_seconds() { + long now = System.currentTimeMillis(); + DurationLabel.Result result = DurationLabel.ago(now - System.currentTimeMillis()); + assertThat(result.key()).isEqualTo("duration.seconds.ago"); + assertThat(result.value()).isNull(); + } + + @Test + public void ago_minute() { + DurationLabel.Result result = DurationLabel.label(now() - ago(MINUTE), true); + assertThat(result.key()).isEqualTo("duration.minute.ago"); + assertThat(result.value()).isNull(); + } + + @Test + public void ago_minutes() { + long minutes = 2; + DurationLabel.Result result = DurationLabel.label(now() - ago(minutes * MINUTE), true); + assertThat(result.key()).isEqualTo("duration.minutes.ago"); + assertThat(result.value()).isEqualTo(minutes); + + } + + @Test + public void ago_hour() { + DurationLabel.Result result = DurationLabel.label(now() - ago(HOUR), true); + assertThat(result.key()).isEqualTo("duration.hour.ago"); + assertThat(result.value()).isNull(); + } + + @Test + public void ago_hours() { + long hours = 3; + DurationLabel.Result result = DurationLabel.label(now() - ago(hours * HOUR), true); + assertThat(result.key()).isEqualTo("duration.hours.ago"); + assertThat(result.value()).isEqualTo(hours); + } + + @Test + public void ago_day() { + DurationLabel.Result result = DurationLabel.label(now() - ago(30 * HOUR), true); + assertThat(result.key()).isEqualTo("duration.day.ago"); + assertThat(result.value()).isNull(); + } + + @Test + public void ago_days() { + long days = 4; + DurationLabel.Result result = DurationLabel.label(now() - ago(days * DAY), true); + assertThat(result.key()).isEqualTo("duration.days.ago"); + assertThat(result.value()).isEqualTo(days); + } + + @Test + public void ago_month() { + DurationLabel.Result result = DurationLabel.label(now() - ago(35 * DAY), true); + assertThat(result.key()).isEqualTo("duration.month.ago"); + assertThat(result.value()).isNull(); + } + + @Test + public void ago_months() { + long months = 2; + DurationLabel.Result result = DurationLabel.label(now() - ago(months * MONTH), true); + assertThat(result.key()).isEqualTo("duration.months.ago"); + assertThat(result.value()).isEqualTo(months); + } + + @Test + public void year_ago() { + DurationLabel.Result result = DurationLabel.label(now() - ago(14 * MONTH), true); + assertThat(result.key()).isEqualTo("duration.year.ago"); + assertThat(result.value()).isNull(); + } + + @Test + public void years_ago() { + long years = 7; + DurationLabel.Result result = DurationLabel.label(now() - ago(years * YEAR), true); + assertThat(result.key()).isEqualTo("duration.years.ago"); + assertThat(result.value()).isEqualTo(years); + } + + private long ago(long offset) { + return System.currentTimeMillis() - offset; + } + + private long now() { + // Add 5 seconds in order to have zero false positive + return System.currentTimeMillis() + 5000; + } + +} diff --git a/sonar-core/src/test/java/org/sonar/core/i18n/I18nManagerTest.java b/sonar-core/src/test/java/org/sonar/core/i18n/I18nManagerTest.java index 49558738dff..6445fc256aa 100644 --- a/sonar-core/src/test/java/org/sonar/core/i18n/I18nManagerTest.java +++ b/sonar-core/src/test/java/org/sonar/core/i18n/I18nManagerTest.java @@ -159,6 +159,16 @@ public class I18nManagerTest { assertThat(html).isNull(); } + @Test + public void get_time_ago() { + assertThat(manager.ago(Locale.ENGLISH, 10)).isEqualTo("less than a minute ago"); + } + + @Test + public void get_time_instant() { + assertThat(manager.instant(Locale.ENGLISH, 10)).isEqualTo("less than a minute"); + } + static URLClassLoader newCoreClassloader() { return newClassLoader("/org/sonar/core/i18n/corePlugin/"); } diff --git a/sonar-core/src/test/resources/org/sonar/core/i18n/corePlugin/org/sonar/l10n/core.properties b/sonar-core/src/test/resources/org/sonar/core/i18n/corePlugin/org/sonar/l10n/core.properties index de205d651cb..f3e62c46cf7 100644 --- a/sonar-core/src/test/resources/org/sonar/core/i18n/corePlugin/org/sonar/l10n/core.properties +++ b/sonar-core/src/test/resources/org/sonar/core/i18n/corePlugin/org/sonar/l10n/core.properties @@ -1,4 +1,6 @@ by=By empty= with.parameters=First is {0} and second is {1} -only.in.english=Missing in French bundle \ No newline at end of file +only.in.english=Missing in French bundle +duration.seconds=less than a minute +duration.seconds.ago=less than a minute ago diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java b/sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java index 0a8b8543805..6c0b1c891ee 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/i18n/I18n.java @@ -49,4 +49,14 @@ public interface I18n extends ServerComponent, BatchComponent { */ String message(final Locale locale, final String key, @Nullable final String defaultValue, final Object... parameters); + /** + * @since 4.2 + */ + String instant(Locale locale, long durationInMillis); + + /** + * @since 4.2 + */ + String ago(Locale locale, long durationInMillis); + }