MAILBOX-333 Solve unit formatting issues As referenced in https://issues.apache.org/jira/browse/IO-226, FileUtils does not round down correctly and loses precisions (1.99 MB will be Expressed as 1 MB)
Also, https://en.wikipedia.org/wiki/Byte shows the unit should be XiB and not XB. For these reasons, we implemented our own size formatting logic. Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/31ca2640 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/31ca2640 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/31ca2640 Branch: refs/heads/master Commit: 31ca264027d349a8d3c05c7db51ec28b348fbb0f Parents: 8982f4a Author: benwa <btell...@linagora.com> Authored: Mon May 14 10:47:56 2018 +0700 Committer: benwa <btell...@linagora.com> Committed: Tue May 15 16:19:34 2018 +0700 ---------------------------------------------------------------------- .../subscribers/QuotaThresholdNotice.java | 5 +- .../quota/mailing/subscribers/SizeFormat.java | 92 ++++++++++++++++++++ .../subscribers/QuotaThresholdNoticeTest.java | 28 ++++-- .../mailing/subscribers/SizeFormatTest.java | 89 +++++++++++++++++++ 4 files changed, 203 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/31ca2640/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNotice.java ---------------------------------------------------------------------- diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNotice.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNotice.java index bc007c5..52ab097 100644 --- a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNotice.java +++ b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNotice.java @@ -30,7 +30,6 @@ import java.util.HashMap; import java.util.Objects; import java.util.Optional; -import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.james.core.builder.MimeMessageBuilder; import org.apache.james.filesystem.api.FileSystem; @@ -177,10 +176,10 @@ public class QuotaThresholdNotice { sizeThreshold.ifPresent(value -> scopes.put("sizeThreshold", value.getQuotaOccupationRatioAsPercent())); countThreshold.ifPresent(value -> scopes.put("countThreshold", value.getQuotaOccupationRatioAsPercent())); - scopes.put("usedSize", FileUtils.byteCountToDisplaySize(sizeQuota.getUsed().asLong())); + scopes.put("usedSize", SizeFormat.format(sizeQuota.getUsed().asLong())); scopes.put("hasSizeLimit", sizeQuota.getLimit().isLimited()); if (sizeQuota.getLimit().isLimited()) { - scopes.put("limitSize", FileUtils.byteCountToDisplaySize(sizeQuota.getLimit().asLong())); + scopes.put("limitSize", SizeFormat.format(sizeQuota.getLimit().asLong())); } scopes.put("usedCount", countQuota.getUsed().asLong()); http://git-wip-us.apache.org/repos/asf/james-project/blob/31ca2640/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/subscribers/SizeFormat.java ---------------------------------------------------------------------- diff --git a/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/subscribers/SizeFormat.java b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/subscribers/SizeFormat.java new file mode 100644 index 0000000..9a9ebfb --- /dev/null +++ b/mailbox/plugin/quota-mailing/src/main/java/org/apache/james/mailbox/quota/mailing/subscribers/SizeFormat.java @@ -0,0 +1,92 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.mailbox.quota.mailing.subscribers; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +import org.apache.commons.io.FileUtils; + +import com.google.common.base.Preconditions; + +public class SizeFormat { + + public enum Unit { + TiB(FileUtils.ONE_TB_BI, "TiB"), + GiB(FileUtils.ONE_GB_BI, "GiB"), + MiB(FileUtils.ONE_MB_BI, "MiB"), + KiB(FileUtils.ONE_KB_BI, "KiB"), + Byte(BigInteger.valueOf(1), "bytes"); + + static Unit locateUnit(long bytesCount) { + if (bytesCount / FileUtils.ONE_TB > 0) { + return TiB; + } + if (bytesCount / FileUtils.ONE_GB > 0) { + return GiB; + } + if (bytesCount / FileUtils.ONE_MB > 0) { + return MiB; + } + if (bytesCount / FileUtils.ONE_KB > 0) { + return KiB; + } + return Byte; + } + + private static final int SCALE = 2; + private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = new DecimalFormatSymbols(Locale.US); + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.##", DECIMAL_FORMAT_SYMBOLS); + + private final BigInteger bytesCount; + private final String notation; + + Unit(BigInteger bytesCount, String notation) { + this.bytesCount = bytesCount; + this.notation = notation; + } + + public String format(long size) { + return format(new BigDecimal(size)); + } + + public String format(BigDecimal sizeAsDecimal) { + return asString(scaleToUnit(sizeAsDecimal)) + " " + notation; + } + + public BigDecimal scaleToUnit(BigDecimal sizeAsDecimal) { + return sizeAsDecimal.divide(new BigDecimal((bytesCount)), SCALE, BigDecimal.ROUND_FLOOR); + } + + private String asString(BigDecimal bigDecimal) { + return DECIMAL_FORMAT.format(bigDecimal.doubleValue()); + } + } + + public static String format(long bytesCount) { + Preconditions.checkArgument(bytesCount >= 0, "Formatting of a negative size is forbidden"); + + return Unit.locateUnit(bytesCount) + .format(bytesCount); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/31ca2640/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNoticeTest.java ---------------------------------------------------------------------- diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNoticeTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNoticeTest.java index 0898b18..3bdc35b 100644 --- a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNoticeTest.java +++ b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/QuotaThresholdNoticeTest.java @@ -209,20 +209,32 @@ class QuotaThresholdNoticeTest { .withConfiguration(DEFAULT_CONFIGURATION) .sizeQuota(Quota.<QuotaSize>builder() .used(QuotaSize.size(801 * 1024 * 1024)) - .computedLimit(QuotaSize.size(1024 * 1024 * 1024)) + .computedLimit(QuotaSize.size(1 * 1024 * 1024 * 1024)) .build()) .countQuota(Counts._72_PERCENT) .sizeThreshold(HistoryEvolution.higherThresholdReached(sizeThresholdChange, NotAlreadyReachedDuringGracePeriod)) .build() .get() .generateReport(fileSystem)) - .isEqualTo("You receive this email because you recently exceeded a threshold related to the quotas of your email account.\n" + - "\n" + - "You currently occupy more than 80 % of the total size allocated to you.\n" + - "You currently occupy 801 MB on a total of 1 GB allocated to you.\n" + - "\n" + - "You need to be aware that actions leading to exceeded quotas will be denied. This will result in a degraded service.\n" + - "To mitigate this issue you might reach your administrator in order to increase your configured quota. You might also delete some non important emails."); + .contains("You currently occupy 801 MiB on a total of 1 GiB allocated to you."); + } + + @Test + void generateReportShouldTruncateLowDigitsFormatSizeUnits() throws Exception { + QuotaThresholdChange sizeThresholdChange = new QuotaThresholdChange(_80, NOW); + + assertThat(QuotaThresholdNotice.builder() + .withConfiguration(DEFAULT_CONFIGURATION) + .sizeQuota(Quota.<QuotaSize>builder() + .used(QuotaSize.size(801 * 1024 * 1024)) + .computedLimit(QuotaSize.size((2 * 1024 * 1024 * 1024) - 1)) + .build()) + .countQuota(Counts._72_PERCENT) + .sizeThreshold(HistoryEvolution.higherThresholdReached(sizeThresholdChange, NotAlreadyReachedDuringGracePeriod)) + .build() + .get() + .generateReport(fileSystem)) + .contains("You currently occupy 801 MiB on a total of 1.99 GiB allocated to you."); } @Test http://git-wip-us.apache.org/repos/asf/james-project/blob/31ca2640/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/SizeFormatTest.java ---------------------------------------------------------------------- diff --git a/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/SizeFormatTest.java b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/SizeFormatTest.java new file mode 100644 index 0000000..fdcb144 --- /dev/null +++ b/mailbox/plugin/quota-mailing/src/test/java/org/apache/james/mailbox/quota/mailing/subscribers/SizeFormatTest.java @@ -0,0 +1,89 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.mailbox.quota.mailing.subscribers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.Test; + +public class SizeFormatTest { + + @Test + public void formatShouldThrowWhenNegative() { + assertThatThrownBy(() -> SizeFormat.format(-1)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void formatShouldAcceptZero() { + assertThat(SizeFormat.format(0)) + .isEqualTo("0 bytes"); + } + + @Test + public void formatShouldUseByteWhenAlmostKiB() { + assertThat(SizeFormat.format(1023)) + .isEqualTo("1023 bytes"); + } + + @Test + public void formatShouldUseKiBWhenExactlyOneKiB() { + assertThat(SizeFormat.format(1024)) + .isEqualTo("1 KiB"); + } + + @Test + public void formatShouldHaveTwoDigitPrecision() { + assertThat(SizeFormat.format(1024 + 100)) + .isEqualTo("1.09 KiB"); + } + + @Test + public void formatShouldBeExpressedInKiBWhenAlmostMiB() { + assertThat(SizeFormat.format(1024 * 1024 - 1)) + .isEqualTo("1023.99 KiB"); + } + + @Test + public void formatShouldKeepTwoDigitPrecisionWhenRoundingDown() { + assertThat(SizeFormat.format(2 * 1024 * 1024 - 1)) + .isEqualTo("1.99 MiB"); + } + + @Test + public void formatShouldUseMiBWhenExactlyOneMiB() { + assertThat(SizeFormat.format(1024 * 1024)) + .isEqualTo("1 MiB"); + } + + @Test + public void formatShouldUseGiBWhenExactlyOneGiB() { + assertThat(SizeFormat.format(1024 * 1024 * 1024)) + .isEqualTo("1 GiB"); + } + + @Test + public void formatShouldUseTiBWhenExactlyOneTiB() { + assertThat(SizeFormat.format(1024L * 1024L * 1024L * 1024L)) + .isEqualTo("1 TiB"); + } + +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org