| // Copyright 2017 Google Inc. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package serializer | |
| import ( | |
| "archive/tar" | |
| "bytes" | |
| "compress/gzip" | |
| "fmt" | |
| "io" | |
| "log" | |
| "os" | |
| "reflect" | |
| "sort" | |
| "strings" | |
| "testing" | |
| "github.com/google/licenseclassifier" | |
| "github.com/google/licenseclassifier/stringclassifier/searchset" | |
| ) | |
| var ( | |
| apache20Header, mit []byte | |
| normApache, normMIT string | |
| ) | |
| func TestMain(m *testing.M) { | |
| var err error | |
| apache20Header, err = licenseclassifier.ReadLicenseFile("Apache-2.0.header.txt") | |
| if err != nil { | |
| log.Fatalf("error reading contents of Apache-2.0.header.txt: %v", err) | |
| } | |
| normApache = normalize(string(apache20Header)) | |
| mit, err = licenseclassifier.ReadLicenseFile("MIT.txt") | |
| if err != nil { | |
| log.Fatalf("error reading contents of MIT.txt: %v", err) | |
| } | |
| normMIT = normalize(string(mit)) | |
| os.Exit(m.Run()) | |
| } | |
| type entry struct { | |
| name string | |
| size int64 | |
| contents string | |
| } | |
| func TestSerializer_ArchiveLicense(t *testing.T) { | |
| tests := []struct { | |
| description string | |
| licenses []string | |
| want []entry | |
| }{ | |
| { | |
| description: "Archiving Apache 2.0 header", | |
| licenses: []string{"Apache-2.0.header.txt"}, | |
| want: []entry{ | |
| { | |
| name: "Apache-2.0.header.txt", | |
| size: int64(len(normApache)), | |
| contents: normApache, | |
| }, | |
| }, | |
| }, | |
| { | |
| description: "Archiving Apache 2.0 header + MIT", | |
| licenses: []string{"Apache-2.0.header.txt", "MIT.txt"}, | |
| want: []entry{ | |
| { | |
| name: "Apache-2.0.header.txt", | |
| size: int64(len(normApache)), | |
| contents: normApache, | |
| }, | |
| { | |
| name: "MIT.txt", | |
| size: int64(len(normMIT)), | |
| contents: normMIT, | |
| }, | |
| }, | |
| }, | |
| } | |
| for _, tt := range tests { | |
| var writer bytes.Buffer | |
| if err := ArchiveLicenses(tt.licenses, &writer); err != nil { | |
| t.Errorf("ArchiveLicenses(%q): cannot archive license: %v", tt.description, err) | |
| continue | |
| } | |
| reader := bytes.NewReader(writer.Bytes()) | |
| gr, err := gzip.NewReader(reader) | |
| if err != nil { | |
| t.Errorf("ArchiveLicenses(%q): cannot create gzip reader: %v", tt.description, err) | |
| continue | |
| } | |
| tr := tar.NewReader(gr) | |
| for i := 0; ; i++ { | |
| hdr, err := tr.Next() | |
| if err == io.EOF { | |
| break | |
| } | |
| if err != nil { | |
| t.Errorf("ArchiveLicenses(%q): cannot read header: %v", tt.description, err) | |
| break | |
| } | |
| if i >= len(tt.want)+1 { | |
| t.Errorf("ArchiveLicenses(%q): too many files in tar, %d want %d", tt.description, i, len(tt.want)) | |
| break | |
| } | |
| if hdr.Name != tt.want[i].name { | |
| t.Errorf("ArchiveLicenses(%q) = %+v, want %+v", tt.description, hdr.Name, tt.want[i].name) | |
| } | |
| if hdr.Size != tt.want[i].size { | |
| t.Errorf("ArchiveLicenses(%q) = %v, want %v", tt.description, hdr.Size, tt.want[i].size) | |
| } | |
| var b bytes.Buffer | |
| if _, err = io.Copy(&b, tr); err != nil { | |
| t.Errorf("ArchiveLicenses(%q): cannot read contents: %v", tt.description, err) | |
| break | |
| } | |
| if got, want := b.String(), tt.want[i].contents; got != want { | |
| t.Errorf("ArchiveLicenses(%q) = got\n%s\nwant:\n%s", tt.description, got, want) | |
| } | |
| hdr, err = tr.Next() | |
| if err != nil { | |
| t.Errorf("ArchiveLicenses(%q): no hash file found in archive: %v", tt.description, err) | |
| break | |
| } | |
| if hdr.Name != strings.TrimSuffix(tt.want[i].name, "txt")+"hash" { | |
| t.Errorf("ArchiveLicenses(%q) = %+v, want %+v", tt.description, hdr.Name, strings.TrimSuffix(tt.want[i].name, "txt")+"hash") | |
| } | |
| b.Reset() | |
| if _, err = io.Copy(&b, tr); err != nil { | |
| t.Errorf("ArchiveLicenses(%q): cannot read contents: %v", tt.description, err) | |
| break | |
| } | |
| var got searchset.SearchSet | |
| if err := searchset.Deserialize(&b, &got); err != nil { | |
| t.Errorf("ArchiveLicenses(%q): cannot deserialize search set: %v", tt.description, err) | |
| break | |
| } | |
| want := searchset.New(tt.want[i].contents, searchset.DefaultGranularity) | |
| if err := compareSearchSets(want, &got); err != nil { | |
| t.Errorf("ArchiveLicenses(%q): search sets not equal: %v", tt.description, err) | |
| break | |
| } | |
| } | |
| } | |
| } | |
| type sortUInt32 []uint32 | |
| func (s sortUInt32) Len() int { return len(s) } | |
| func (s sortUInt32) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | |
| func (s sortUInt32) Less(i, j int) bool { return s[i] < s[j] } | |
| func compareSearchSets(x, y *searchset.SearchSet) error { | |
| // Check to see that the tokens are equal. | |
| if len(x.Tokens) != len(y.Tokens) { | |
| return fmt.Errorf("Lengths differ = %d vs %d", len(x.Tokens), len(y.Tokens)) | |
| } | |
| for i := 0; i < len(x.Tokens); i++ { | |
| if x.Tokens[i].Text != y.Tokens[i].Text { | |
| return fmt.Errorf("Token values at %d differ = %q vs %q", i, x.Tokens[i].Text, y.Tokens[i].Text) | |
| } | |
| if x.Tokens[i].Offset != y.Tokens[i].Offset { | |
| return fmt.Errorf("Token offsets at %d differ = %d vs %d", i, x.Tokens[i].Offset, y.Tokens[i].Offset) | |
| } | |
| } | |
| // Now check that the hash maps are equal. | |
| var xKeys []uint32 | |
| for k := range x.Hashes { | |
| xKeys = append(xKeys, k) | |
| } | |
| var yKeys []uint32 | |
| for k := range y.Hashes { | |
| yKeys = append(yKeys, k) | |
| } | |
| if len(xKeys) != len(yKeys) { | |
| return fmt.Errorf("Lengths of hashes differ = %d vs %d", len(xKeys), len(yKeys)) | |
| } | |
| sort.Sort(sortUInt32(xKeys)) | |
| sort.Sort(sortUInt32(yKeys)) | |
| for i := 0; i < len(xKeys); i++ { | |
| if xKeys[i] != yKeys[i] { | |
| return fmt.Errorf("Hash keys differ = %d vs %d", xKeys[i], yKeys[i]) | |
| } | |
| if !reflect.DeepEqual(x.Hashes[xKeys[i]], y.Hashes[yKeys[i]]) { | |
| return fmt.Errorf("Hash values differ = %v vs %v", x.Hashes[xKeys[i]], y.Hashes[yKeys[i]]) | |
| } | |
| } | |
| return nil | |
| } | |
| func normalize(s string) string { | |
| for _, n := range licenseclassifier.Normalizers { | |
| s = n(s) | |
| } | |
| return s | |
| } |