| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package tar |
| |
| import ( |
| "math" |
| "strings" |
| "testing" |
| "time" |
| ) |
| |
| func TestFitsInBase256(t *testing.T) { |
| vectors := []struct { |
| in int64 |
| width int |
| ok bool |
| }{ |
| {+1, 8, true}, |
| {0, 8, true}, |
| {-1, 8, true}, |
| {1 << 56, 8, false}, |
| {(1 << 56) - 1, 8, true}, |
| {-1 << 56, 8, true}, |
| {(-1 << 56) - 1, 8, false}, |
| {121654, 8, true}, |
| {-9849849, 8, true}, |
| {math.MaxInt64, 9, true}, |
| {0, 9, true}, |
| {math.MinInt64, 9, true}, |
| {math.MaxInt64, 12, true}, |
| {0, 12, true}, |
| {math.MinInt64, 12, true}, |
| } |
| |
| for _, v := range vectors { |
| ok := fitsInBase256(v.width, v.in) |
| if ok != v.ok { |
| t.Errorf("fitsInBase256(%d, %d): got %v, want %v", v.in, v.width, ok, v.ok) |
| } |
| } |
| } |
| |
| func TestParseNumeric(t *testing.T) { |
| vectors := []struct { |
| in string |
| want int64 |
| ok bool |
| }{ |
| // Test base-256 (binary) encoded values. |
| {"", 0, true}, |
| {"\x80", 0, true}, |
| {"\x80\x00", 0, true}, |
| {"\x80\x00\x00", 0, true}, |
| {"\xbf", (1 << 6) - 1, true}, |
| {"\xbf\xff", (1 << 14) - 1, true}, |
| {"\xbf\xff\xff", (1 << 22) - 1, true}, |
| {"\xff", -1, true}, |
| {"\xff\xff", -1, true}, |
| {"\xff\xff\xff", -1, true}, |
| {"\xc0", -1 * (1 << 6), true}, |
| {"\xc0\x00", -1 * (1 << 14), true}, |
| {"\xc0\x00\x00", -1 * (1 << 22), true}, |
| {"\x87\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true}, |
| {"\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true}, |
| {"\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true}, |
| {"\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true}, |
| {"\x80\x7f\xff\xff\xff\xff\xff\xff\xff", math.MaxInt64, true}, |
| {"\x80\x80\x00\x00\x00\x00\x00\x00\x00", 0, false}, |
| {"\xff\x80\x00\x00\x00\x00\x00\x00\x00", math.MinInt64, true}, |
| {"\xff\x7f\xff\xff\xff\xff\xff\xff\xff", 0, false}, |
| {"\xf5\xec\xd1\xc7\x7e\x5f\x26\x48\x81\x9f\x8f\x9b", 0, false}, |
| |
| // Test base-8 (octal) encoded values. |
| {"0000000\x00", 0, true}, |
| {" \x0000000\x00", 0, true}, |
| {" \x0000003\x00", 3, true}, |
| {"00000000227\x00", 0227, true}, |
| {"032033\x00 ", 032033, true}, |
| {"320330\x00 ", 0320330, true}, |
| {"0000660\x00 ", 0660, true}, |
| {"\x00 0000660\x00 ", 0660, true}, |
| {"0123456789abcdef", 0, false}, |
| {"0123456789\x00abcdef", 0, false}, |
| {"01234567\x0089abcdef", 342391, true}, |
| {"0123\x7e\x5f\x264123", 0, false}, |
| } |
| |
| for _, v := range vectors { |
| var p parser |
| got := p.parseNumeric([]byte(v.in)) |
| ok := (p.err == nil) |
| if ok != v.ok { |
| if v.ok { |
| t.Errorf("parseNumeric(%q): got parsing failure, want success", v.in) |
| } else { |
| t.Errorf("parseNumeric(%q): got parsing success, want failure", v.in) |
| } |
| } |
| if ok && got != v.want { |
| t.Errorf("parseNumeric(%q): got %d, want %d", v.in, got, v.want) |
| } |
| } |
| } |
| |
| func TestFormatNumeric(t *testing.T) { |
| vectors := []struct { |
| in int64 |
| want string |
| ok bool |
| }{ |
| // Test base-256 (binary) encoded values. |
| {-1, "\xff", true}, |
| {-1, "\xff\xff", true}, |
| {-1, "\xff\xff\xff", true}, |
| {(1 << 0), "0", false}, |
| {(1 << 8) - 1, "\x80\xff", true}, |
| {(1 << 8), "0\x00", false}, |
| {(1 << 16) - 1, "\x80\xff\xff", true}, |
| {(1 << 16), "00\x00", false}, |
| {-1 * (1 << 0), "\xff", true}, |
| {-1*(1<<0) - 1, "0", false}, |
| {-1 * (1 << 8), "\xff\x00", true}, |
| {-1*(1<<8) - 1, "0\x00", false}, |
| {-1 * (1 << 16), "\xff\x00\x00", true}, |
| {-1*(1<<16) - 1, "00\x00", false}, |
| {537795476381659745, "0000000\x00", false}, |
| {537795476381659745, "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", true}, |
| {-615126028225187231, "0000000\x00", false}, |
| {-615126028225187231, "\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", true}, |
| {math.MaxInt64, "0000000\x00", false}, |
| {math.MaxInt64, "\x80\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff", true}, |
| {math.MinInt64, "0000000\x00", false}, |
| {math.MinInt64, "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00", true}, |
| {math.MaxInt64, "\x80\x7f\xff\xff\xff\xff\xff\xff\xff", true}, |
| {math.MinInt64, "\xff\x80\x00\x00\x00\x00\x00\x00\x00", true}, |
| } |
| |
| for _, v := range vectors { |
| var f formatter |
| got := make([]byte, len(v.want)) |
| f.formatNumeric(got, v.in) |
| ok := (f.err == nil) |
| if ok != v.ok { |
| if v.ok { |
| t.Errorf("formatNumeric(%d): got formatting failure, want success", v.in) |
| } else { |
| t.Errorf("formatNumeric(%d): got formatting success, want failure", v.in) |
| } |
| } |
| if string(got) != v.want { |
| t.Errorf("formatNumeric(%d): got %q, want %q", v.in, got, v.want) |
| } |
| } |
| } |
| |
| func TestParsePAXTime(t *testing.T) { |
| timestamps := map[string]time.Time{ |
| "1350244992.023960108": time.Unix(1350244992, 23960108), // The common case |
| "1350244992.02396010": time.Unix(1350244992, 23960100), // Lower precision value |
| "1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value |
| "1350244992": time.Unix(1350244992, 0), // Low precision value |
| } |
| |
| for input, expected := range timestamps { |
| ts, err := parsePAXTime(input) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !ts.Equal(expected) { |
| t.Fatalf("Time parsing failure %s %s", ts, expected) |
| } |
| } |
| } |
| |
| func TestParsePAXRecord(t *testing.T) { |
| medName := strings.Repeat("CD", 50) |
| longName := strings.Repeat("AB", 100) |
| |
| vectors := []struct { |
| in string |
| wantRes string |
| wantKey string |
| wantVal string |
| ok bool |
| }{ |
| {"6 k=v\n\n", "\n", "k", "v", true}, |
| {"19 path=/etc/hosts\n", "", "path", "/etc/hosts", true}, |
| {"210 path=" + longName + "\nabc", "abc", "path", longName, true}, |
| {"110 path=" + medName + "\n", "", "path", medName, true}, |
| {"9 foo=ba\n", "", "foo", "ba", true}, |
| {"11 foo=bar\n\x00", "\x00", "foo", "bar", true}, |
| {"18 foo=b=\nar=\n==\x00\n", "", "foo", "b=\nar=\n==\x00", true}, |
| {"27 foo=hello9 foo=ba\nworld\n", "", "foo", "hello9 foo=ba\nworld", true}, |
| {"27 ☺☻☹=日a本b語ç\nmeow mix", "meow mix", "☺☻☹", "日a本b語ç", true}, |
| {"17 \x00hello=\x00world\n", "", "\x00hello", "\x00world", true}, |
| {"1 k=1\n", "1 k=1\n", "", "", false}, |
| {"6 k~1\n", "6 k~1\n", "", "", false}, |
| {"6_k=1\n", "6_k=1\n", "", "", false}, |
| {"6 k=1 ", "6 k=1 ", "", "", false}, |
| {"632 k=1\n", "632 k=1\n", "", "", false}, |
| {"16 longkeyname=hahaha\n", "16 longkeyname=hahaha\n", "", "", false}, |
| {"3 somelongkey=\n", "3 somelongkey=\n", "", "", false}, |
| {"50 tooshort=\n", "50 tooshort=\n", "", "", false}, |
| } |
| |
| for _, v := range vectors { |
| key, val, res, err := parsePAXRecord(v.in) |
| ok := (err == nil) |
| if ok != v.ok { |
| if v.ok { |
| t.Errorf("parsePAXRecord(%q): got parsing failure, want success", v.in) |
| } else { |
| t.Errorf("parsePAXRecord(%q): got parsing success, want failure", v.in) |
| } |
| } |
| if v.ok && (key != v.wantKey || val != v.wantVal) { |
| t.Errorf("parsePAXRecord(%q): got (%q: %q), want (%q: %q)", |
| v.in, key, val, v.wantKey, v.wantVal) |
| } |
| if res != v.wantRes { |
| t.Errorf("parsePAXRecord(%q): got residual %q, want residual %q", |
| v.in, res, v.wantRes) |
| } |
| } |
| } |
| |
| func TestFormatPAXRecord(t *testing.T) { |
| medName := strings.Repeat("CD", 50) |
| longName := strings.Repeat("AB", 100) |
| |
| vectors := []struct { |
| inKey string |
| inVal string |
| want string |
| }{ |
| {"k", "v", "6 k=v\n"}, |
| {"path", "/etc/hosts", "19 path=/etc/hosts\n"}, |
| {"path", longName, "210 path=" + longName + "\n"}, |
| {"path", medName, "110 path=" + medName + "\n"}, |
| {"foo", "ba", "9 foo=ba\n"}, |
| {"foo", "bar", "11 foo=bar\n"}, |
| {"foo", "b=\nar=\n==\x00", "18 foo=b=\nar=\n==\x00\n"}, |
| {"foo", "hello9 foo=ba\nworld", "27 foo=hello9 foo=ba\nworld\n"}, |
| {"☺☻☹", "日a本b語ç", "27 ☺☻☹=日a本b語ç\n"}, |
| {"\x00hello", "\x00world", "17 \x00hello=\x00world\n"}, |
| } |
| |
| for _, v := range vectors { |
| got := formatPAXRecord(v.inKey, v.inVal) |
| if got != v.want { |
| t.Errorf("formatPAXRecord(%q, %q): got %q, want %q", |
| v.inKey, v.inVal, got, v.want) |
| } |
| } |
| } |