1// Copyright 2023 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 trace_test 6 7import ( 8 "bytes" 9 "flag" 10 "fmt" 11 "io" 12 "os" 13 "path/filepath" 14 "strings" 15 "testing" 16 17 "internal/trace" 18 "internal/trace/raw" 19 "internal/trace/testtrace" 20 "internal/trace/version" 21) 22 23var ( 24 logEvents = flag.Bool("log-events", false, "whether to log high-level events; significantly slows down tests") 25 dumpTraces = flag.Bool("dump-traces", false, "dump traces even on success") 26) 27 28func TestReaderGolden(t *testing.T) { 29 matches, err := filepath.Glob("./testdata/tests/*.test") 30 if err != nil { 31 t.Fatalf("failed to glob for tests: %v", err) 32 } 33 for _, testPath := range matches { 34 testPath := testPath 35 testName, err := filepath.Rel("./testdata", testPath) 36 if err != nil { 37 t.Fatalf("failed to relativize testdata path: %v", err) 38 } 39 t.Run(testName, func(t *testing.T) { 40 tr, exp, err := testtrace.ParseFile(testPath) 41 if err != nil { 42 t.Fatalf("failed to parse test file at %s: %v", testPath, err) 43 } 44 testReader(t, tr, exp) 45 }) 46 } 47} 48 49func FuzzReader(f *testing.F) { 50 // Currently disabled because the parser doesn't do much validation and most 51 // getters can be made to panic. Turn this on once the parser is meant to 52 // reject invalid traces. 53 const testGetters = false 54 55 f.Fuzz(func(t *testing.T, b []byte) { 56 r, err := trace.NewReader(bytes.NewReader(b)) 57 if err != nil { 58 return 59 } 60 for { 61 ev, err := r.ReadEvent() 62 if err != nil { 63 break 64 } 65 66 if !testGetters { 67 continue 68 } 69 // Make sure getters don't do anything that panics 70 switch ev.Kind() { 71 case trace.EventLabel: 72 ev.Label() 73 case trace.EventLog: 74 ev.Log() 75 case trace.EventMetric: 76 ev.Metric() 77 case trace.EventRangeActive, trace.EventRangeBegin: 78 ev.Range() 79 case trace.EventRangeEnd: 80 ev.Range() 81 ev.RangeAttributes() 82 case trace.EventStateTransition: 83 ev.StateTransition() 84 case trace.EventRegionBegin, trace.EventRegionEnd: 85 ev.Region() 86 case trace.EventTaskBegin, trace.EventTaskEnd: 87 ev.Task() 88 case trace.EventSync: 89 case trace.EventStackSample: 90 case trace.EventBad: 91 } 92 } 93 }) 94} 95 96func testReader(t *testing.T, tr io.Reader, exp *testtrace.Expectation) { 97 r, err := trace.NewReader(tr) 98 if err != nil { 99 if err := exp.Check(err); err != nil { 100 t.Error(err) 101 } 102 return 103 } 104 v := testtrace.NewValidator() 105 for { 106 ev, err := r.ReadEvent() 107 if err == io.EOF { 108 break 109 } 110 if err != nil { 111 if err := exp.Check(err); err != nil { 112 t.Error(err) 113 } 114 return 115 } 116 if *logEvents { 117 t.Log(ev.String()) 118 } 119 if err := v.Event(ev); err != nil { 120 t.Error(err) 121 } 122 } 123 if err := exp.Check(nil); err != nil { 124 t.Error(err) 125 } 126} 127 128func dumpTraceToText(t *testing.T, b []byte) string { 129 t.Helper() 130 131 br, err := raw.NewReader(bytes.NewReader(b)) 132 if err != nil { 133 t.Fatalf("dumping trace: %v", err) 134 } 135 var sb strings.Builder 136 tw, err := raw.NewTextWriter(&sb, version.Current) 137 if err != nil { 138 t.Fatalf("dumping trace: %v", err) 139 } 140 for { 141 ev, err := br.ReadEvent() 142 if err == io.EOF { 143 break 144 } 145 if err != nil { 146 t.Fatalf("dumping trace: %v", err) 147 } 148 if err := tw.WriteEvent(ev); err != nil { 149 t.Fatalf("dumping trace: %v", err) 150 } 151 } 152 return sb.String() 153} 154 155func dumpTraceToFile(t *testing.T, testName string, stress bool, b []byte) string { 156 t.Helper() 157 158 desc := "default" 159 if stress { 160 desc = "stress" 161 } 162 name := fmt.Sprintf("%s.%s.trace.", testName, desc) 163 f, err := os.CreateTemp("", name) 164 if err != nil { 165 t.Fatalf("creating temp file: %v", err) 166 } 167 defer f.Close() 168 if _, err := io.Copy(f, bytes.NewReader(b)); err != nil { 169 t.Fatalf("writing trace dump to %q: %v", f.Name(), err) 170 } 171 return f.Name() 172} 173