kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/platform/kcd/kcd_test.go (about)

     1  /*
     2   * Copyright 2016 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package kcd
    18  
    19  import (
    20  	"regexp"
    21  	"testing"
    22  	"time"
    23  )
    24  
    25  func TestHexDigest(t *testing.T) {
    26  	// SHA256 test vectors from http://www.nsrl.nist.gov/testdata/
    27  	// We're using these just to make sure we actually get SHA256.
    28  	tests := map[string]string{
    29  		"":    "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    30  		"abc": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
    31  		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq": "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1",
    32  	}
    33  	for input, want := range tests {
    34  		if got := HexDigest([]byte(input)); got != want {
    35  			t.Errorf("HexDigest(%q): got %q, want %q", input, got, want)
    36  		}
    37  	}
    38  }
    39  
    40  func regexps(exprs ...string) (res []*regexp.Regexp) {
    41  	for _, expr := range exprs {
    42  		res = append(res, regexp.MustCompile(expr))
    43  	}
    44  	return
    45  }
    46  
    47  func TestIsEmpty(t *testing.T) {
    48  	tests := []struct {
    49  		input *FindFilter
    50  		want  bool
    51  	}{
    52  		{nil, true},
    53  		{new(FindFilter), true},
    54  		{&FindFilter{Revisions: []string{}}, true},
    55  		{&FindFilter{Languages: []string{""}}, false},
    56  		{&FindFilter{Sources: regexps("foo")}, false},
    57  		{&FindFilter{Outputs: regexps("foo")}, false},
    58  		{&FindFilter{BuildCorpus: []string{"google3", "foo", "bar"}}, false},
    59  	}
    60  	for _, test := range tests {
    61  		if got := test.input.IsEmpty(); got != test.want {
    62  			t.Errorf("%v.IsEmpty(): got %v, want %v", test.input, got, test.want)
    63  		}
    64  	}
    65  }
    66  
    67  func TestMatcher(t *testing.T) {
    68  	// The matcher function is an internal detail, but it provides the kernel
    69  	// of functionality for several other pieces of the package so testing this
    70  	// is a convenient way to cover the most important bits.
    71  	tests := []struct {
    72  		exprs  []string
    73  		inputs []string
    74  		quote  func(string) string
    75  		want   bool
    76  	}{
    77  		// Fallback on empty exprs should work, with or without inputs.
    78  		{want: true},
    79  		{inputs: []string{"xyz"}, want: true},
    80  		{exprs: []string{}, want: true},
    81  		{exprs: []string{}, inputs: []string{"pdq"}, want: true},
    82  
    83  		{ // No values at all; no match.
    84  			exprs: []string{"a", "b", "c"}},
    85  
    86  		{ // Some values, but no match.
    87  			exprs:  []string{"needle"},
    88  			inputs: []string{"hay", "stack"}},
    89  
    90  		{ // Some values and a match.
    91  			exprs:  []string{"needle"},
    92  			inputs: []string{"a", "needle", "is", "here"},
    93  			want:   true},
    94  
    95  		{ // Make sure quoting works.
    96  			exprs:  []string{"foo"},
    97  			inputs: []string{"xfoox"},
    98  			quote:  func(s string) string { return "x" + s + "x" },
    99  			want:   true},
   100  
   101  		{ // Make sure quoting works, negative case.
   102  			exprs:  []string{"foo"},
   103  			inputs: []string{"foo"},
   104  			quote:  func(s string) string { return "bollocks" }},
   105  
   106  		{ // Another negative case for quoting.
   107  			exprs:  []string{"f.*g"},
   108  			inputs: []string{"foooog"},
   109  			quote:  regexp.QuoteMeta},
   110  
   111  		{ // Matching with regexp operators.
   112  			exprs:  []string{"w[aeiou]{2,}"},
   113  			inputs: []string{"waaaaooooouuuu"},
   114  			want:   true},
   115  	}
   116  
   117  	for _, test := range tests {
   118  		m, err := matcher(test.exprs, test.quote)
   119  		if err != nil {
   120  			t.Errorf("Matcher for %+v: unexpected error: %v [broken test]", test, err)
   121  			continue
   122  		}
   123  		if got := m(test.inputs...); got != test.want {
   124  			t.Errorf("Match failed: got %v, want %v", got, test.want)
   125  			t.Logf("Exprs: %+q\nInput: %+q", test.exprs, test.inputs)
   126  		}
   127  	}
   128  }
   129  
   130  func TestCombineRegexps(t *testing.T) {
   131  	res := []*regexp.Regexp{
   132  		regexp.MustCompile(`^(foo|bar)$`),
   133  		regexp.MustCompile(`.*xxx.*`),
   134  	}
   135  	match, err := combineRegexps(res)
   136  	if err != nil {
   137  		t.Fatalf("combineRegexps %+v: unexpected error: %v", res, err)
   138  	}
   139  	for _, yes := range []string{"foo", "bar", "xxx", "axxxb", "fooxxxbar"} {
   140  		if !match(yes) {
   141  			t.Errorf("match(%q): got false, want true", yes)
   142  		}
   143  	}
   144  	for _, no := range []string{"food", "barf", "xyxx", "xxfooxx"} {
   145  		if match(no) {
   146  			t.Errorf("match(%q): got true, want false", no)
   147  		}
   148  	}
   149  }
   150  
   151  func TestRevMatch(t *testing.T) {
   152  	tests := []struct {
   153  		// Pieces of the filter
   154  		filterRev, filterCorpus string
   155  		until                   time.Time
   156  
   157  		// Input to test against the filter
   158  		inputRev, inputCorpus string
   159  		timestamp             time.Time
   160  
   161  		// Should it match?
   162  		want bool
   163  	}{
   164  		// An empty filter matches everything.
   165  		{want: true},
   166  		{inputRev: "xyzzy", want: true},
   167  		{inputRev: "plugh", inputCorpus: "deedle", want: true},
   168  		{inputCorpus: "kitteh!", want: true},
   169  		{inputRev: "zardoz", timestamp: time.Unix(1940, 204), want: true},
   170  
   171  		// Exact match on the revision string.
   172  		{filterRev: "xyz pdq", inputRev: "xyz pdq", want: true},
   173  		{filterRev: "保護者", inputRev: "watcher", want: false},
   174  
   175  		// Regexp match on the revision string.
   176  		{filterRev: `1.*`, inputRev: "12345", inputCorpus: "alpha", want: true},
   177  		{filterRev: `\d+`, inputRev: "12345", inputCorpus: "bravo", want: true},
   178  		{filterRev: `(?i)[aeiou]lf`, inputRev: "Elf", want: true},
   179  
   180  		// Exact match on the corpus string.
   181  		{filterCorpus: "alpha", inputRev: "12345", inputCorpus: "alpha", want: true},
   182  		{filterCorpus: "alpha", inputRev: "12345", inputCorpus: "bravo", want: false},
   183  		{filterRev: `\d+`, filterCorpus: "alpha", inputRev: "12345", inputCorpus: "alpha", want: true},
   184  		{filterRev: `1{5}`, filterCorpus: "alpha", inputRev: "12345", want: false},
   185  
   186  		// Corpus doesn't accept regexp matches.
   187  		{filterCorpus: `p(ie|ea)ces?`, inputRev: "q", inputCorpus: "pieces", want: false},
   188  
   189  		// Timestamp constraints.
   190  		{until: time.Unix(25, 0), inputRev: "boozle", timestamp: time.Unix(25, 1), want: false},
   191  		{until: time.Unix(25, 0), inputRev: "beezle", timestamp: time.Unix(24, 0), want: true},
   192  	}
   193  
   194  	for _, test := range tests {
   195  		filter := &RevisionsFilter{
   196  			Revision: test.filterRev,
   197  			Corpus:   test.filterCorpus,
   198  			Until:    test.until,
   199  		}
   200  		matches, err := filter.Compile()
   201  		if err != nil {
   202  			t.Errorf("Compile %+v failed: %v", filter, err)
   203  			continue
   204  		}
   205  		rev := Revision{test.inputRev, test.inputCorpus, test.timestamp}
   206  		if got := matches(rev); got != test.want {
   207  			t.Errorf("Match failed: got %v, want %v\nFilter: %+v\nQuery:  %+v", got, test.want,
   208  				filter, rev)
   209  		}
   210  	}
   211  }
   212  
   213  func TestIsValidDigest(t *testing.T) {
   214  	tests := []struct {
   215  		input string
   216  		want  bool
   217  	}{
   218  		// String too short.
   219  		{"", false},
   220  		{"e3b0c4429", false},
   221  		{"E3B0C4429", false},
   222  		{"x y z", false},
   223  		{"<dir>", false},
   224  
   225  		// Invalid characters in the digest.
   226  		{"e3b-c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", false},
   227  		{"e3b0c?4298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85+", false},
   228  		{"all your base are belong to us you have no chance to survive ha!", false},
   229  
   230  		// Correct format, correct case.
   231  		{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", true},
   232  		{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true},
   233  		{"0000000000000000000000000000000000000000000000000000000000000000", true},
   234  
   235  		// Correct format, but case matters.
   236  		{"E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", false},
   237  	}
   238  	for _, test := range tests {
   239  		got := IsValidDigest(test.input)
   240  		if got != test.want {
   241  			t.Errorf("IsValidDigest(%q): got %v, want %v", test.input, got, test.want)
   242  		}
   243  	}
   244  }