From 1e857dc9988d4cc298b489b7accaf5efe3a462fe Mon Sep 17 00:00:00 2001 From: Francisco Carriedo Date: Tue, 22 Jul 2014 00:38:47 -0700 Subject: [PATCH 01/10] pkg/units: Using 'case' instead of trickled ifs Seems the perfect case for 'case' ;). Docker-DCO-1.1-Signed-off-by: Francisco Carriedo (github: fcarriedo) Upstream-commit: 03697f0a93b04122a5039db915afddf60ccec448 Component: engine --- components/engine/pkg/units/size.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/components/engine/pkg/units/size.go b/components/engine/pkg/units/size.go index e6e9f21b47..d8410b4233 100644 --- a/components/engine/pkg/units/size.go +++ b/components/engine/pkg/units/size.go @@ -42,15 +42,16 @@ func FromHumanSize(size string) (int64, error) { unit := strings.ToLower(matches[2]) - if unit == "k" { + switch unit { + case "k": theSize *= 1000 - } else if unit == "m" { + case "m": theSize *= 1000 * 1000 - } else if unit == "g" { + case "g": theSize *= 1000 * 1000 * 1000 - } else if unit == "t" { + case "t": theSize *= 1000 * 1000 * 1000 * 1000 - } else if unit == "p" { + case "p": theSize *= 1000 * 1000 * 1000 * 1000 * 1000 } @@ -80,13 +81,14 @@ func RAMInBytes(size string) (int64, error) { unit := strings.ToLower(matches[2]) - if unit == "k" { + switch unit { + case "k": memLimit *= 1024 - } else if unit == "m" { + case "m": memLimit *= 1024 * 1024 - } else if unit == "g" { + case "g": memLimit *= 1024 * 1024 * 1024 - } else if unit == "t" { + case "t": memLimit *= 1024 * 1024 * 1024 * 1024 } From fc192c453a7b3c3ae5e186ef30ee1d0de9912193 Mon Sep 17 00:00:00 2001 From: Francisco Carriedo Date: Mon, 21 Jul 2014 23:49:52 -0700 Subject: [PATCH 02/10] pkg/units: Standardized supported sizes May make sense that both `FromHumanSize()` and `RAMInBytes()` support the same units. Added 'PB' to the RAMInBytes regex. Also updated tests. Note: int64 is overflowed on quantities >= EB Docker-DCO-1.1-Signed-off-by: Francisco Carriedo (github: fcarriedo) Upstream-commit: 0fd37bd7ac0f92eda0a55cf2bedf77f1996b8472 Component: engine --- components/engine/pkg/units/size.go | 4 +++- components/engine/pkg/units/size_test.go | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/components/engine/pkg/units/size.go b/components/engine/pkg/units/size.go index d8410b4233..4a953fbba5 100644 --- a/components/engine/pkg/units/size.go +++ b/components/engine/pkg/units/size.go @@ -63,7 +63,7 @@ func FromHumanSize(size string) (int64, error) { // returns the number of bytes, or -1 if the string is unparseable. // Units are case-insensitive, and the 'b' suffix is optional. func RAMInBytes(size string) (int64, error) { - re, err := regexp.Compile("^(\\d+)([kKmMgGtT])?[bB]?$") + re, err := regexp.Compile("^(\\d+)([kKmMgGtTpP])?[bB]?$") if err != nil { return -1, err } @@ -90,6 +90,8 @@ func RAMInBytes(size string) (int64, error) { memLimit *= 1024 * 1024 * 1024 case "t": memLimit *= 1024 * 1024 * 1024 * 1024 + case "p": + memLimit *= 1024 * 1024 * 1024 * 1024 * 1024 } return memLimit, nil diff --git a/components/engine/pkg/units/size_test.go b/components/engine/pkg/units/size_test.go index de8b44f8ce..a2dfedceb4 100644 --- a/components/engine/pkg/units/size_test.go +++ b/components/engine/pkg/units/size_test.go @@ -64,7 +64,11 @@ func TestRAMInBytes(t *testing.T) { assertRAMInBytes(t, "32MB", false, 32*1024*1024) assertRAMInBytes(t, "32Gb", false, 32*1024*1024*1024) assertRAMInBytes(t, "32G", false, 32*1024*1024*1024) + assertRAMInBytes(t, "32GB", false, 32*1024*1024*1024) assertRAMInBytes(t, "32Tb", false, 32*1024*1024*1024*1024) + assertRAMInBytes(t, "8Pb", false, 8*1024*1024*1024*1024*1024) + assertRAMInBytes(t, "8PB", false, 8*1024*1024*1024*1024*1024) + assertRAMInBytes(t, "8P", false, 8*1024*1024*1024*1024*1024) assertRAMInBytes(t, "", true, -1) assertRAMInBytes(t, "hello", true, -1) From 9cee4d41c4ab7034ea45184a233af9df8e39414b Mon Sep 17 00:00:00 2001 From: Francisco Carriedo Date: Mon, 21 Jul 2014 23:50:27 -0700 Subject: [PATCH 03/10] pkg/units: Refactor regexp.Compile to init() No need to recompile a fixed regular expression each time the function executes. Abstracting it to the `init()` method. Docker-DCO-1.1-Signed-off-by: Francisco Carriedo (github: fcarriedo) Upstream-commit: a4d57d8a851bf288804215b39c9d56a51550a228 Component: engine --- components/engine/pkg/units/size.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/components/engine/pkg/units/size.go b/components/engine/pkg/units/size.go index 4a953fbba5..91b617634c 100644 --- a/components/engine/pkg/units/size.go +++ b/components/engine/pkg/units/size.go @@ -7,6 +7,15 @@ import ( "strings" ) +var sizeRegex *regexp.Regexp + +func init() { + var err error + if sizeRegex, err = regexp.Compile("^(\\d+)([kKmMgGtTpP])?[bB]?$"); err != nil { + panic("Failed to compile the 'size' regular expression") + } +} + // HumanSize returns a human-readable approximation of a size // using SI standard (eg. "44kB", "17MB") func HumanSize(size int64) string { @@ -24,12 +33,7 @@ func HumanSize(size int64) string { // FromHumanSize returns an integer from a human-readable specification of a size // using SI standard (eg. "44kB", "17MB") func FromHumanSize(size string) (int64, error) { - re, err := regexp.Compile("^(\\d+)([kKmMgGtTpP])?[bB]?$") - if err != nil { - return -1, fmt.Errorf("%s does not specify not a size", size) - } - - matches := re.FindStringSubmatch(size) + matches := sizeRegex.FindStringSubmatch(size) if len(matches) != 3 { return -1, fmt.Errorf("Invalid size: '%s'", size) @@ -63,12 +67,7 @@ func FromHumanSize(size string) (int64, error) { // returns the number of bytes, or -1 if the string is unparseable. // Units are case-insensitive, and the 'b' suffix is optional. func RAMInBytes(size string) (int64, error) { - re, err := regexp.Compile("^(\\d+)([kKmMgGtTpP])?[bB]?$") - if err != nil { - return -1, err - } - - matches := re.FindStringSubmatch(size) + matches := sizeRegex.FindStringSubmatch(size) if len(matches) != 3 { return -1, fmt.Errorf("Invalid size: '%s'", size) From 23fcdf4f5e2d5f21a3451d62a1ecfa7664fb8172 Mon Sep 17 00:00:00 2001 From: Francisco Carriedo Date: Wed, 23 Jul 2014 23:53:16 -0700 Subject: [PATCH 04/10] pkg/units: Compacted var declaration and initialization No need to have two lines. The type is even explicit when type casting to `float64(size)` Docker-DCO-1.1-Signed-off-by: Francisco Carriedo (github: fcarriedo) Upstream-commit: d512294382c9013dee2820745db02ab63dc2ecdd Component: engine --- components/engine/pkg/units/size.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/engine/pkg/units/size.go b/components/engine/pkg/units/size.go index 91b617634c..6535540ce2 100644 --- a/components/engine/pkg/units/size.go +++ b/components/engine/pkg/units/size.go @@ -19,10 +19,9 @@ func init() { // HumanSize returns a human-readable approximation of a size // using SI standard (eg. "44kB", "17MB") func HumanSize(size int64) string { - i := 0 - var sizef float64 - sizef = float64(size) units := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + i := 0 + sizef := float64(size) for sizef >= 1000.0 { sizef = sizef / 1000.0 i++ From 088f734e5e0daabe5c48bea648a5605c1c2c1898 Mon Sep 17 00:00:00 2001 From: Francisco Carriedo Date: Wed, 23 Jul 2014 23:55:48 -0700 Subject: [PATCH 05/10] pkg/units: Moved 'units' out of function No need to initialize every time the function executes since it works as a catalog. Docker-DCO-1.1-Signed-off-by: Francisco Carriedo (github: fcarriedo) Upstream-commit: e4ab996b9d0d97089b076cfef32405f5f3fedc7c Component: engine --- components/engine/pkg/units/size.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/engine/pkg/units/size.go b/components/engine/pkg/units/size.go index 6535540ce2..027886e5f3 100644 --- a/components/engine/pkg/units/size.go +++ b/components/engine/pkg/units/size.go @@ -16,17 +16,18 @@ func init() { } } +var bytePrefixes = [...]string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + // HumanSize returns a human-readable approximation of a size // using SI standard (eg. "44kB", "17MB") func HumanSize(size int64) string { - units := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} i := 0 sizef := float64(size) for sizef >= 1000.0 { sizef = sizef / 1000.0 i++ } - return fmt.Sprintf("%.4g %s", sizef, units[i]) + return fmt.Sprintf("%.4g %s", sizef, bytePrefixes[i]) } // FromHumanSize returns an integer from a human-readable specification of a size From 22e53a9bae78a529ae866f75785b4e9513d77a70 Mon Sep 17 00:00:00 2001 From: Francisco Carriedo Date: Tue, 22 Jul 2014 14:23:52 -0700 Subject: [PATCH 06/10] pkg/units: Refactored common code to single func Both functions perform the same logic and they just vary on the base multiplication units. We can refactor the common code into a single place. Docker-DCO-1.1-Signed-off-by: Francisco Carriedo (github: fcarriedo) Upstream-commit: 386d300a6e07c99380a0814497cf0bc672df1e63 Component: engine --- components/engine/pkg/units/size.go | 71 ++++++++++++----------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/components/engine/pkg/units/size.go b/components/engine/pkg/units/size.go index 027886e5f3..50e8ec6c81 100644 --- a/components/engine/pkg/units/size.go +++ b/components/engine/pkg/units/size.go @@ -7,6 +7,11 @@ import ( "strings" ) +const ( + decimalKUnit = 1000 + binaryKUnit = 1024 +) + var sizeRegex *regexp.Regexp func init() { @@ -30,9 +35,23 @@ func HumanSize(size int64) string { return fmt.Sprintf("%.4g %s", sizef, bytePrefixes[i]) } -// FromHumanSize returns an integer from a human-readable specification of a size -// using SI standard (eg. "44kB", "17MB") +// FromHumanSize returns an integer from a human-readable specification of a +// size using SI standard (eg. "44kB", "17MB") func FromHumanSize(size string) (int64, error) { + return parseSize(size, decimalKUnit) +} + +// Parses a human-readable string representing an amount of RAM +// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and +// returns the number of bytes, or -1 if the string is unparseable. +// Units are case-insensitive, and the 'b' suffix is optional. +func RAMInBytes(size string) (int64, error) { + return parseSize(size, binaryKUnit) +} + +// Parses the human-readable size string into the amount it represents given +// the desired kilo unit [decimalKiloUnit=1000|binaryKiloUnit=1024] +func parseSize(size string, kUnit int64) (int64, error) { matches := sizeRegex.FindStringSubmatch(size) if len(matches) != 3 { @@ -44,54 +63,20 @@ func FromHumanSize(size string) (int64, error) { return -1, err } - unit := strings.ToLower(matches[2]) + unitPrefix := strings.ToLower(matches[2]) - switch unit { + switch unitPrefix { case "k": - theSize *= 1000 + theSize *= kUnit case "m": - theSize *= 1000 * 1000 + theSize *= kUnit * kUnit case "g": - theSize *= 1000 * 1000 * 1000 + theSize *= kUnit * kUnit * kUnit case "t": - theSize *= 1000 * 1000 * 1000 * 1000 + theSize *= kUnit * kUnit * kUnit * kUnit case "p": - theSize *= 1000 * 1000 * 1000 * 1000 * 1000 + theSize *= kUnit * kUnit * kUnit * kUnit * kUnit } return theSize, nil } - -// Parses a human-readable string representing an amount of RAM -// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and -// returns the number of bytes, or -1 if the string is unparseable. -// Units are case-insensitive, and the 'b' suffix is optional. -func RAMInBytes(size string) (int64, error) { - matches := sizeRegex.FindStringSubmatch(size) - - if len(matches) != 3 { - return -1, fmt.Errorf("Invalid size: '%s'", size) - } - - memLimit, err := strconv.ParseInt(matches[1], 10, 0) - if err != nil { - return -1, err - } - - unit := strings.ToLower(matches[2]) - - switch unit { - case "k": - memLimit *= 1024 - case "m": - memLimit *= 1024 * 1024 - case "g": - memLimit *= 1024 * 1024 * 1024 - case "t": - memLimit *= 1024 * 1024 * 1024 * 1024 - case "p": - memLimit *= 1024 * 1024 * 1024 * 1024 * 1024 - } - - return memLimit, nil -} From f664c760577182f7e2468a7aff333e1ff7d9d867 Mon Sep 17 00:00:00 2001 From: fcarriedo Date: Thu, 24 Jul 2014 12:34:11 -0700 Subject: [PATCH 07/10] pkg/units: Updated `Compile()` to `MustCompile()` We were doing exactly what `regexp.MustCompile()` already does. Docker-DCO-1.1-Signed-off-by: fcarriedo (github: fcarriedo) Upstream-commit: c765134ac954f54f8b6aca6df25cf5e28911b563 Component: engine --- components/engine/pkg/units/size.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/components/engine/pkg/units/size.go b/components/engine/pkg/units/size.go index 50e8ec6c81..1594cd6549 100644 --- a/components/engine/pkg/units/size.go +++ b/components/engine/pkg/units/size.go @@ -15,10 +15,7 @@ const ( var sizeRegex *regexp.Regexp func init() { - var err error - if sizeRegex, err = regexp.Compile("^(\\d+)([kKmMgGtTpP])?[bB]?$"); err != nil { - panic("Failed to compile the 'size' regular expression") - } + sizeRegex = regexp.MustCompile("^(\\d+)([kKmMgGtTpP])?[bB]?$") } var bytePrefixes = [...]string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} From 81918c582cd8ec3904a019e1d49cd22d3020d5ee Mon Sep 17 00:00:00 2001 From: fcarriedo Date: Fri, 25 Jul 2014 09:33:04 -0700 Subject: [PATCH 08/10] pkg/units: including suggestions and enhancements Docker-DCO-1.1-Signed-off-by: fcarriedo (github: fcarriedo) Upstream-commit: 2fb63aba0bf991873d56888f244d6aeee0c5fc57 Component: engine --- components/engine/pkg/units/size.go | 61 +++++++++++++++++------------ 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/components/engine/pkg/units/size.go b/components/engine/pkg/units/size.go index 1594cd6549..f04785e048 100644 --- a/components/engine/pkg/units/size.go +++ b/components/engine/pkg/units/size.go @@ -7,9 +7,30 @@ import ( "strings" ) +type unit int64 + +// See: http://en.wikipedia.org/wiki/Binary_prefix const ( - decimalKUnit = 1000 - binaryKUnit = 1024 + // Decimal + KB unit = 1000 + MB = 1000 * KB + GB = 1000 * MB + TB = 1000 * GB + PB = 1000 * TB + + // Binary + KiB unit = 1024 + MiB = 1024 * KiB + GiB = 1024 * MiB + TiB = 1024 * GiB + PiB = 1024 * TiB +) + +type unitMap map[string]unit + +var ( + decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} + binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} ) var sizeRegex *regexp.Regexp @@ -18,7 +39,7 @@ func init() { sizeRegex = regexp.MustCompile("^(\\d+)([kKmMgGtTpP])?[bB]?$") } -var bytePrefixes = [...]string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} +var unitAbbrs = [...]string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} // HumanSize returns a human-readable approximation of a size // using SI standard (eg. "44kB", "17MB") @@ -29,13 +50,13 @@ func HumanSize(size int64) string { sizef = sizef / 1000.0 i++ } - return fmt.Sprintf("%.4g %s", sizef, bytePrefixes[i]) + return fmt.Sprintf("%.4g %s", sizef, unitAbbrs[i]) } // FromHumanSize returns an integer from a human-readable specification of a // size using SI standard (eg. "44kB", "17MB") func FromHumanSize(size string) (int64, error) { - return parseSize(size, decimalKUnit) + return parseSize(size, decimalMap) } // Parses a human-readable string representing an amount of RAM @@ -43,37 +64,25 @@ func FromHumanSize(size string) (int64, error) { // returns the number of bytes, or -1 if the string is unparseable. // Units are case-insensitive, and the 'b' suffix is optional. func RAMInBytes(size string) (int64, error) { - return parseSize(size, binaryKUnit) + return parseSize(size, binaryMap) } -// Parses the human-readable size string into the amount it represents given -// the desired kilo unit [decimalKiloUnit=1000|binaryKiloUnit=1024] -func parseSize(size string, kUnit int64) (int64, error) { - matches := sizeRegex.FindStringSubmatch(size) - +// Parses the human-readable size string into the amount it represents +func parseSize(sizeStr string, uMap unitMap) (int64, error) { + matches := sizeRegex.FindStringSubmatch(sizeStr) if len(matches) != 3 { - return -1, fmt.Errorf("Invalid size: '%s'", size) + return -1, fmt.Errorf("Invalid size: '%s'", sizeStr) } - theSize, err := strconv.ParseInt(matches[1], 10, 0) + size, err := strconv.ParseInt(matches[1], 10, 0) if err != nil { return -1, err } unitPrefix := strings.ToLower(matches[2]) - - switch unitPrefix { - case "k": - theSize *= kUnit - case "m": - theSize *= kUnit * kUnit - case "g": - theSize *= kUnit * kUnit * kUnit - case "t": - theSize *= kUnit * kUnit * kUnit * kUnit - case "p": - theSize *= kUnit * kUnit * kUnit * kUnit * kUnit + if mul, ok := uMap[unitPrefix]; ok { + size *= int64(mul) } - return theSize, nil + return size, nil } From 3b6e93d7175ed8c6d214143658f951c424640802 Mon Sep 17 00:00:00 2001 From: Francisco Carriedo Date: Sat, 26 Jul 2014 19:44:23 -0700 Subject: [PATCH 09/10] pkg/units: Unit constants directly int64 int64 seems sufficient Docker-DCO-1.1-Signed-off-by: Francisco Carriedo (github: fcarriedo) Upstream-commit: c7a2e86b5186acb58999d7b2b3a85a9bb4421b1f Component: engine --- components/engine/pkg/units/size.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/components/engine/pkg/units/size.go b/components/engine/pkg/units/size.go index f04785e048..88d91dd99e 100644 --- a/components/engine/pkg/units/size.go +++ b/components/engine/pkg/units/size.go @@ -7,26 +7,24 @@ import ( "strings" ) -type unit int64 - // See: http://en.wikipedia.org/wiki/Binary_prefix const ( // Decimal - KB unit = 1000 - MB = 1000 * KB - GB = 1000 * MB - TB = 1000 * GB - PB = 1000 * TB + KB = 1000 + MB = 1000 * KB + GB = 1000 * MB + TB = 1000 * GB + PB = 1000 * TB // Binary - KiB unit = 1024 - MiB = 1024 * KiB - GiB = 1024 * MiB - TiB = 1024 * GiB - PiB = 1024 * TiB + KiB = 1024 + MiB = 1024 * KiB + GiB = 1024 * MiB + TiB = 1024 * GiB + PiB = 1024 * TiB ) -type unitMap map[string]unit +type unitMap map[string]int64 var ( decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} @@ -81,7 +79,7 @@ func parseSize(sizeStr string, uMap unitMap) (int64, error) { unitPrefix := strings.ToLower(matches[2]) if mul, ok := uMap[unitPrefix]; ok { - size *= int64(mul) + size *= mul } return size, nil From f378b5d004751db53312511b2028110bc0eee255 Mon Sep 17 00:00:00 2001 From: Francisco Carriedo Date: Sat, 26 Jul 2014 19:48:02 -0700 Subject: [PATCH 10/10] pkg/units: Updated tests with unit constants Also, now that I was at it, gave them a small refactor. Docker-DCO-1.1-Signed-off-by: Francisco Carriedo (github: fcarriedo) Upstream-commit: 81c7d28b842844778a652b7353f52332f4276afb Component: engine --- components/engine/pkg/units/duration_test.go | 6 - components/engine/pkg/units/size_test.go | 145 ++++++++++--------- 2 files changed, 75 insertions(+), 76 deletions(-) diff --git a/components/engine/pkg/units/duration_test.go b/components/engine/pkg/units/duration_test.go index 4906f8a18b..a22947402b 100644 --- a/components/engine/pkg/units/duration_test.go +++ b/components/engine/pkg/units/duration_test.go @@ -44,9 +44,3 @@ func TestHumanDuration(t *testing.T) { assertEquals(t, "2.010959 years", HumanDuration(24*month+2*week)) assertEquals(t, "3.164384 years", HumanDuration(3*year+2*month)) } - -func assertEquals(t *testing.T, expected, actual interface{}) { - if expected != actual { - t.Errorf("Expected '%s' but got '%s'", expected, actual) - } -} diff --git a/components/engine/pkg/units/size_test.go b/components/engine/pkg/units/size_test.go index a2dfedceb4..8dae7e716b 100644 --- a/components/engine/pkg/units/size_test.go +++ b/components/engine/pkg/units/size_test.go @@ -1,6 +1,9 @@ package units import ( + "reflect" + "runtime" + "strings" "testing" ) @@ -9,85 +12,87 @@ func TestHumanSize(t *testing.T) { assertEquals(t, "1.024 kB", HumanSize(1024)) assertEquals(t, "1 MB", HumanSize(1000000)) assertEquals(t, "1.049 MB", HumanSize(1048576)) - assertEquals(t, "2 MB", HumanSize(2*1000*1000)) - assertEquals(t, "3.42 GB", HumanSize(3.42*1000*1000*1000)) - assertEquals(t, "5.372 TB", HumanSize(5.372*1000*1000*1000*1000)) - assertEquals(t, "2.22 PB", HumanSize(2.22*1000*1000*1000*1000*1000)) - assertEquals(t, "2.22 EB", HumanSize(2.22*1000*1000*1000*1000*1000*1000)) - assertEquals(t, "7.707 EB", HumanSize(7.707*1000*1000*1000*1000*1000*1000)) + assertEquals(t, "2 MB", HumanSize(2*MB)) + assertEquals(t, "3.42 GB", HumanSize(3.42*GB)) + assertEquals(t, "5.372 TB", HumanSize(5.372*TB)) + assertEquals(t, "2.22 PB", HumanSize(2.22*PB)) } func TestFromHumanSize(t *testing.T) { - assertFromHumanSize(t, "32", false, 32) - assertFromHumanSize(t, "32b", false, 32) - assertFromHumanSize(t, "32B", false, 32) - assertFromHumanSize(t, "32k", false, 32*1000) - assertFromHumanSize(t, "32K", false, 32*1000) - assertFromHumanSize(t, "32kb", false, 32*1000) - assertFromHumanSize(t, "32Kb", false, 32*1000) - assertFromHumanSize(t, "32Mb", false, 32*1000*1000) - assertFromHumanSize(t, "32Gb", false, 32*1000*1000*1000) - assertFromHumanSize(t, "32Tb", false, 32*1000*1000*1000*1000) - assertFromHumanSize(t, "8Pb", false, 8*1000*1000*1000*1000*1000) + assertSuccessEquals(t, 32, FromHumanSize, "32") + assertSuccessEquals(t, 32, FromHumanSize, "32b") + assertSuccessEquals(t, 32, FromHumanSize, "32B") + assertSuccessEquals(t, 32*KB, FromHumanSize, "32k") + assertSuccessEquals(t, 32*KB, FromHumanSize, "32K") + assertSuccessEquals(t, 32*KB, FromHumanSize, "32kb") + assertSuccessEquals(t, 32*KB, FromHumanSize, "32Kb") + assertSuccessEquals(t, 32*MB, FromHumanSize, "32Mb") + assertSuccessEquals(t, 32*GB, FromHumanSize, "32Gb") + assertSuccessEquals(t, 32*TB, FromHumanSize, "32Tb") + assertSuccessEquals(t, 32*PB, FromHumanSize, "32Pb") - assertFromHumanSize(t, "", true, -1) - assertFromHumanSize(t, "hello", true, -1) - assertFromHumanSize(t, "-32", true, -1) - assertFromHumanSize(t, " 32 ", true, -1) - assertFromHumanSize(t, "32 mb", true, -1) - assertFromHumanSize(t, "32m b", true, -1) - assertFromHumanSize(t, "32bm", true, -1) -} - -func assertFromHumanSize(t *testing.T, size string, expectError bool, expectedBytes int64) { - actualBytes, err := FromHumanSize(size) - if (err != nil) && !expectError { - t.Errorf("Unexpected error parsing '%s': %s", size, err) - } - if (err == nil) && expectError { - t.Errorf("Expected to get an error parsing '%s', but got none (bytes=%d)", size, actualBytes) - } - if actualBytes != expectedBytes { - t.Errorf("Expected '%s' to parse as %d bytes, got %d", size, expectedBytes, actualBytes) - } + assertError(t, FromHumanSize, "") + assertError(t, FromHumanSize, "hello") + assertError(t, FromHumanSize, "-32") + assertError(t, FromHumanSize, "32.3") + assertError(t, FromHumanSize, " 32 ") + assertError(t, FromHumanSize, "32.3Kb") + assertError(t, FromHumanSize, "32 mb") + assertError(t, FromHumanSize, "32m b") + assertError(t, FromHumanSize, "32bm") } func TestRAMInBytes(t *testing.T) { - assertRAMInBytes(t, "32", false, 32) - assertRAMInBytes(t, "32b", false, 32) - assertRAMInBytes(t, "32B", false, 32) - assertRAMInBytes(t, "32k", false, 32*1024) - assertRAMInBytes(t, "32K", false, 32*1024) - assertRAMInBytes(t, "32kb", false, 32*1024) - assertRAMInBytes(t, "32Kb", false, 32*1024) - assertRAMInBytes(t, "32Mb", false, 32*1024*1024) - assertRAMInBytes(t, "32MB", false, 32*1024*1024) - assertRAMInBytes(t, "32Gb", false, 32*1024*1024*1024) - assertRAMInBytes(t, "32G", false, 32*1024*1024*1024) - assertRAMInBytes(t, "32GB", false, 32*1024*1024*1024) - assertRAMInBytes(t, "32Tb", false, 32*1024*1024*1024*1024) - assertRAMInBytes(t, "8Pb", false, 8*1024*1024*1024*1024*1024) - assertRAMInBytes(t, "8PB", false, 8*1024*1024*1024*1024*1024) - assertRAMInBytes(t, "8P", false, 8*1024*1024*1024*1024*1024) + assertSuccessEquals(t, 32, RAMInBytes, "32") + assertSuccessEquals(t, 32, RAMInBytes, "32b") + assertSuccessEquals(t, 32, RAMInBytes, "32B") + assertSuccessEquals(t, 32*KiB, RAMInBytes, "32k") + assertSuccessEquals(t, 32*KiB, RAMInBytes, "32K") + assertSuccessEquals(t, 32*KiB, RAMInBytes, "32kb") + assertSuccessEquals(t, 32*KiB, RAMInBytes, "32Kb") + assertSuccessEquals(t, 32*MiB, RAMInBytes, "32Mb") + assertSuccessEquals(t, 32*GiB, RAMInBytes, "32Gb") + assertSuccessEquals(t, 32*TiB, RAMInBytes, "32Tb") + assertSuccessEquals(t, 32*PiB, RAMInBytes, "32Pb") + assertSuccessEquals(t, 32*PiB, RAMInBytes, "32PB") + assertSuccessEquals(t, 32*PiB, RAMInBytes, "32P") - assertRAMInBytes(t, "", true, -1) - assertRAMInBytes(t, "hello", true, -1) - assertRAMInBytes(t, "-32", true, -1) - assertRAMInBytes(t, " 32 ", true, -1) - assertRAMInBytes(t, "32 mb", true, -1) - assertRAMInBytes(t, "32m b", true, -1) - assertRAMInBytes(t, "32bm", true, -1) + assertError(t, RAMInBytes, "") + assertError(t, RAMInBytes, "hello") + assertError(t, RAMInBytes, "-32") + assertError(t, RAMInBytes, "32.3") + assertError(t, RAMInBytes, " 32 ") + assertError(t, RAMInBytes, "32.3Kb") + assertError(t, RAMInBytes, "32 mb") + assertError(t, RAMInBytes, "32m b") + assertError(t, RAMInBytes, "32bm") } -func assertRAMInBytes(t *testing.T, size string, expectError bool, expectedBytes int64) { - actualBytes, err := RAMInBytes(size) - if (err != nil) && !expectError { - t.Errorf("Unexpected error parsing '%s': %s", size, err) - } - if (err == nil) && expectError { - t.Errorf("Expected to get an error parsing '%s', but got none (bytes=%d)", size, actualBytes) - } - if actualBytes != expectedBytes { - t.Errorf("Expected '%s' to parse as %d bytes, got %d", size, expectedBytes, actualBytes) +func assertEquals(t *testing.T, expected, actual interface{}) { + if expected != actual { + t.Errorf("Expected '%v' but got '%v'", expected, actual) + } +} + +// func that maps to the parse function signatures as testing abstraction +type parseFn func(string) (int64, error) + +// Define 'String()' for pretty-print +func (fn parseFn) String() string { + fnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() + return fnName[strings.LastIndex(fnName, ".")+1:] +} + +func assertSuccessEquals(t *testing.T, expected int64, fn parseFn, arg string) { + res, err := fn(arg) + if err != nil || res != expected { + t.Errorf("%s(\"%s\") -> expected '%d' but got '%d' with error '%v'", fn, arg, expected, res, err) + } +} + +func assertError(t *testing.T, fn parseFn, arg string) { + res, err := fn(arg) + if err == nil && res != -1 { + t.Errorf("%s(\"%s\") -> expected error but got '%d'", fn, arg, res) } }