blob: 769d43c64c8e482196d13a30741d5a087c12b1b5 [file] [log] [blame]
// 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 licenseclassifier
import (
"bytes"
"log"
"os"
"path/filepath"
"strings"
"testing"
"github.com/google/licenseclassifier/stringclassifier"
)
var (
agpl30, agpl30Header, apache20, bsd3, gpl20, ccbync20 string
classifier *License
)
func TestMain(m *testing.M) {
a30, err := ReadLicenseFile("AGPL-3.0.txt")
if err != nil {
log.Fatalf("error reading contents of AGPL-3.0.txt: %v", err)
}
a30h, err := ReadLicenseFile("AGPL-3.0.header.txt")
if err != nil {
log.Fatalf("error reading contents of AGPL-3.0.header.txt: %v", err)
}
a20, err := ReadLicenseFile("Apache-2.0.txt")
if err != nil {
log.Fatalf("error reading contents of Apache-2.0.txt: %v", err)
}
b3, err := ReadLicenseFile("BSD-3-Clause.txt")
if err != nil {
log.Fatalf("error reading contents of BSD-3-Clause.txt: %v", err)
}
g2, err := ReadLicenseFile("GPL-2.0.txt")
if err != nil {
log.Fatalf("error reading contents of GPL-2.0.txt: %v", err)
}
cc20, err := ReadLicenseFile("CC-BY-NC-2.0.txt")
if err != nil {
log.Fatalf("error reading contents of CC-BY-NC-2.0.txt: %v", err)
}
agpl30 = TrimExtraneousTrailingText(string(a30))
agpl30Header = TrimExtraneousTrailingText(string(a30h))
apache20 = TrimExtraneousTrailingText(string(a20))
bsd3 = TrimExtraneousTrailingText(string(b3))
gpl20 = TrimExtraneousTrailingText(string(g2))
ccbync20 = TrimExtraneousTrailingText(string(cc20))
classifier, err = New(DefaultConfidenceThreshold)
if err != nil {
log.Fatalf("cannot create license classifier: %v", err)
}
os.Exit(m.Run())
}
func TestClassifier_NearestMatch(t *testing.T) {
tests := []struct {
description string
filename string
extraText string
wantLicense string
wantConfidence float64
}{
{
description: "AGPL 3.0 license",
filename: "AGPL-3.0.txt",
wantLicense: "AGPL-3.0",
wantConfidence: 1.0,
},
{
description: "Apache 2.0 license",
filename: "Apache-2.0.txt",
wantLicense: "Apache-2.0",
wantConfidence: 1.0,
},
{
description: "GPL 2.0 license",
filename: "GPL-2.0.txt",
wantLicense: "GPL-2.0",
wantConfidence: 1.0,
},
{
description: "BSD 3 Clause license with extra text",
filename: "BSD-3-Clause.txt",
extraText: "New BSD License\nCopyright © 1998 Yoyodyne, Inc.\n",
wantLicense: "BSD-3-Clause",
wantConfidence: 0.94,
},
}
classifier.Threshold = DefaultConfidenceThreshold
for _, tt := range tests {
content, err := ReadLicenseFile(tt.filename)
if err != nil {
t.Errorf("error reading contents of %q license: %v", tt.wantLicense, err)
continue
}
m := classifier.NearestMatch(tt.extraText + TrimExtraneousTrailingText(string(content)))
if got, want := m.Name, tt.wantLicense; got != want {
t.Errorf("NearestMatch(%q) = %q, want %q", tt.description, got, want)
}
if got, want := m.Confidence, tt.wantConfidence; got < want {
t.Errorf("NearestMatch(%q) = %v, want %v", tt.description, got, want)
}
}
}
func TestClassifier_MultipleMatch(t *testing.T) {
tests := []struct {
description string
text string
want stringclassifier.Matches
}{
{
description: "Two licenses",
text: "Copyright (c) 2016 Yoyodyne, Inc.\n" + apache20 + strings.Repeat("-", 80) + "\n" + bsd3,
want: stringclassifier.Matches{
{
Name: "Apache-2.0",
Confidence: 1.0,
},
{
Name: "BSD-3-Clause",
Confidence: 1.0,
},
},
},
{
description: "Two licenses: partial match",
text: "Copyright (c) 2016 Yoyodyne, Inc.\n" +
string(apache20[:len(apache20)/2-1]) + string(apache20[len(apache20)/2+7:]) + strings.Repeat("-", 80) + "\n" +
string(bsd3[:len(bsd3)/2]) + "intervening stuff" + string(bsd3[len(bsd3)/2:]),
want: stringclassifier.Matches{
{
Name: "Apache-2.0",
Confidence: 0.99,
},
{
Name: "BSD-3-Clause",
Confidence: 0.98,
},
},
},
{
description: "Two licenses: one forbidden the other okay",
text: "Copyright (c) 2016 Yoyodyne, Inc.\n" + apache20 + strings.Repeat("-", 80) + "\n" + ccbync20,
want: stringclassifier.Matches{
{
Name: "Apache-2.0",
Confidence: 0.99,
},
{
Name: "CC-BY-NC-2.0",
Confidence: 1.0,
},
},
},
{
description: "Two licenses without any space between them.",
text: apache20 + "." + bsd3,
want: stringclassifier.Matches{
{
Name: "Apache-2.0",
Confidence: 1.0,
},
{
Name: "BSD-3-Clause",
Confidence: 1.0,
},
},
},
}
classifier.Threshold = 0.95
defer func() {
classifier.Threshold = DefaultConfidenceThreshold
}()
for _, tt := range tests {
m := classifier.MultipleMatch(tt.text, false)
if len(m) != len(tt.want) {
t.Fatalf("MultipleMatch(%q) number matches: %v, want %v", tt.description, len(m), len(tt.want))
continue
}
for i := 0; i < len(m); i++ {
w := tt.want[i]
if got, want := m[i].Name, w.Name; got != want {
t.Errorf("MultipleMatch(%q) = %q, want %q", tt.description, got, want)
}
if got, want := m[i].Confidence, w.Confidence; got < want {
t.Errorf("MultipleMatch(%q) = %v, want %v", tt.description, got, want)
}
}
}
}
func TestClassifier_MultipleMatch_Headers(t *testing.T) {
tests := []struct {
description string
text string
want stringclassifier.Matches
}{
{
description: "AGPL-3.0 header",
text: "Copyright (c) 2016 Yoyodyne, Inc.\n" + agpl30Header,
want: stringclassifier.Matches{
{
Name: "AGPL-3.0",
Confidence: 1.0,
Offset: 0,
},
},
},
{
description: "Modified LGPL-2.1 header",
text: `Common Widget code.
Copyright (C) 2013-2015 Yoyodyne, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version (but not!).
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
`,
want: stringclassifier.Matches{
{
Name: "LGPL-2.1",
Confidence: 0.97,
Offset: 197,
},
},
},
}
classifier.Threshold = 0.90
defer func() {
classifier.Threshold = DefaultConfidenceThreshold
}()
for _, tt := range tests {
m := classifier.MultipleMatch(tt.text, true)
if len(m) != len(tt.want) {
t.Errorf("MultipleMatch(%q) number matches: %v, want %v", tt.description, len(m), len(tt.want))
continue
}
for i := 0; i < len(m); i++ {
w := tt.want[i]
if got, want := m[i].Name, w.Name; got != want {
t.Errorf("MultipleMatch(%q) = %q, want %q", tt.description, got, want)
}
if got, want := m[i].Confidence, w.Confidence; got < want {
t.Errorf("MultipleMatch(%q) = %v, want %v", tt.description, got, want)
}
}
}
}
func TestClassifier_CopyrightHolder(t *testing.T) {
tests := []struct {
copyright string
want string
}{
{
copyright: "Copyright 2008 Yoyodyne Inc. All Rights Reserved.",
want: "Yoyodyne Inc.",
},
{
copyright: "Copyright 2010-2016 Yoyodyne, Inc.",
want: "Yoyodyne, Inc.",
},
{
copyright: "Copyright 2010, 2011, 2012 Yoyodyne, Inc., All rights reserved.",
want: "Yoyodyne, Inc.",
},
{
copyright: "Copyright (c) 2015 Yoyodyne, Inc. All rights reserved.",
want: "Yoyodyne, Inc.",
},
{
copyright: "Copyright © 1998 by Yoyodyne, Inc., San Narciso, CA, US.",
want: "Yoyodyne, Inc., San Narciso, CA, US",
},
{
copyright: "Copyright (c) 2015 The Algonquin Round Table. All rights reserved.",
want: "The Algonquin Round Table",
},
{
copyright: "Copyright 2016, The Android Open Source Project",
want: "The Android Open Source Project",
},
{
copyright: `---------------------------------------------------------
foo.c:
Copyright 2016, The Android Open Source Project
`,
want: "The Android Open Source Project",
},
}
for _, tt := range tests {
got := CopyrightHolder(tt.copyright)
if got != tt.want {
t.Errorf("CopyrightHolder(%q) = %q, want %q", tt.copyright, got, tt.want)
}
}
}
func TestClassifier_WithinConfidenceThreshold(t *testing.T) {
tests := []struct {
description string
text string
confDef bool
conf99 bool
conf93 bool
conf5 bool
}{
{
description: "Apache 2.0",
text: apache20,
confDef: true,
conf99: true,
conf93: true,
conf5: true,
},
{
description: "GPL 2.0",
text: gpl20,
confDef: true,
conf99: true,
conf93: true,
conf5: true,
},
{
description: "BSD 3 Clause license with extra text",
text: "New BSD License\nCopyright © 1998 Yoyodyne, Inc.\n" + bsd3,
confDef: true,
conf99: true,
conf93: true,
conf5: true,
},
{
description: "Very low confidence",
text: strings.Repeat("Random text is random, but not a license\n", 40),
confDef: false,
conf99: false,
conf93: false,
conf5: true,
},
}
defer func() {
classifier.Threshold = DefaultConfidenceThreshold
}()
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
classifier.Threshold = DefaultConfidenceThreshold
m := classifier.NearestMatch(tt.text)
if got := classifier.WithinConfidenceThreshold(m.Confidence); got != tt.confDef {
t.Errorf("WithinConfidenceThreshold() at %v returned wrong result; got %v, want %v", classifier.Threshold, got, tt.confDef)
}
classifier.Threshold = 0.99
m = classifier.NearestMatch(tt.text)
if got := classifier.WithinConfidenceThreshold(m.Confidence); got != tt.conf99 {
t.Errorf("WithinConfidenceThreshold(%q) = %v, want %v", tt.description, got, tt.conf99)
}
classifier.Threshold = 0.93
m = classifier.NearestMatch(tt.text)
if got := classifier.WithinConfidenceThreshold(m.Confidence); got != tt.conf93 {
t.Errorf("WithinConfidenceThreshold(%q) = %v, want %v", tt.description, got, tt.conf93)
}
classifier.Threshold = 0.05
m = classifier.NearestMatch(tt.text)
if got := classifier.WithinConfidenceThreshold(m.Confidence); got != tt.conf5 {
t.Errorf("WithinConfidenceThreshold(%q) = %v, want %v", tt.description, got, tt.conf5)
}
})
}
}
func TestRemoveIgnorableText(t *testing.T) {
const want = `Lorem ipsum dolor sit amet, pellentesque wisi tortor duis, amet adipiscing bibendum elit aliquam
leo. Mattis commodo sed accumsan at in.
`
tests := []struct {
original string
want string
}{
{"MIT License\n", "\n"},
{"The MIT License\n", "\n"},
{"The MIT License (MIT)\n", "\n"},
{"BSD License\n", "\n"},
{"New BSD License\n", "\n"},
{"COPYRIGHT AND PERMISSION NOTICE\n", "\n"},
{"Copyright (c) 2016, Yoyodyne, Inc.\n", "\n"},
{"All rights reserved.\n", "\n"},
{"Some rights reserved.\n", "\n"},
{"@license\n", "\n"},
// Now with wanted texts.
{
original: `The MIT License
Copyright (c) 2016, Yoyodyne, Inc.
All rights reserved.
` + want,
want: strings.ToLower(want),
},
}
for _, tt := range tests {
if got := removeIgnorableTexts(strings.ToLower(tt.original)); got != tt.want {
t.Errorf("Mismatch(%q) =>\n%s\nwant:\n%s", tt.original, got, tt.want)
}
}
}
func TestRemoveShebangLine(t *testing.T) {
tests := []struct {
original string
want string
}{
{
original: "",
want: "",
},
{
original: "#!/usr/bin/env python -C",
want: "#!/usr/bin/env python -C",
},
{
original: `#!/usr/bin/env python -C
# First line of license text.
# Second line of license text.
`,
want: `# First line of license text.
# Second line of license text.
`,
},
{
original: `# First line of license text.
# Second line of license text.
`,
want: `# First line of license text.
# Second line of license text.
`,
},
}
for _, tt := range tests {
got := removeShebangLine(tt.original)
if got != tt.want {
t.Errorf("RemoveShebangLine(%q) =>\n%s\nwant:\n%s", tt.original, got, tt.want)
}
}
}
func TestRemoveNonWords(t *testing.T) {
tests := []struct {
original string
want string
}{
{
original: `# # Hello
## World
`,
want: ` Hello World `,
},
{
original: ` * This text has a bulleted list:
* * item 1
* * item 2`,
want: ` This text has a bulleted list item 1 item 2`,
},
{
original: `
* This text has a bulleted list:
* * item 1
* * item 2`,
want: ` This text has a bulleted list item 1 item 2`,
},
{
original: `// This text has a bulleted list:
// 1. item 1
// 2. item 2`,
want: ` This text has a bulleted list 1 item 1 2 item 2`,
},
{
original: `// «Copyright (c) 1998 Yoyodyne, Inc.»
// This text has a bulleted list:
// 1. item 1
// 2. item 2
`,
want: ` «Copyright c 1998 Yoyodyne Inc » This text has a bulleted list 1 item 1 2 item 2 `,
},
{
original: `*
* This is the first line we want.
* This is the second line we want.
* This is the third line we want.
* This is the last line we want.
`,
want: ` This is the first line we want This is the second line we want This is the third line we want This is the last line we want `,
},
{
original: `===---------------------------------------------===
***
* This is the first line we want.
* This is the second line we want.
* This is the third line we want.
* This is the last line we want.
***
===---------------------------------------------===
`,
want: ` This is the first line we want This is the second line we want This is the third line we want This is the last line we want `,
},
{
original: strings.Repeat("-", 80),
want: " ",
},
{
original: strings.Repeat("=", 80),
want: " ",
},
{
original: "/*\n",
want: " ",
},
{
original: "/*\n * precursor text\n */\n",
want: " precursor text ",
},
// Test for b/63540492.
{
original: " */\n",
want: " ",
},
{
original: "",
want: "",
},
}
for _, tt := range tests {
if got := stringclassifier.FlattenWhitespace(RemoveNonWords(tt.original)); got != tt.want {
t.Errorf("Mismatch(%q) => %v, want %v", tt.original, got, tt.want)
}
}
}
func TestNormalizePunctuation(t *testing.T) {
tests := []struct {
original string
want string
}{
// Hyphens and dashes.
{"—", "-"},
{"-", "-"},
{"‒", "-"},
{"–", "-"},
{"—", "-"},
// Quotes.
{"'", "'"},
{`"`, "'"},
{"‘", "'"},
{"’", "'"},
{"“", "'"},
{"”", "'"},
{" ” ", " ' "},
// Backtick.
{"`", "'"},
// Copyright mark.
{"©", "(c)"},
// Hyphen-separated words.
{"general- purpose, non- compliant", "general-purpose, non-compliant"},
// Section.
{"§", "(s)"},
{"¤", "(s)"},
}
for _, tt := range tests {
if got := NormalizePunctuation(tt.original); got != tt.want {
t.Errorf("Mismatch => %v, want %v", got, tt.want)
}
}
}
func TestNormalizeEquivalentWords(t *testing.T) {
tests := []struct {
original string
want string
}{
{"acknowledgment", "Acknowledgement"},
{"ANalogue", "Analog"},
{"AnAlyse", "Analyze"},
{"ArtefacT", "Artifact"},
{"authorisation", "Authorization"},
{"AuthoriSed", "Authorized"},
{"CalIbre", "Caliber"},
{"CanCelled", "Canceled"},
{"CapitaliSations", "Capitalizations"},
{"CatalogUe", "Catalog"},
{"CategoriSe", "Categorize"},
{"CentRE", "Center"},
{"EmphasiSed", "Emphasized"},
{"FavoUr", "Favor"},
{"FavoUrite", "Favorite"},
{"FulfiL", "Fulfill"},
{"FulfiLment", "Fulfillment"},
{"InitialiSe", "Initialize"},
{"JudGMent", "Judgement"},
{"LabelLing", "Labeling"},
{"LaboUr", "Labor"},
{"LicenCe", "License"},
{"MaximiSe", "Maximize"},
{"ModelLed", "Modeled"},
{"ModeLling", "Modeling"},
{"OffenCe", "Offense"},
{"OptimiSe", "Optimize"},
{"OrganiSation", "Organization"},
{"OrganiSe", "Organize"},
{"PractiSe", "Practice"},
{"ProgramME", "Program"},
{"RealiSe", "Realize"},
{"RecogniSe", "Recognize"},
{"SignalLing", "Signaling"},
{"sub-license", "Sublicense"},
{"sub license", "Sublicense"},
{"UtiliSation", "Utilization"},
{"WhilST", "While"},
{"WilfuL", "Wilfull"},
{"Non-coMMercial", "Noncommercial"},
{"Per Cent", "Percent"},
}
for _, tt := range tests {
if got := NormalizeEquivalentWords(tt.original); got != tt.want {
t.Errorf("Mismatch => %v, want %v", got, tt.want)
}
}
}
func TestTrimExtraneousTrailingText(t *testing.T) {
tests := []struct {
original string
want string
}{
{
original: `12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it free
software which everyone can redistribute and change under these terms.
`,
want: `12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
END OF TERMS AND CONDITIONS`,
},
}
for _, tt := range tests {
if got := TrimExtraneousTrailingText(tt.original); got != tt.want {
t.Errorf("Mismatch => %q, want %q", got, tt.want)
}
}
}
func TestCommonLicenseWords(t *testing.T) {
files, err := ReadLicenseDir()
if err != nil {
t.Fatalf("error: cannot read licenses directory: %v", err)
}
if files == nil {
t.Fatal("error: cannot get licenses from license directory")
}
for _, file := range files {
if filepath.Ext(file.Name()) != ".txt" {
continue
}
text, err := ReadLicenseFile(file.Name())
if err != nil {
t.Fatalf("error reading contents of %q: %v", file.Name(), err)
}
if got := classifier.hasCommonLicenseWords(string(text)); !got {
t.Errorf("Mismatch(%q) => false, want true", file.Name())
}
}
text := strings.Repeat("Þetta er ekki leyfi.\n", 80)
if got := classifier.hasCommonLicenseWords(text); got {
t.Error("Mismatch => true, want false")
}
}
func TestLicenseMatchQuality(t *testing.T) {
files, err := ReadLicenseDir()
if err != nil {
t.Fatalf("error: cannot read licenses directory: %v", err)
}
classifier.Threshold = 1.0
defer func() {
classifier.Threshold = DefaultConfidenceThreshold
}()
for _, file := range files {
if filepath.Ext(file.Name()) != ".txt" {
continue
}
name := strings.TrimSuffix(file.Name(), ".txt")
contents, err := ReadLicenseFile(file.Name())
if err != nil {
t.Fatalf("error reading contents of %q: %v", file.Name(), err)
}
m := classifier.NearestMatch(TrimExtraneousTrailingText(string(contents)))
if m == nil {
t.Errorf("Couldn't match %q", name)
continue
}
if !classifier.WithinConfidenceThreshold(m.Confidence) {
t.Errorf("ConfidenceMatch(%q) => %v, want %v", name, m.Confidence, 0.99)
}
want := strings.TrimSuffix(name, ".header")
if want != m.Name {
t.Errorf("LicenseMatch(%q) => %v, want %v", name, m.Name, want)
}
}
}
func BenchmarkClassifier(b *testing.B) {
contents := apache20[:len(apache20)/2] + "hello" + apache20[len(apache20)/2:]
b.ResetTimer()
for i := 0; i < b.N; i++ {
classifier, err := New(DefaultConfidenceThreshold)
if err != nil {
b.Errorf("Cannot create classifier: %v", err)
continue
}
classifier.NearestMatch(contents)
}
}
func TestNew(t *testing.T) {
tests := []struct {
desc string
options []OptionFunc
wantArchive func() []byte
wantErr bool
}{
{
desc: "no options, use default",
options: []OptionFunc{},
wantArchive: nil,
},
{
desc: "specify ForbiddenLicenseArchive",
options: []OptionFunc{Archive(ForbiddenLicenseArchive)},
wantArchive: func() []byte {
b, _ := ReadLicenseFile(ForbiddenLicenseArchive)
return b
},
},
{
desc: "file doesn't exist results in error",
options: []OptionFunc{Archive("doesnotexist")},
wantArchive: func() []byte { return nil },
wantErr: true,
},
{
desc: "raw bytes archive",
options: []OptionFunc{ArchiveBytes([]byte("not a gzipped file"))},
wantArchive: func() []byte { return []byte("not a gzipped file") },
wantErr: true,
},
{
desc: "function archive",
options: []OptionFunc{ArchiveFunc(func() ([]byte, error) {
return []byte("not a gzipped file"), nil
})},
wantArchive: func() []byte { return []byte("not a gzipped file") },
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
c, err := New(0.5, tt.options...)
if tt.wantErr != (err != nil) {
t.Fatalf("unexpected error: %v", err)
}
if err == nil {
if tt.wantArchive == nil {
if c.archive != nil {
t.Errorf("wanted default archive, but got specified archive")
}
} else {
got, _ := c.archive()
want := tt.wantArchive()
if !bytes.Equal(got, want) {
t.Errorf("archives did not match; got %d bytes, wanted %d", len(got), len(want))
}
}
}
})
}
}