Source file src/internal/trace/reader_test.go

     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  
     5  package trace_test
     6  
     7  import (
     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  
    23  var (
    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  
    28  func 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  
    49  func 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  
    96  func 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  
   128  func 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  
   155  func 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  

View as plain text