GoPLS Viewer

Home|gopls/go/callgraph/vta/internal/trie/op_test.go
1// Copyright 2021 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package trie_test
6
7import (
8    "fmt"
9    "math/rand"
10    "reflect"
11    "testing"
12    "time"
13
14    "golang.org/x/tools/go/callgraph/vta/internal/trie"
15)
16
17// This file tests trie.Map by cross checking operations on a collection of
18// trie.Map's against a collection of map[uint64]interface{}. This includes
19// both limited fuzz testing for correctness and benchmarking.
20
21// mapCollection is effectively a []map[uint64]interface{}.
22// These support operations being applied to the i'th maps.
23type mapCollection interface {
24    Elements() []map[uint64]interface{}
25
26    DeepEqual(lr intbool
27    Lookup(id intk uint64) (interface{}, bool)
28
29    Insert(id intk uint64v interface{})
30    Update(id intk uint64v interface{})
31    Remove(id intk uint64)
32    Intersect(l intr int)
33    Merge(l intr int)
34    Clear(id int)
35
36    Average(l intr int)
37    Assign(l intr int)
38}
39
40// opCode of an operation.
41type opCode int
42
43const (
44    deepEqualsOp opCode = iota
45    lookupOp
46    insert
47    update
48    remove
49    merge
50    intersect
51    clear
52    takeAverage
53    assign
54)
55
56func (op opCodeString() string {
57    switch op {
58    case deepEqualsOp:
59        return "DE"
60    case lookupOp:
61        return "LO"
62    case insert:
63        return "IN"
64    case update:
65        return "UP"
66    case remove:
67        return "RE"
68    case merge:
69        return "ME"
70    case intersect:
71        return "IT"
72    case clear:
73        return "CL"
74    case takeAverage:
75        return "AV"
76    case assign:
77        return "AS"
78    default:
79        return "??"
80    }
81}
82
83// A mapCollection backed by MutMaps.
84type trieCollection struct {
85    b     *trie.Builder
86    tries []trie.MutMap
87}
88
89func (c *trieCollectionElements() []map[uint64]interface{} {
90    var maps []map[uint64]interface{}
91    for _m := range c.tries {
92        maps = append(mapstrie.Elems(m.M))
93    }
94    return maps
95}
96func (c *trieCollectionEq(id intm map[uint64]interface{}) bool {
97    elems := trie.Elems(c.tries[id].M)
98    return !reflect.DeepEqual(elemsm)
99}
100
101func (c *trieCollectionLookup(id intk uint64) (interface{}, bool) {
102    return c.tries[id].M.Lookup(k)
103}
104func (c *trieCollectionDeepEqual(lr intbool {
105    return c.tries[l].M.DeepEqual(c.tries[r].M)
106}
107
108func (c *trieCollectionAdd() {
109    c.tries = append(c.triesc.b.MutEmpty())
110}
111
112func (c *trieCollectionInsert(id intk uint64v interface{}) {
113    c.tries[id].Insert(kv)
114}
115
116func (c *trieCollectionUpdate(id intk uint64v interface{}) {
117    c.tries[id].Update(kv)
118}
119
120func (c *trieCollectionRemove(id intk uint64) {
121    c.tries[id].Remove(k)
122}
123
124func (c *trieCollectionIntersect(l intr int) {
125    c.tries[l].Intersect(c.tries[r].M)
126}
127
128func (c *trieCollectionMerge(l intr int) {
129    c.tries[l].Merge(c.tries[r].M)
130}
131
132func (c *trieCollectionAverage(l intr int) {
133    c.tries[l].MergeWith(averagec.tries[r].M)
134}
135
136func (c *trieCollectionClear(id int) {
137    c.tries[id] = c.b.MutEmpty()
138}
139func (c *trieCollectionAssign(lr int) {
140    c.tries[l] = c.tries[r]
141}
142
143func average(x interface{}, y interface{}) interface{} {
144    if xok := x.(float32); ok {
145        if yok := y.(float32); ok {
146            return (x + y) / 2.0
147        }
148    }
149    return x
150}
151
152type builtinCollection []map[uint64]interface{}
153
154func (c builtinCollectionElements() []map[uint64]interface{} {
155    return c
156}
157
158func (c builtinCollectionLookup(id intk uint64) (interface{}, bool) {
159    vok := c[id][k]
160    return vok
161}
162func (c builtinCollectionDeepEqual(lr intbool {
163    return reflect.DeepEqual(c[l], c[r])
164}
165
166func (c builtinCollectionInsert(id intk uint64v interface{}) {
167    if _ok := c[id][k]; !ok {
168        c[id][k] = v
169    }
170}
171
172func (c builtinCollectionUpdate(id intk uint64v interface{}) {
173    c[id][k] = v
174}
175
176func (c builtinCollectionRemove(id intk uint64) {
177    delete(c[id], k)
178}
179
180func (c builtinCollectionIntersect(l intr int) {
181    result := map[uint64]interface{}{}
182    for kv := range c[l] {
183        if _ok := c[r][k]; ok {
184            result[k] = v
185        }
186    }
187    c[l] = result
188}
189
190func (c builtinCollectionMerge(l intr int) {
191    result := map[uint64]interface{}{}
192    for kv := range c[r] {
193        result[k] = v
194    }
195    for kv := range c[l] {
196        result[k] = v
197    }
198    c[l] = result
199}
200
201func (c builtinCollectionAverage(l intr int) {
202    avg := map[uint64]interface{}{}
203    for klv := range c[l] {
204        if rvok := c[r][k]; ok {
205            avg[k] = average(lvrv)
206        } else {
207            avg[k] = lv // add elements just in l
208        }
209    }
210    for krv := range c[r] {
211        if _ok := c[l][k]; !ok {
212            avg[k] = rv // add elements just in r
213        }
214    }
215    c[l] = avg
216}
217
218func (c builtinCollectionAssign(lr int) {
219    m := map[uint64]interface{}{}
220    for kv := range c[r] {
221        m[k] = v
222    }
223    c[l] = m
224}
225
226func (c builtinCollectionClear(id int) {
227    c[id] = map[uint64]interface{}{}
228}
229
230func newTriesCollection(size int) *trieCollection {
231    tc := &trieCollection{
232        b:     trie.NewBuilder(),
233        triesmake([]trie.MutMapsize),
234    }
235    for i := 0i < sizei++ {
236        tc.tries[i] = tc.b.MutEmpty()
237    }
238    return tc
239}
240
241func newMapsCollection(size int) *builtinCollection {
242    maps := make(builtinCollectionsize)
243    for i := 0i < sizei++ {
244        maps[i] = map[uint64]interface{}{}
245    }
246    return &maps
247}
248
249// operation on a map collection.
250type operation struct {
251    code opCode
252    lr int
253    k    uint64
254    v    float32
255}
256
257// Apply the operation to maps.
258func (op operationApply(maps mapCollection) interface{} {
259    type lookupresult struct {
260        v  interface{}
261        ok bool
262    }
263    switch op.code {
264    case deepEqualsOp:
265        return maps.DeepEqual(op.lop.r)
266    case lookupOp:
267        vok := maps.Lookup(op.lop.k)
268        return lookupresult{vok}
269    case insert:
270        maps.Insert(op.lop.kop.v)
271    case update:
272        maps.Update(op.lop.kop.v)
273    case remove:
274        maps.Remove(op.lop.k)
275    case merge:
276        maps.Merge(op.lop.r)
277    case intersect:
278        maps.Intersect(op.lop.r)
279    case clear:
280        maps.Clear(op.l)
281    case takeAverage:
282        maps.Average(op.lop.r)
283    case assign:
284        maps.Assign(op.lop.r)
285    }
286    return nil
287}
288
289// Returns a collection of op codes with dist[op] copies of op.
290func distribution(dist map[opCode]int) []opCode {
291    var codes []opCode
292    for opn := range dist {
293        for i := 0i < ni++ {
294            codes = append(codesop)
295        }
296    }
297    return codes
298}
299
300// options for generating a random operation.
301type options struct {
302    maps   int
303    maxKey uint64
304    maxVal int
305    codes  []opCode
306}
307
308// returns a random operation using r as a source of randomness.
309func randOperator(r *rand.Randopts optionsoperation {
310    id := func() int { return r.Intn(opts.maps) }
311    key := func() uint64 { return r.Uint64() % opts.maxKey }
312    val := func() float32 { return float32(r.Intn(opts.maxVal)) }
313    switch code := opts.codes[r.Intn(len(opts.codes))]; code {
314    case lookupOpremove:
315        return operation{codecodelid(), kkey()}
316    case insertupdate:
317        return operation{codecodelid(), kkey(), vval()}
318    case deepEqualsOpmergeintersecttakeAverageassign:
319        return operation{codecodelid(), rid()}
320    case clear:
321        return operation{codecodelid()}
322    default:
323        panic("Invalid op code")
324    }
325}
326
327func randOperators(r *rand.Randnumops intopts options) []operation {
328    ops := make([]operationnumops)
329    for i := 0i < numopsi++ {
330        ops[i] = randOperator(ropts)
331    }
332    return ops
333}
334
335// TestOperations applies a series of random operations to collection of
336// trie.MutMaps and map[uint64]interface{}. It tests for the maps being equal.
337func TestOperations(t *testing.T) {
338    seed := time.Now().UnixNano()
339    s := rand.NewSource(seed)
340    r := rand.New(s)
341    t.Log("seed: "seed)
342
343    size := 10
344    N := 100000
345    ops := randOperators(rNoptions{
346        maps:   size,
347        maxKey128,
348        maxVal100,
349        codesdistribution(map[opCode]int{
350            deepEqualsOp1,
351            lookupOp:     10,
352            insert:       10,
353            update:       10,
354            remove:       10,
355            merge:        10,
356            intersect:    10,
357            clear:        2,
358            takeAverage:  5,
359            assign:       5,
360        }),
361    })
362
363    var tries mapCollection = newTriesCollection(size)
364    var maps mapCollection = newMapsCollection(size)
365    check := func() error {
366        if gotwant := tries.Elements(), maps.Elements(); !reflect.DeepEqual(gotwant) {
367            return fmt.Errorf("elements of tries and maps and tries differed. got %v want %v"gotwant)
368        }
369        return nil
370    }
371
372    for iop := range ops {
373        gotwant := op.Apply(tries), op.Apply(maps)
374        if got != want {
375            t.Errorf("op[%d]: (%v).Apply(%v) != (%v).Apply(%v). got %v want %v",
376                ioptriesopmapsgotwant)
377        }
378    }
379    if err := check(); err != nil {
380        t.Errorf("%d operators failed with %s"sizeerr)
381        t.Log("Rerunning with more checking")
382        triesmaps = newTriesCollection(size), newMapsCollection(size)
383        for iop := range ops {
384            op.Apply(tries)
385            op.Apply(maps)
386            if err := check(); err != nil {
387                t.Fatalf("Failed first on op[%d]=%v: %v"ioperr)
388            }
389        }
390    }
391}
392
393func run(b *testing.Bopts optionsseed int64mk func(intmapCollection) {
394    r := rand.New(rand.NewSource(seed))
395    ops := randOperators(rb.Nopts)
396    maps := mk(opts.maps)
397    for _op := range ops {
398        op.Apply(maps)
399    }
400}
401
402var standard options = options{
403    maps:   10,
404    maxKey128,
405    maxVal100,
406    codesdistribution(map[opCode]int{
407        deepEqualsOp1,
408        lookupOp:     20,
409        insert:       20,
410        update:       20,
411        remove:       20,
412        merge:        10,
413        intersect:    10,
414        clear:        1,
415        takeAverage:  5,
416        assign:       20,
417    }),
418}
419
420func BenchmarkTrieStandard(b *testing.B) {
421    run(bstandard123, func(size intmapCollection {
422        return newTriesCollection(size)
423    })
424}
425
426func BenchmarkMapsStandard(b *testing.B) {
427    run(bstandard123, func(size intmapCollection {
428        return newMapsCollection(size)
429    })
430}
431
432var smallWide options = options{
433    maps:   100,
434    maxKey100,
435    maxVal8,
436    codesdistribution(map[opCode]int{
437        deepEqualsOp0,
438        lookupOp:     0,
439        insert:       30,
440        update:       20,
441        remove:       0,
442        merge:        10,
443        intersect:    0,
444        clear:        1,
445        takeAverage:  0,
446        assign:       30,
447    }),
448}
449
450func BenchmarkTrieSmallWide(b *testing.B) {
451    run(bsmallWide456, func(size intmapCollection {
452        return newTriesCollection(size)
453    })
454}
455
456func BenchmarkMapsSmallWide(b *testing.B) {
457    run(bsmallWide456, func(size intmapCollection {
458        return newMapsCollection(size)
459    })
460}
461
MembersX
trieCollection.Update.c
trieCollection.Clear
builtinCollection.Lookup
builtinCollection.Lookup.k
builtinCollection.Clear
options.maxVal
TestOperations
smallWide
BenchmarkTrieSmallWide.b
time
trieCollection.Lookup.c
trieCollection.Average.c
builtinCollection.Intersect.RangeStmt_3802.v
builtinCollection.Merge.c
builtinCollection.Assign.RangeStmt_4514.v
randOperator
TestOperations.BlockStmt.RangeStmt_8466.i
trieCollection.Merge.c
builtinCollection
builtinCollection.Average.avg
builtinCollection.Average.r
distribution.RangeStmt_5948.n
TestOperations.BlockStmt.RangeStmt_8466.op
run.opts
trieCollection.Insert.v
average.y
builtinCollection.Insert.c
builtinCollection.Insert.id
builtinCollection.Merge.result
operation.Apply.op
operation.Apply.maps
operation.Apply.lookupresult.v
TestOperations.maps
run.seed
BenchmarkMapsSmallWide.b
reflect
trieCollection.DeepEqual
builtinCollection.Insert.v
operation.Apply.lookupresult.ok
TestOperations.t
run.ops
trieCollection.Intersect.c
builtinCollection.Insert
builtinCollection.Update.k
builtinCollection.Merge.r
builtinCollection.Average.RangeStmt_4175.k
fmt
trieCollection.Insert
trieCollection.Intersect
builtinCollection.Intersect.result
newMapsCollection.size
newMapsCollection.i
randOperator.r
TestOperations.RangeStmt_8063.i
trieCollection.Elements.RangeStmt_1738.m
trieCollection.Update.id
builtinCollection.DeepEqual.c
builtinCollection.Update.c
builtinCollection.Intersect.c
builtinCollection.Merge.l
builtinCollection.Average.RangeStmt_4318.rv
randOperator.opts
BenchmarkTrieSmallWide
trieCollection.Remove.id
trieCollection.Remove.k
trieCollection.Merge
builtinCollection.Clear.id
newMapsCollection
operation.r
builtinCollection.Update
BenchmarkTrieStandard.b
builtinCollection.DeepEqual.r
builtinCollection.Intersect
distribution.dist
trieCollection.DeepEqual.c
trieCollection.Insert.id
operation.k
distribution.RangeStmt_5948.op
TestOperations.r
TestOperations.RangeStmt_8063.BlockStmt.want
opCode
trieCollection.Eq
average.x
builtinCollection.Remove.c
builtinCollection.Assign.m
newMapsCollection.maps
randOperators.opts
BenchmarkMapsStandard.b
trieCollection.Add.c
builtinCollection.Merge
builtinCollection.Assign.c
operation.l
operation.Apply.BlockStmt.ok
distribution.RangeStmt_5948.BlockStmt.i
randOperators.i
TestOperations.s
builtinCollection.Average
distribution.codes
TestOperations.BlockStmt.want
deepEqualsOp
trieCollection.tries
trieCollection.Intersect.r
trieCollection.Assign.c
newTriesCollection.i
randOperators
TestOperations.RangeStmt_8063.op
trieCollection.Insert.k
builtinCollection.Lookup.c
builtinCollection.Update.v
builtinCollection.Average.RangeStmt_4318.k
TestOperations.BlockStmt.RangeStmt_8466.BlockStmt.err
run
trie
trieCollection.Insert.c
trieCollection.Clear.c
builtinCollection.Elements.c
builtinCollection.DeepEqual
randOperators.r
TestOperations.tries
run.mk
BenchmarkTrieStandard
mapCollection
opCode.String.op
trieCollection.Assign
builtinCollection.DeepEqual.l
builtinCollection.Update.id
builtinCollection.Remove
TestOperations.ops
trieCollection.b
trieCollection.DeepEqual.r
trieCollection.Remove.c
operation.v
TestOperations.size
opCode.String
trieCollection.Lookup
trieCollection.Lookup.k
trieCollection.Add
trieCollection.Remove
trieCollection.Assign.l
newTriesCollection
operation.Apply
options
TestOperations.N
trieCollection.Merge.r
average
builtinCollection.Lookup.id
trieCollection.Elements.c
trieCollection.Elements.maps
trieCollection.DeepEqual.l
builtinCollection.Insert.k
builtinCollection.Remove.k
builtinCollection.Intersect.l
builtinCollection.Intersect.RangeStmt_3802.k
builtinCollection.Clear.c
run.RangeStmt_8812.op
trieCollection.Eq.m
trieCollection.Assign.r
randOperators.ops
run.r
run.maps
standard
trieCollection.Update.k
trieCollection.Average.r
builtinCollection.Merge.RangeStmt_4028.v
newTriesCollection.tc
distribution
options.maps
randOperators.numops
trieCollection.Update.v
trieCollection.Intersect.l
trieCollection.Average
builtinCollection.Elements
builtinCollection.Merge.RangeStmt_3983.k
builtinCollection.Merge.RangeStmt_4028.k
builtinCollection.Average.l
builtinCollection.Assign.r
options.maxKey
trieCollection.Eq.c
trieCollection.Lookup.id
trieCollection.Update
trieCollection.Average.l
trieCollection.Clear.id
builtinCollection.Average.c
TestOperations.err
BenchmarkMapsStandard
builtinCollection.Assign.RangeStmt_4514.k
newTriesCollection.size
operation.Apply.lookupresult
TestOperations.RangeStmt_8063.BlockStmt.got
run.b
trieCollection
trieCollection.Eq.id
trieCollection.Merge.l
builtinCollection.Remove.id
builtinCollection.Assign
builtinCollection.Assign.l
options.codes
BenchmarkMapsSmallWide
trieCollection.Elements
builtinCollection.Intersect.r
builtinCollection.Merge.RangeStmt_3983.v
operation.code
TestOperations.BlockStmt.got
trieCollection.Eq.elems
builtinCollection.Average.RangeStmt_4175.lv
operation
operation.Apply.BlockStmt.v
TestOperations.seed
Members
X