diff --git a/base/src/main/kotlin/de/staropensource/engine/base/utility/DataSize.kt b/base/src/main/kotlin/de/staropensource/engine/base/utility/DataSize.kt new file mode 100644 index 0000000..78b9731 --- /dev/null +++ b/base/src/main/kotlin/de/staropensource/engine/base/utility/DataSize.kt @@ -0,0 +1,457 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Authors + * Licensed under the GNU General Public License v3. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.engine.base.utility + +import java.math.RoundingMode +import java.text.DecimalFormat +import java.text.DecimalFormatSymbols +import java.util.Locale + +/** + * Converts between various data size units. + * + * @param bytes bytes to convert + * @since v1-alpha10 + */ +data class DataSize( + val bytes: ULong +) { + /** + * Companion object of [DataSize]. + * + * @since v1-alpha10 + */ + companion object { + /** + * Converts the specified amount of + * kilobytes into a [DataSize] instance. + * + * @param kilobytes kilobytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofKilobytes(kilobytes: Double): DataSize = DataSize(formatOf(kilobytes.times(1000))) + + /** + * Converts the specified amount of + * kibibytes into a [DataSize] instance. + * + * @param kibibytes kibibytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofKibibytes(kibibytes: Double): DataSize = DataSize(formatOf(kibibytes.times(1024))) + + /** + * Converts the specified amount of + * megabytes into a [DataSize] instance. + * + * @param megabytes megabytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofMegabyte(megabytes: Double): DataSize = DataSize(formatOf(megabytes.times(1000000))) + + /** + * Converts the specified amount of + * mebibytes into a [DataSize] instance. + * + * @param mebibytes mebibytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofMebibyte(mebibytes: Double): DataSize = DataSize(formatOf(mebibytes.times(1048576))) + + /** + * Converts the specified amount of + * gigabytes into a [DataSize] instance. + * + * @param gigabytes gigabytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofGigabyte(gigabytes: Double): DataSize = DataSize(formatOf(gigabytes.times(1000000000))) + + /** + * Converts the specified amount of + * gebibytes into a [DataSize] instance. + * + * @param gebibytes gebibytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofGibibyte(gebibytes: Double): DataSize = DataSize(formatOf(gebibytes.times(1073741824))) + + /** + * Converts the specified amount of + * terabytes into a [DataSize] instance. + * + * @param terabytes terabytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofTerabyte(terabytes: Double): DataSize = DataSize(formatOf(terabytes.times(1000000000000))) + + /** + * Converts the specified amount of + * tebibytes into a [DataSize] instance. + * + * @param tebibytes tebibytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofTebibyte(tebibytes: Double): DataSize = DataSize(formatOf(tebibytes.times(1099511627776))) + + /** + * Converts the specified amount of + * petabytes into a [DataSize] instance. + * + * @param petabytes petabytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofPetabyte(petabytes: Double): DataSize = DataSize(formatOf(petabytes.times(1000000000000000))) + + /** + * Converts the specified amount of + * pebibytes into a [DataSize] instance. + * + * @param pebibytes pebibytes to convert + * @since v1-alpha10 + */ + @JvmStatic + fun ofPebibyte(pebibytes: Double): DataSize = DataSize(formatOf(pebibytes.times(1125899906842624))) + + + // -----> Internal + /** + * Performs miscellaneous operations + * on the specified [Double] to + * "optimize" it's value. + * + * @param number [Double] to operate on + * @return modified [Double] + * @since v1-alpha10 + */ + fun formatTo(number: Double): Double { + val decimalFormat = DecimalFormat("#.####", DecimalFormatSymbols(Locale.ROOT)) + decimalFormat.roundingMode = RoundingMode.HALF_UP + return decimalFormat.format(number).toDouble() + } + + /** + * Performs miscellaneous operations + * on the specified [Double] and + * converts it into a [ULong] + * + * @param number [Double] to operate on + * @return modified [Double] + * @since v1-alpha10 + */ + fun formatOf(number: Double): ULong { + val decimalFormat = DecimalFormat("#", DecimalFormatSymbols(Locale.ROOT)) + decimalFormat.roundingMode = RoundingMode.HALF_UP + return decimalFormat.format(number).toULong() + } + } + + + // -----> Conversion + /** + * Converts the specified amount + * of [bytes] into kilobytes. + * + * @return [bytes] in kilobytes + * @since v1-alpha10 + */ + fun toKilobyte(): Double = formatTo(bytes.toDouble().div(1000)) + + /** + * Converts the specified amount + * of [bytes] into kibibytes. + * + * @return [bytes] in kibibytes + * @since v1-alpha10 + */ + fun toKibibyte(): Double = formatTo(bytes.toDouble().div(1024)) + + /** + * Converts the specified amount + * of [bytes] into megabytes. + * + * @return [bytes] in megabytes + * @since v1-alpha10 + */ + fun toMegabyte(): Double = formatTo(bytes.toDouble().div(1000000)) + + /** + * Converts the specified amount + * of [bytes] into mebibytes. + * + * @return [bytes] in mebibytes + * @since v1-alpha10 + */ + fun toMebibyte(): Double = formatTo(bytes.toDouble().div(1048576)) + + /** + * Converts the specified amount + * of [bytes] into gigabytes. + * + * @return [bytes] in gigabytes + * @since v1-alpha10 + */ + fun toGigabyte(): Double = formatTo(bytes.toDouble().div(1000000000)) + + /** + * Converts the specified amount + * of [bytes] into gibibytes. + * + * @return [bytes] in gibibytes + * @since v1-alpha10 + */ + fun toGibibyte(): Double = formatTo(bytes.toDouble().div(1073741824)) + + /** + * Converts the specified amount + * of [bytes] into terabytes. + * + * @return [bytes] in terabytes + * @since v1-alpha10 + */ + fun toTerabyte(): Double = formatTo(bytes.toDouble().div(1000000000000)) + + /** + * Converts the specified amount + * of [bytes] into tebibytes. + * + * @return [bytes] in tebibytes + * @since v1-alpha10 + */ + fun toTebibyte(): Double = formatTo(bytes.toDouble().div(1099511627776)) + + /** + * Converts the specified amount + * of [bytes] into petabytes. + * + * @return [bytes] in petabytes + * @since v1-alpha10 + */ + fun toPetabyte(): Double = formatTo(bytes.toDouble().div(1000000000000000)) + + /** + * Converts the specified amount + * of [bytes] into pebibytes. + * + * @return [bytes] in pebibytes + * @since v1-alpha10 + */ + fun toPebibyte(): Double = formatTo(bytes.toDouble().div(1125899906842624)) + + + // -----> Miscellaneous + /** + * Returns the perfect representation + * of the specified data size. + * + * @return perfect representation + * @since v1-alpha10 + */ + override fun toString(): String = bytes.toString() + + /** + * Returns the perfect representation + * of the specified data size. + * + * @param binary uses a factor of `1024` (giving [Unit.MEBIBYTE], [Unit.GIBIBYTE], ...) instead of `1000` (giving [Unit.MEGABYTE], [Unit.GIGABYTE], ...) + * @return pair of the converted amount and the [Unit] + * @since v1-alpha10 + */ + fun toPerfectRepresentation(binary: Boolean = true): Pair { + if (binary) { + return if (toPebibyte() >= 1) + Pair(toPebibyte(), Unit.PEBIBYTE) + else if (toTebibyte() >= 1) + Pair(toTebibyte(), Unit.TEBIBYTE) + else if (toGibibyte() >= 1) + Pair(toGibibyte(), Unit.GIBIBYTE) + else if (toMebibyte() >= 1) + Pair(toMebibyte(), Unit.MEBIBYTE) + else + Pair(toKibibyte(), Unit.KIBIBYTE) + } else { + return if (toPetabyte() >= 1) + Pair(toPetabyte(), Unit.PETABYTE) + else if (toTerabyte() >= 1) + Pair(toTerabyte(), Unit.TERABYTE) + else if (toGigabyte() >= 1) + Pair(toGigabyte(), Unit.GIGABYTE) + else if (toMegabyte() >= 1) + Pair(toMegabyte(), Unit.MEGABYTE) + else + Pair(toKilobyte(), Unit.KILOBYTE) + } + } + + + // -----> Inner classes + /** + * Represents all unit types [DataSize] supports. + * + * @since v1-alpha10 + */ + enum class Unit { + // -----> by 1024 + /** + * The kibibyte (KiB) unit. + * + * Next unit after bytes. + * + * @since v1-alpha10 + */ + KIBIBYTE, + + /** + * The mibibyte (MiB) unit. + * + * Next unit after [KIBIBYTE]. + * + * @since v1-alpha10 + */ + MEBIBYTE, + + /** + * The gibibyte (GiB) unit. + * + * Next unit after [MEBIBYTE]. + * + * @since v1-alpha10 + */ + GIBIBYTE, + + /** + * The tebibyte (TiB) unit. + * + * Next unit after [GIBIBYTE]. + * + * @since v1-alpha10 + */ + TEBIBYTE, + + /** + * The pebibyte (PiB) unit. + * + * Next unit after [TEBIBYTE]. + * + * @since v1-alpha10 + */ + PEBIBYTE, + + // -----> by 1000 + /** + * The kilobyte (KB) unit. + * + * Next unit after bytes. + * + * @since v1-alpha10 + */ + KILOBYTE, + + /** + * The megabyte (MB) unit. + * + * Next unit after [KILOBYTE]. + * + * @since v1-alpha10 + */ + MEGABYTE, + + /** + * The gigabyte (GB) unit. + * + * Next unit after [MEGABYTE]. + * + * @since v1-alpha10 + */ + GIGABYTE, + + /** + * The terabyte (TB) unit. + * + * Next unit after [GIGABYTE]. + * + * @since v1-alpha10 + */ + TERABYTE, + + /** + * The petabytes (PB) unit. + * + * Next unit after [TERABYTE]. + * + * @since v1-alpha10 + */ + PETABYTE; + + + /** + * Returns the name of this unit. + * + * @return name + * @since v1-alpha10 + */ + fun getName(): String = when (this) { + // by 1024 + KIBIBYTE -> "Kibibyte" + MEBIBYTE -> "Mebibyte" + GIBIBYTE -> "Gibibyte" + TEBIBYTE -> "Tebibyte" + PEBIBYTE -> "Pebibyte" + + // by 1000 + KILOBYTE -> "Kilobyte" + MEGABYTE -> "Megabyte" + GIGABYTE -> "Gigabyte" + TERABYTE -> "Terabyte" + PETABYTE -> "Petabyte" + } + + /** + * Returns the short name of this unit. + * + * @return short name + * @since v1-alpha10 + */ + fun getShortName(): String = when (this) { + // by 1024 + KIBIBYTE -> "KiB" + MEBIBYTE -> "MiB" + GIBIBYTE -> "GiB" + TEBIBYTE -> "TiB" + PEBIBYTE -> "PiB" + + // by 1000 + KILOBYTE -> "KB" + MEGABYTE -> "MB" + GIGABYTE -> "GB" + TERABYTE -> "TB" + PETABYTE -> "PB" + } + } +} diff --git a/base/src/test/kotlin/de/staropensource/engine/base/utility/DataSizeTest.kt b/base/src/test/kotlin/de/staropensource/engine/base/utility/DataSizeTest.kt new file mode 100644 index 0000000..e3ededc --- /dev/null +++ b/base/src/test/kotlin/de/staropensource/engine/base/utility/DataSizeTest.kt @@ -0,0 +1,260 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Authors + * Licensed under the GNU General Public License v3. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.engine.base.utility + +import de.staropensource.engine.testing.TestBase +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.CsvSource + +class DataSizeTest : TestBase() { + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4400" + "6.1", "6100" + "0.9", "900" + "691.131", "691131"""" + ) + fun kilobytes(kilobytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofKilobytes(kilobytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toKilobyte() > kilobytes.minus(5), ".toKilobyte = ${dataSize.toKilobyte()} is smaller than range floor ${kilobytes.minus(5)}") + assertTrue(dataSize.toKilobyte() < kilobytes.plus(5), ".toKilobyte = ${dataSize.toKilobyte()} is bigger than range ceiling ${kilobytes.plus(5)}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4505" + "6.1", "6246" + "0.9", "921" + "691.131", "707718"""" + ) + fun kibibytes(kibibytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofKibibytes(kibibytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toKibibyte() > kibibytes.minus(5), ".toKibibyte = ${dataSize.toKibibyte()} is smaller than range floor ${kibibytes.minus(5)}") + assertTrue(dataSize.toKibibyte() < kibibytes.plus(5), ".toKibibyte = ${dataSize.toKibibyte()} is bigger than range ceiling ${kibibytes.plus(5)}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4400000" + "6.1", "6100000" + "0.9", "900000" + "691.131", "691131000"""" + ) + fun megabyte(megabytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofMegabyte(megabytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toMegabyte() > megabytes.minus(5), ".toMegabyte = ${dataSize.toMegabyte()} is smaller than range floor ${megabytes.minus(5)}") + assertTrue(dataSize.toMegabyte() < megabytes.plus(5), ".toMegabyte = ${dataSize.toMegabyte()} is bigger than range ceiling ${megabytes.plus(5)}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4613734" + "6.1", "6396313" + "0.9", "943718" + "691.131", "724703379"""" + ) + fun mebibyte(mebibytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofMebibyte(mebibytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toMebibyte() > mebibytes.minus(5), ".toMebibyte = ${dataSize.toMebibyte()} is smaller than range floor ${mebibytes.minus(5)}") + assertTrue(dataSize.toMebibyte() < mebibytes.plus(5), ".toMebibyte = ${dataSize.toMebibyte()} is bigger than range ceiling ${mebibytes.plus(5)}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4400000000" + "6.1", "6100000000" + "0.9", "900000000" + "691.131", "691131000000"""" + ) + fun gigabyte(gigabytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofGigabyte(gigabytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toGigabyte() > gigabytes.minus(5), ".toGigabyte = ${dataSize.toGigabyte()} is smaller than range floor ${gigabytes.minus(5)}") + assertTrue(dataSize.toGigabyte() < gigabytes.plus(5), ".toGigabyte = ${dataSize.toGigabyte()} is bigger than range ceiling ${gigabytes.plus(5)}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4724464025" + "6.1", "6549825126" + "0.9", "966367641" + "691.131", "742096260562"""" + ) + fun gibibyte(gibibytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofGibibyte(gibibytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toGibibyte() > gibibytes.minus(5), ".toGibibyte = ${dataSize.toGibibyte()} is smaller than range floor ${gibibytes.minus(5)}") + assertTrue(dataSize.toGibibyte() < gibibytes.plus(5), ".toGibibyte = ${dataSize.toGibibyte()} is bigger than range ceiling ${gibibytes.plus(5)}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4400000000000" + "6.1", "6100000000000" + "0.9", "900000000000" + "691.131", "691131000000000"""" + ) + fun terabyte(terabytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofTerabyte(terabytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toTerabyte() > terabytes.minus(5), ".toTerabyte = ${dataSize.toTerabyte()} is smaller than range floor ${terabytes.minus(5)}") + assertTrue(dataSize.toTerabyte() < terabytes.plus(5), ".toTerabyte = ${dataSize.toTerabyte()} is bigger than range ceiling ${terabytes.plus(5)}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4837851162214" + "6.1", "6707020929433" + "0.9", "989560464998" + "691.131", "759906570816454"""" + ) + fun tebibyte(tebibytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofTebibyte(tebibytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toTebibyte() > tebibytes.minus(5), ".toTebibyte = ${dataSize.toTebibyte()} is smaller than range floor ${tebibytes.minus(5)}") + assertTrue(dataSize.toTebibyte() < tebibytes.plus(5), ".toTebibyte = ${dataSize.toTebibyte()} is bigger than range ceiling ${tebibytes.plus(5)}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4400000000000000" + "6.1", "6100000000000000" + "0.9", "900000000000000"""" + ) + fun petabyte(petabytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofPetabyte(petabytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toPetabyte() > petabytes.minus(5), ".toPetabyte = ${dataSize.toPetabyte()} is smaller than range floor ${petabytes.minus(5)}") + assertTrue(dataSize.toPetabyte() < petabytes.plus(5), ".toPetabyte = ${dataSize.toPetabyte()} is bigger than range ceiling ${petabytes.plus(5)}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "4.4", "4953959590107546" + "6.1", "6867989431740006" + "0.9", "1013309916158361"""" + ) + fun pebibyte(pebibytes: Double, bytes: ULong) { + val dataSize: DataSize = DataSize.ofPebibyte(pebibytes) + assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}") + assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}") + assertTrue(dataSize.toPebibyte() > pebibytes.minus(5), ".toPebibyte = ${dataSize.toPebibyte()} is smaller than range floor ${pebibytes.minus(5)}") + assertTrue(dataSize.toPebibyte() < pebibytes.plus(5), ".toPebibyte = ${dataSize.toPebibyte()} is bigger than range ceiling ${pebibytes.plus(5)}") + } + + @Test + @DisplayName("toString") + fun toStringTest() { + val dataSize: DataSize = DataSize.ofMebibyte(410465.451) + assertEquals(dataSize.bytes, dataSize.toString().toULong()) + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "591", "KIBIBYTE" + "1023", "KIBIBYTE" + "1024", "KIBIBYTE" + "1047552", "KIBIBYTE" + "1048576", "MEBIBYTE" + "1072693248", "MEBIBYTE" + "1073741824", "GIBIBYTE" + "1098437885952", "GIBIBYTE" + "1099511627776", "TEBIBYTE" + "1124800395214848", "TEBIBYTE" + "1125899906842624", "PEBIBYTE" + "1151795604700004400", "PEBIBYTE"""" + ) + @DisplayName("toPerfectRepresentation (by 1024)") + fun toPerfectRepresentationBy1024(bytes: ULong, unit: DataSize.Unit) { + assertEquals(unit, DataSize(bytes).toPerfectRepresentation(binary = true).second, "Bytes ${bytes} did not match Unit.${unit.name}") + } + + @ParameterizedTest + @CsvSource( + delimiter = ',', + quoteCharacter = '"', + textBlock = """ + "591", "KILOBYTE" + "999", "KILOBYTE" + "1000", "KILOBYTE" + "999000", "KILOBYTE" + "1000000", "MEGABYTE" + "999000000", "MEGABYTE" + "1000000000", "GIGABYTE" + "999000000000", "GIGABYTE" + "1000000000000", "TERABYTE" + "999000000000000", "TERABYTE" + "1000000000000000", "PETABYTE" + "999000000000000000", "PETABYTE"""" + ) + @DisplayName("toPerfectRepresentation (by 1000)") + fun toPerfectRepresentationBy1000(bytes: ULong, unit: DataSize.Unit) { + assertEquals(unit, DataSize(bytes).toPerfectRepresentation(binary = false).second, "Bytes ${bytes} did not match Unit.${unit.name}") + } +}