github.com/ledgerwatch/erigon-lib@v1.0.0/patricia/patricia_fuzz_test.go (about)

     1  //go:build !nofuzz
     2  
     3  /*
     4  Copyright 2021 Erigon contributors
     5  
     6  Licensed under the Apache License, Version 2.0 (the "License");
     7  you may not use this file except in compliance with the License.
     8  You may obtain a copy of the License at
     9  
    10  	http://www.apache.org/licenses/LICENSE-2.0
    11  
    12  Unless required by applicable law or agreed to in writing, software
    13  distributed under the License is distributed on an "AS IS" BASIS,
    14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  See the License for the specific language governing permissions and
    16  limitations under the License.
    17  */
    18  package patricia
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/binary"
    23  	"fmt"
    24  	"testing"
    25  )
    26  
    27  // go test -trimpath -v -fuzz=FuzzPatricia -fuzztime=10s ./patricia
    28  
    29  func FuzzPatricia(f *testing.F) {
    30  	f.Fuzz(func(t *testing.T, build []byte, test []byte) {
    31  		var n node
    32  		keyMap := make(map[string][]byte)
    33  		i := 0
    34  		for i < len(build) {
    35  			keyLen := int(build[i]>>4) + 1
    36  			valLen := int(build[i]&15) + 1
    37  			i++
    38  			var key []byte
    39  			var val []byte
    40  			for keyLen > 0 && i < len(build) {
    41  				key = append(key, build[i])
    42  				i++
    43  				keyLen--
    44  			}
    45  			for valLen > 0 && i < len(build) {
    46  				val = append(val, build[i])
    47  				i++
    48  				valLen--
    49  			}
    50  			n.insert(key, val)
    51  			keyMap[string(key)] = val
    52  		}
    53  		var testKeys [][]byte
    54  		i = 0
    55  		for i < len(test) {
    56  			keyLen := int(test[i]>>4) + 1
    57  			i++
    58  			var key []byte
    59  			for keyLen > 0 && i < len(test) {
    60  				key = append(key, test[i])
    61  				i++
    62  				keyLen--
    63  			}
    64  			if _, ok := keyMap[string(key)]; !ok {
    65  				testKeys = append(testKeys, key)
    66  			}
    67  		}
    68  		// Test for keys
    69  		for key, vals := range keyMap {
    70  			v, ok := n.get([]byte(key))
    71  			if ok {
    72  				if !bytes.Equal(vals, v.([]byte)) {
    73  					t.Errorf("for key %x expected value %x, got %x", key, vals, v.([]byte))
    74  				}
    75  			}
    76  		}
    77  		// Test for non-existent keys
    78  		for _, key := range testKeys {
    79  			_, ok := n.get(key)
    80  			if ok {
    81  				t.Errorf("unexpected key found [%x]", key)
    82  			}
    83  		}
    84  	})
    85  }
    86  
    87  func FuzzLongestMatch(f *testing.F) {
    88  	f.Fuzz(func(t *testing.T, build []byte, test []byte) {
    89  		var pt PatriciaTree
    90  		keyMap := make(map[string][]byte)
    91  		i := 0
    92  		for i < len(build) {
    93  			keyLen := int(build[i]>>4) + 1
    94  			valLen := int(build[i]&15) + 1
    95  			i++
    96  			var key []byte
    97  			var val []byte
    98  			for keyLen > 0 && i < len(build) {
    99  				key = append(key, build[i])
   100  				i++
   101  				keyLen--
   102  			}
   103  			for valLen > 0 && i < len(build) {
   104  				val = append(val, build[i])
   105  				i++
   106  				valLen--
   107  			}
   108  			pt.Insert(key, val)
   109  			keyMap[string(key)] = val
   110  		}
   111  		var keys []string
   112  		for key := range keyMap {
   113  			keys = append(keys, key)
   114  		}
   115  		if len(keys) == 0 {
   116  			t.Skip()
   117  		}
   118  		var data []byte
   119  		for i := 0; i < 4*(len(test)/4); i += 4 {
   120  			keyIdx := int(binary.BigEndian.Uint32(test[i : i+4]))
   121  			keyIdx = keyIdx % len(keys)
   122  			key := []byte(keys[keyIdx])
   123  			data = append(data, key...)
   124  			for j := 0; j < len(key); j++ {
   125  				data = append(data, key[len(key)-1-j])
   126  			}
   127  		}
   128  		mf := NewMatchFinder(&pt)
   129  		m1 := mf.FindLongestMatches(data)
   130  		mf2 := NewMatchFinder2(&pt)
   131  		m2 := mf2.FindLongestMatches(data)
   132  		if len(m1) == len(m2) {
   133  			for i, m := range m1 {
   134  				mm := m2[i]
   135  				if m.Start != mm.Start || m.End != mm.End {
   136  					t.Errorf("mismatch, expected %+v, got %+v", m, mm)
   137  				}
   138  			}
   139  		} else {
   140  			t.Errorf("matches %d, expected %d", len(m2), len(m1))
   141  			for _, m := range m1 {
   142  				fmt.Printf("%+v, match1: [%x]\n", m, data[m.Start:m.End])
   143  			}
   144  			for _, m := range m2 {
   145  				fmt.Printf("%+v, match2: [%x]\n", m, data[m.Start:m.End])
   146  			}
   147  		}
   148  	})
   149  }