gitlab.com/SkynetLabs/skyd@v1.6.9/cmd/skyc/parse_test.go (about) 1 package main 2 3 import ( 4 "math" 5 "math/big" 6 "testing" 7 8 "gitlab.com/NebulousLabs/errors" 9 "gitlab.com/NebulousLabs/fastrand" 10 "go.sia.tech/siad/types" 11 ) 12 13 // TestParseFileSize probes the parseFilesize function 14 func TestParseFilesize(t *testing.T) { 15 tests := []struct { 16 in, out string 17 err error 18 }{ 19 {"1b", "1", nil}, 20 {"1 b", "1", nil}, 21 {"1KB", "1000", nil}, 22 {"1 kb", "1000", nil}, 23 {"1 kB", "1000", nil}, 24 {" 1Kb ", "1000", nil}, 25 {"1MB", "1000000", nil}, 26 {"1 MB", "1000000", nil}, 27 {" 1GB ", "1000000000", nil}, 28 {"1 GB ", "1000000000", nil}, 29 {"1TB", "1000000000000", nil}, 30 {"1 TB", "1000000000000", nil}, 31 {"1KiB", "1024", nil}, 32 {"1 KiB", "1024", nil}, 33 {"1MiB", "1048576", nil}, 34 {"1 MiB", "1048576", nil}, 35 {"1GiB", "1073741824", nil}, 36 {"1 GiB", "1073741824", nil}, 37 {"1TiB", "1099511627776", nil}, 38 {"1 TiB", "1099511627776", nil}, 39 {"", "", ErrParseSizeUnits}, 40 {"123", "", ErrParseSizeUnits}, 41 {"123b", "123", nil}, 42 {"123 TB", "123000000000000", nil}, 43 {"123GiB", "132070244352", nil}, 44 {"123BiB", "", ErrParseSizeAmount}, 45 {"GB", "", ErrParseSizeAmount}, 46 {"123G", "", ErrParseSizeUnits}, 47 {"123B99", "", ErrParseSizeUnits}, 48 {"12A3456", "", ErrParseSizeUnits}, 49 {"1.23KB", "1230", nil}, 50 {"1.234 KB", "1234", nil}, 51 {"1.2345KB", "1234", nil}, 52 } 53 for _, test := range tests { 54 res, err := parseFilesize(test.in) 55 if res != test.out || err != test.err { 56 t.Errorf("parseFilesize(%v): expected %v %v, got %v %v", test.in, test.out, test.err, res, err) 57 } 58 } 59 } 60 61 // TestParsePeriod probes the parsePeriod function 62 func TestParsePeriod(t *testing.T) { 63 tests := []struct { 64 in, out string 65 err error 66 }{ 67 {"x", "", ErrParsePeriodUnits}, 68 {"1", "", ErrParsePeriodUnits}, 69 {"b", "", ErrParsePeriodAmount}, 70 {"1b", "1", nil}, 71 {"1 b", "1", nil}, 72 {"1block", "1", nil}, 73 {"1 block ", "1", nil}, 74 {"1blocks", "1", nil}, 75 {"1 blocks", "1", nil}, 76 {" 2b ", "2", nil}, 77 {"2 b", "2", nil}, 78 {"2block", "2", nil}, 79 {"2 block", "2", nil}, 80 {"2blocks", "2", nil}, 81 {"2 blocks", "2", nil}, 82 {"2h", "12", nil}, 83 {"2 h", "12", nil}, 84 {"2hour", "12", nil}, 85 {"2 hour", "12", nil}, 86 {" 2hours ", "12", nil}, 87 {"2 hours", "12", nil}, 88 {"0.5d", "72", nil}, 89 {" 0.5 d", "72", nil}, 90 {"0.5day", "72", nil}, 91 {"0.5 day", "72", nil}, 92 {"0.5days", "72", nil}, 93 {"0.5 days", "72", nil}, 94 {"10w", "10080", nil}, 95 {"10 w", "10080", nil}, 96 {"10week", "10080", nil}, 97 {"10 week", "10080", nil}, 98 {"10weeks", "10080", nil}, 99 {"10 weeks", "10080", nil}, 100 {"1 fortnight", "", ErrParsePeriodUnits}, 101 {"three h", "", ErrParsePeriodAmount}, 102 } 103 for _, test := range tests { 104 res, err := parsePeriod(test.in) 105 if res != test.out || err != test.err { 106 t.Errorf("parsePeriod(%v): expected %v %v, got %v %v", test.in, test.out, test.err, res, err) 107 } 108 } 109 } 110 111 // TestCurrencyUnits probes the currencyUnits function 112 func TestCurrencyUnits(t *testing.T) { 113 tests := []struct { 114 in, out string 115 }{ 116 {"1", "1 H"}, 117 {"1000", "1000 H"}, 118 {"100000000000", "100000000000 H"}, 119 {"1000000000000", "1 pS"}, 120 {"1234560000000", "1.235 pS"}, 121 {"12345600000000", "12.35 pS"}, 122 {"123456000000000", "123.5 pS"}, 123 {"1000000000000000", "1 nS"}, 124 {"1000000000000000000", "1 uS"}, 125 {"1000000000000000000000", "1 mS"}, 126 {"1000000000000000000000000", "1 SC"}, 127 {"1000000000000000000000000000", "1 KS"}, 128 {"1000000000000000000000000000000", "1 MS"}, 129 {"1000000000000000000000000000000000", "1 GS"}, 130 {"1000000000000000000000000000000000000", "1 TS"}, 131 {"1234560000000000000000000000000000000", "1.235 TS"}, 132 {"1234560000000000000000000000000000000000", "1235 TS"}, 133 } 134 for _, test := range tests { 135 i, _ := new(big.Int).SetString(test.in, 10) 136 out := currencyUnits(types.NewCurrency(i)) 137 if out != test.out { 138 t.Errorf("currencyUnits(%v): expected %v, got %v", test.in, test.out, out) 139 } 140 } 141 } 142 143 // TestCurrencyUnitsWithExchangeRate probes the currencyUnitsWithExchangeRate 144 // function. Here we only test for the general format. The precise formatting of 145 // the foreign currency amount is further tested in types/exchangerate_test.go . 146 func TestCurrencyUnitsWithExchangeRate(t *testing.T) { 147 tests := []struct { 148 cStr string 149 rateStr string 150 result string 151 }{ 152 {"1", "", "1 H"}, 153 {"1000000000000000000000", "n/a", "1 mS"}, 154 {"1000000000000000000000000", "---", "1 SC"}, 155 {"1000000000000000000000000000", "", "1 KS"}, 156 {"1000000000000000000000000", "1 USD", "1 SC (~ 1.00 USD)"}, 157 {"100000000000000000000000", "1 EUR", "100 mS (~ 0.10 EUR)"}, 158 {"10000000000000000000000", "10 EUR", "10 mS (~ 0.10 EUR)"}, 159 {"1000000000000000000000000000", "0.0039 USD", "1 KS (~ 3.90 USD)"}, 160 } 161 for _, test := range tests { 162 i, _ := new(big.Int).SetString(test.cStr, 10) 163 164 rate, _ := types.ParseExchangeRate(test.rateStr) 165 // ignore potential parse errors; those are being tested in types/exchangerate_test.go 166 167 result := currencyUnitsWithExchangeRate(types.NewCurrency(i), rate) 168 if test.result != result { 169 t.Errorf("currencyUnitsWithExchangeRate(%v, %v): expected %#v, got %#v", 170 test.cStr, test.rateStr, test.result, result) 171 } 172 } 173 } 174 175 // TestRateLimitUnits probes the ratelimitUnits function 176 func TestRatelimitUnits(t *testing.T) { 177 tests := []struct { 178 in int64 179 out string 180 }{ 181 {0, "0 B/s"}, 182 {123, "123 B/s"}, 183 {1234, "1.234 KB/s"}, 184 {1234000, "1.234 MB/s"}, 185 {1234000000, "1.234 GB/s"}, 186 {1234000000000, "1.234 TB/s"}, 187 } 188 for _, test := range tests { 189 out := ratelimitUnits(test.in) 190 if out != test.out { 191 t.Errorf("ratelimitUnits(%v): expected %v, got %v", test.in, test.out, out) 192 } 193 } 194 } 195 196 // TestParseRateLimit probes the parseRatelimit function 197 func TestParseRatelimit(t *testing.T) { 198 tests := []struct { 199 in string 200 out int64 201 err error 202 }{ 203 {"x", 0, ErrParseRateLimitUnits}, 204 {"1", 0, ErrParseRateLimitUnits}, 205 {"B/s", 0, ErrParseRateLimitNoAmount}, 206 {"Bps", 0, ErrParseRateLimitNoAmount}, 207 {"1Bps", 0, ErrParseRateLimitAmount}, 208 {" 1B/s ", 1, nil}, 209 {"1 B/s", 1, nil}, 210 {"8Bps", 1, nil}, 211 {"8 Bps", 1, nil}, 212 {" 1KB/s ", 1000, nil}, 213 {"1 KB/s", 1000, nil}, 214 {"8Kbps", 1000, nil}, 215 {" 8 Kbps", 1000, nil}, 216 {"1MB/s", 1000000, nil}, 217 {"1 MB/s", 1000000, nil}, 218 {"8Mbps", 1000000, nil}, 219 {"8 Mbps", 1000000, nil}, 220 {"1GB/s", 1000000000, nil}, 221 {"1 GB/s", 1000000000, nil}, 222 {"8Gbps", 1000000000, nil}, 223 {"8 Gbps", 1000000000, nil}, 224 {"1TB/s", 1000000000000, nil}, 225 {"1 TB/s", 1000000000000, nil}, 226 {"8Tbps", 1000000000000, nil}, 227 {"8 Tbps", 1000000000000, nil}, 228 } 229 230 for _, test := range tests { 231 res, err := parseRatelimit(test.in) 232 if res != test.out || (err != test.err && !errors.Contains(err, test.err)) { 233 t.Errorf("parsePeriod(%v): expected %v %v, got %v %v", test.in, test.out, test.err, res, err) 234 } 235 } 236 } 237 238 // TestParsePercentages probes the parsePercentages function 239 func TestParsePercentages(t *testing.T) { 240 tests := []struct { 241 in []float64 242 out []float64 243 }{ 244 {[]float64{50.0, 50.0}, []float64{50, 50}}, 245 {[]float64{49.5, 50.5}, []float64{50, 50}}, 246 {[]float64{33.1, 33.4, 33.5}, []float64{33, 33, 34}}, 247 {[]float64{63.1, 33.4, 3.5}, []float64{63, 33, 4}}, 248 {[]float64{0, 0, 100}, []float64{0, 0, 100}}, 249 {[]float64{100}, []float64{100}}, 250 } 251 252 // Test set cases to ensure known edge cases are always handled 253 for _, test := range tests { 254 res := parsePercentages(test.in) 255 for i, v := range res { 256 if v != test.out[i] { 257 t.Log("Result", res) 258 t.Log("Expected", test.out) 259 t.Fatal("Result not as expected") 260 } 261 } 262 } 263 264 // For test-long test additional random cases 265 if testing.Short() { 266 t.SkipNow() 267 } 268 269 // Test Random Edge Cases 270 for i := 0; i < 10; i++ { 271 values := parsePercentages(randomPercentages()) 272 // Since we can't know what the exact output should be, verify that the 273 // values add up to 100 and that none of the values have a non zero 274 // remainder 275 var total float64 276 for _, v := range values { 277 _, r := math.Modf(v) 278 if r != 0 { 279 t.Log(values) 280 t.Log(v) 281 t.Fatal("Found non zero remainder") 282 } 283 total += v 284 } 285 if total != float64(100) { 286 t.Log(values) 287 t.Log(total) 288 t.Fatal("Values should add up to 100 but added up to", total) 289 } 290 } 291 } 292 293 // randomPercentages creates a slice of pseudo random size, up to 500 elements, 294 // with random elements that add to 100. 295 // 296 // NOTE: this function does not explicitly check that all the elements strictly 297 // add up to 100 due to potential significant digit rounding errors. It was 298 // common to see the elements add up to 100.00000000000001. 299 func randomPercentages() []float64 { 300 var p []float64 301 302 remainder := float64(100) 303 for i := 0; i < 500; i++ { 304 n := float64(fastrand.Intn(1000)) 305 d := float64(fastrand.Intn(100000)) + n 306 val := n / d * 100 307 if math.IsNaN(val) || remainder < val { 308 continue 309 } 310 remainder -= val 311 p = append(p, val) 312 if remainder == 0 { 313 break 314 } 315 } 316 317 // Check if we have a remainder to add 318 if remainder > 0 { 319 p = append(p, remainder) 320 } 321 322 return p 323 } 324 325 // TestSizeString probes the sizeString function 326 func TestSizeString(t *testing.T) { 327 tests := []struct { 328 in uint64 329 out string 330 }{ 331 {0, "0 B"}, 332 {1, "1 B"}, 333 {123, "123 B"}, 334 {999, "999 B"}, 335 {1000, "1 KB"}, 336 {1001, "1.001 KB"}, 337 {1234, "1.234 KB"}, 338 {12340, "12.34 KB"}, 339 {123400, "123.4 KB"}, 340 {1234000, "1.234 MB"}, 341 {500000, "500 KB"}, 342 {900000, "900 KB"}, 343 {998999, "999 KB"}, 344 {999001, "999 KB"}, 345 {999998, "1 MB"}, // Should round up to 1MB 346 {999999, "1 MB"}, // Should round up to 1MB 347 {1000001, "1 MB"}, 348 {1999999, "2 MB"}, // Should round up to 2MB 349 {2000001, "2 MB"}, 350 {1234000, "1.234 MB"}, 351 {1235000000, "1.235 GB"}, // Verifies it doesn't round the last digit 352 {1234500000000, "1.234 TB"}, // Verifies it truncates and doesn't round 353 {1234490000000000, "1.234 PB"}, // Verifies it truncates and doesn't round 354 {1234000000000000000, "1.234 EB"}, 355 {math.MaxUint64, "18.45 EB"}, 356 } 357 for _, test := range tests { 358 out := sizeString(test.in) 359 if out != test.out { 360 t.Errorf("sizeString(%v): expected %v, got %v", test.in, test.out, out) 361 } 362 } 363 364 // Add some random tests for any edge case panics 365 for i := 0; i < 1e3; i++ { 366 sizeString(fastrand.Uint64n(math.MaxUint64)) 367 } 368 }