You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
6.0 KiB

package bytesize_test
import (
"math"
"math/rand"
"strings"
"testing"
"gitoa.ru/go-4devs/bytesize"
)
var parseTests = []struct {
in string
want bytesize.Size
}{
// simple
{"0", 0},
{"5KiB", 5 * bytesize.Kibibyte},
{"30KiB", 30 * bytesize.Kibibyte},
{"1478KiB", 1478 * bytesize.Kibibyte},
// sign
{"-5KiB", -5 * bytesize.Kibibyte},
{"+5KiB", 5 * bytesize.Kibibyte},
{"-0", 0},
{"+0", 0},
// decimal
{"5.0KiB", 5 * bytesize.Kibibyte},
{"5.6KiB", 5*bytesize.Kibibyte + bytesize.Kibibyte*6/10},
{"5.KiB", 5 * bytesize.Kibibyte},
{".5KiB", bytesize.Kibibyte / 2},
{"1.0KiB", 1 * bytesize.Kibibyte},
{"1.00KiB", 1 * bytesize.Kibibyte},
{"1.004KiB", 1*bytesize.Kibibyte + 4*bytesize.Byte},
{"1.0040KiB", 1*bytesize.Kibibyte + 4*bytesize.Byte},
{"100.00100KiB", 100*bytesize.Kibibyte + 1*bytesize.Byte},
// different units
{"10B", 10 * bytesize.Byte},
{"11KiB", 11 * bytesize.Kibibyte},
{"12MiB", 12 * bytesize.Mebibyte},
{"12GiB", 12 * bytesize.Gibibyte},
{"13TiB", 13 * bytesize.Tebibyte},
{"14PiB", 14 * bytesize.Pebibyte},
// composite durations
{"3PiB30TiB", 3*bytesize.Pebibyte + 30*bytesize.Tebibyte},
{"10.5MiB4TiB", 4*bytesize.Tebibyte + 10*bytesize.Mebibyte + bytesize.Mebibyte/2},
{"-2TiB3.4MiB", -(2*bytesize.Tebibyte + 3*bytesize.Mebibyte + bytesize.Mebibyte*4/10)},
{
"1PiB2TiB3GiB4MiB5KiB6B",
1*bytesize.Pebibyte + 2*bytesize.Tebibyte + 3*bytesize.Gibibyte + 4*bytesize.Mebibyte + 5*bytesize.Kibibyte + 6*bytesize.Byte,
},
{
"39PiB9TiB14.425GiB",
39*bytesize.Pebibyte + 9*bytesize.Tebibyte + 14*bytesize.Gibibyte + bytesize.Gibibyte*425/1000,
},
// large value
{"52763797000B", 52763797000 * bytesize.Byte},
// more than 9 digits after decimal point
{"0.3333333333333333333GiB", bytesize.Gibibyte * 3333333333 / 1e10},
// 9007199254740993 = 1<<53+1 cannot be stored precisely in a float64
{"9007199254740993B", (1<<53 + 1) * bytesize.Byte},
// largest duration that can be represented by int64 in nanoseconds
{"9223372036854775807B", (1<<63 - 1) * bytesize.Byte},
{"-9223372036854775808B", -1 << 63 * bytesize.Byte},
// largest negative value
{"-9223372036854775808B", -1 << 63 * bytesize.Byte},
{"0.100000000000000000000MiB", bytesize.Mebibyte * 1 / 10},
// simple
{"5GB", 5 * bytesize.Gigabyte},
{"30GB", 30 * bytesize.Gigabyte},
{"1478GB", 1478 * bytesize.Gigabyte},
// sign
{"-5GB", -5 * bytesize.Gigabyte},
{"+5GB", 5 * bytesize.Gigabyte},
// decimal
{"5.0GB", 5 * bytesize.Gigabyte},
{"5.6GB", 5*bytesize.Gigabyte + 600*bytesize.Megabyte},
{"5.GB", 5 * bytesize.Gigabyte},
{".5GB", 500 * bytesize.Megabyte},
{"1.0GB", 1 * bytesize.Gigabyte},
{"1.00GB", 1 * bytesize.Gigabyte},
{"1.004GB", 1*bytesize.Gigabyte + 4*bytesize.Megabyte},
{"1.0040GB", 1*bytesize.Gigabyte + 4*bytesize.Megabyte},
{"100.00100GB", 100*bytesize.Gigabyte + 1*bytesize.Megabyte},
// different units
{"10B", 10 * bytesize.Byte},
{"11kB", 11 * bytesize.Kilobyte},
{"12MB", 12 * bytesize.Megabyte},
{"12GB", 12 * bytesize.Gigabyte},
{"13TB", 13 * bytesize.Terabyte},
{"14PB", 14 * bytesize.Petabyte},
// composite durations
{"3GB30MB", 3*bytesize.Gigabyte + 30*bytesize.Megabyte},
{"10.5MB4GB", 4*bytesize.Gigabyte + 10*bytesize.Megabyte + 500*bytesize.Kilobyte},
{"-2TB3.4GB", -(2*bytesize.Terabyte + 3*bytesize.Gigabyte + 400*bytesize.Megabyte)},
{
"1PB2TB3GB4MB5kB6B",
1*bytesize.Petabyte + 2*bytesize.Terabyte + 3*bytesize.Gigabyte + 4*bytesize.Megabyte + 5*bytesize.Kilobyte + 6*bytesize.Byte,
},
{"39PB9TB14.425GB", 39*bytesize.Petabyte + 9*bytesize.Terabyte + 14*bytesize.Gigabyte + 425*bytesize.Megabyte},
{"9223372036854775.807kB", (1<<63 - 1) * bytesize.Byte},
{"9223372036GB854MB775kB807B", (1<<63 - 1) * bytesize.Byte},
{"-9223372036854775.808kB", -1 << 63 * bytesize.Byte},
{"-9223372036GB854MB775kB808B", -1 << 63 * bytesize.Byte},
// largest negative round trip value
{"-9223372036GB854MB775.808kB", -1 << 63 * bytesize.Byte},
}
func TestParse(t *testing.T) {
t.Parallel()
for _, tc := range parseTests {
d, err := bytesize.Parse(tc.in)
if err != nil || d != tc.want {
t.Errorf("Parse(%q) = %v, %v, want %v, nil", tc.in, d, err, tc.want)
}
}
}
var parseErrorTests = []struct {
in string
expect string
}{
// invalid
{"", `""`},
{"3", `"3"`},
{"-", `"-"`},
{"kB", `"kB"`},
{".", `"."`},
{"-.", `"-."`},
{".MB", `".MB"`},
{"+.MB", `"+.MB"`},
{"1ExB", `"1ExB"`},
{"\x85\x85", `"\x85\x85"`},
{"\xffff", `"\xffff"`},
{"hello \xffff world", `"hello \xffff world"`},
{"\uFFFD", `"�"`}, // utf8.RuneError
{"\uFFFD hello \uFFFD world", `"� hello � world"`}, // utf8.RuneError
// overflow
{"9223372036854775810B", `"9223372036854775810B"`},
{"9223372036854775808B", `"9223372036854775808B"`},
{"-9223372036854775809B", `"-9223372036854775809B"`},
{"9223372036854776kB", `"9223372036854776kB"`},
{"3000000PB", `"3000000PB"`},
{"9223372036854775.808KiB", `"9223372036854775.808KiB"`},
{"9223372036854MB775kB808B", `"9223372036854MB775kB808B"`},
}
func TestParseErrors(t *testing.T) {
t.Parallel()
for _, tc := range parseErrorTests {
_, err := bytesize.Parse(tc.in)
if err == nil {
t.Errorf("Parse(%q) = _, nil, want _, non-nil", tc.in)
} else if !strings.Contains(err.Error(), tc.expect) {
t.Errorf("Parse(%q) = _, %q, error does not contain %q", tc.in, err, tc.expect)
}
}
}
func TestParseRoundTrip(t *testing.T) {
t.Parallel()
max0 := bytesize.Size(math.MaxInt64)
max1, err := bytesize.Parse(max0.String())
if err != nil || max0 != max1 {
t.Errorf("round-trip failed: %d => %q => %d, %v", max0, max0.String(), max1, err)
}
min0 := bytesize.Size(math.MinInt64)
min1, err := bytesize.Parse(min0.String())
if err != nil || min0 != min1 {
t.Errorf("round-trip failed: %d => %q => %d, %v", min0, min0.String(), min1, err)
}
for range 100 {
// Resolutions finer than milliseconds will result in
// imprecise round-trips.
d0 := bytesize.Size(rand.Int31()) * bytesize.Megabyte
s := d0.String()
d1, err := bytesize.Parse(s)
if err != nil || d0 != d1 {
t.Errorf("round-trip failed: %d => %q => %d, %v", d0, s, d1, err)
}
}
}