-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add test2json and UTOF output to bazel-driven go tests #54655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
gh-worker-dd-mergequeue-cf854d
merged 12 commits into
main
from
alopez/bazel-go-test-tweaks
Aug 14, 2026
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
2d500af
Drop flavor concerns from junit test collection
alopezz dda9f27
Find logs too
alopezz 9259d96
Add tool to get json from the logs
alopezz fb5dca7
Create new task for all post processing, add json output to it
alopezz c8a2a19
UTOF support
alopezz afab3ea
Add copyright header to new tool code
alopezz d222638
Upload all artifacts on coverage job
alopezz b0f1265
Update license data
alopezz e161dd0
Force remote download of .log files just in case
alopezz 9a1fda1
Merge branch 'main' into alopez/bazel-go-test-tweaks
alopezz 9ff7fa0
Merge branch 'main' into alopez/bazel-go-test-tweaks
alopezz 1b94cc8
Merge branch 'main' into alopez/bazel-go-test-tweaks
alopezz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| load("@rules_go//go:def.bzl", "go_binary", "go_library", "go_test") | ||
|
|
||
| go_library( | ||
| name = "testlogs_to_json_lib", | ||
| srcs = ["testlogs_to_json.go"], | ||
| importpath = "github.com/DataDog/datadog-agent/bazel/tools/testlogs_to_json", | ||
| visibility = ["//visibility:private"], | ||
| deps = ["@rules_go//go/tools/bzltestutil"], | ||
| ) | ||
|
|
||
| go_binary( | ||
| name = "testlogs_to_json", | ||
| embed = [":testlogs_to_json_lib"], | ||
| visibility = ["//visibility:public"], | ||
| ) | ||
|
|
||
| # gazelle:dd_agent_go_test off | ||
| go_test( | ||
| name = "testlogs_to_json_test", | ||
| srcs = ["testlogs_to_json_test.go"], | ||
| embed = [":testlogs_to_json_lib"], | ||
| gotags = [], # keep | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| // Unless explicitly stated otherwise all files in this repository are licensed | ||
| // under the Apache License Version 2.0. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
| // Copyright 2026-present Datadog, Inc. | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| "bufio" | ||
| "errors" | ||
| "flag" | ||
| "fmt" | ||
| "io" | ||
| "os" | ||
| "strings" | ||
|
|
||
| "github.com/bazelbuild/rules_go/go/tools/bzltestutil" | ||
| ) | ||
|
|
||
| type manifestEntry struct { | ||
| pkg string | ||
| logPath string | ||
| } | ||
|
|
||
| type options struct { | ||
| manifestPath string | ||
| outputPath string | ||
| } | ||
|
|
||
| func main() { | ||
| if err := run(os.Args[1:], os.Stdout, os.Stderr); err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(1) | ||
| } | ||
| } | ||
|
|
||
| func run(args []string, stdout, stderr io.Writer) error { | ||
| opts, err := parseFlags(args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| entries, err := readManifest(opts.manifestPath) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| var out io.Writer = stdout | ||
| var outFile *os.File | ||
| if opts.outputPath != "" && opts.outputPath != "-" { | ||
| outFile, err = os.Create(opts.outputPath) | ||
| if err != nil { | ||
| return fmt.Errorf("create output %q: %w", opts.outputPath, err) | ||
| } | ||
| defer outFile.Close() | ||
| out = outFile | ||
| } | ||
|
|
||
| if err := convert(entries, out); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| fmt.Fprintf(stderr, "Converted %d Bazel test logs to test2json\n", len(entries)) | ||
| return nil | ||
| } | ||
|
|
||
| func parseFlags(args []string) (options, error) { | ||
| var opts options | ||
| fs := flag.NewFlagSet("testlogs_to_json", flag.ContinueOnError) | ||
| fs.SetOutput(io.Discard) | ||
| fs.StringVar(&opts.manifestPath, "manifest", "", "Path to a tab-separated manifest: <go import path>\\t<test.log path>") | ||
| fs.StringVar(&opts.outputPath, "output", "-", "Path to write test2json JSONL output, or '-' for stdout") | ||
| if err := fs.Parse(args); err != nil { | ||
| return opts, err | ||
| } | ||
| if opts.manifestPath == "" { | ||
| return opts, errors.New("missing required -manifest") | ||
| } | ||
| if fs.NArg() != 0 { | ||
| return opts, fmt.Errorf("unexpected positional arguments: %s", strings.Join(fs.Args(), " ")) | ||
| } | ||
| return opts, nil | ||
| } | ||
|
|
||
| func readManifest(path string) ([]manifestEntry, error) { | ||
| f, err := os.Open(path) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("open manifest %q: %w", path, err) | ||
| } | ||
| defer f.Close() | ||
|
|
||
| var entries []manifestEntry | ||
| scanner := bufio.NewScanner(f) | ||
| for scanner.Scan() { | ||
| line := scanner.Text() | ||
| if strings.TrimSpace(line) == "" { | ||
| continue | ||
| } | ||
| pkg, logPath, ok := strings.Cut(line, "\t") | ||
| if !ok || pkg == "" || logPath == "" { | ||
| return nil, fmt.Errorf("invalid manifest line: expected <go import path>\\t<test.log path>, got %q", line) | ||
| } | ||
| if strings.Contains(logPath, "\t") { | ||
| return nil, fmt.Errorf("invalid manifest line: too many tab-separated fields, got %q", line) | ||
| } | ||
| entries = append(entries, manifestEntry{pkg: pkg, logPath: logPath}) | ||
| } | ||
| if err := scanner.Err(); err != nil { | ||
| return nil, fmt.Errorf("read manifest %q: %w", path, err) | ||
| } | ||
| return entries, nil | ||
| } | ||
|
|
||
| func convert(entries []manifestEntry, out io.Writer) error { | ||
| for _, entry := range entries { | ||
| f, err := os.Open(entry.logPath) | ||
| if err != nil { | ||
| return fmt.Errorf("open test log %q for package %s: %w", entry.logPath, entry.pkg, err) | ||
| } | ||
|
|
||
| converter := bzltestutil.NewConverter(out, entry.pkg, bzltestutil.Timestamp) | ||
| _, copyErr := io.Copy(converter, f) | ||
| closeErr := f.Close() | ||
| if copyErr != nil { | ||
| return fmt.Errorf("convert test log %q for package %s: %w", entry.logPath, entry.pkg, copyErr) | ||
| } | ||
| if closeErr != nil { | ||
| return fmt.Errorf("close test log %q for package %s: %w", entry.logPath, entry.pkg, closeErr) | ||
| } | ||
| if err := converter.Close(); err != nil { | ||
| return fmt.Errorf("close converter for test log %q package %s: %w", entry.logPath, entry.pkg, err) | ||
| } | ||
| } | ||
| return nil | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| // Unless explicitly stated otherwise all files in this repository are licensed | ||
| // under the Apache License Version 2.0. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
| // Copyright 2026-present Datadog, Inc. | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "encoding/json" | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestReadManifest(t *testing.T) { | ||
| dir := t.TempDir() | ||
| manifestPath := filepath.Join(dir, "manifest.tsv") | ||
| if err := os.WriteFile(manifestPath, []byte(strings.Join([]string{ | ||
| "", | ||
| "github.com/DataDog/datadog-agent/pkg/foo\t/path/to/foo.log", | ||
| "github.com/DataDog/datadog-agent/pkg/bar\t/path with spaces/bar.log", | ||
| }, "\n")), 0o644); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| entries, err := readManifest(manifestPath) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| want := []manifestEntry{ | ||
| {pkg: "github.com/DataDog/datadog-agent/pkg/foo", logPath: "/path/to/foo.log"}, | ||
| {pkg: "github.com/DataDog/datadog-agent/pkg/bar", logPath: "/path with spaces/bar.log"}, | ||
| } | ||
| if len(entries) != len(want) { | ||
| t.Fatalf("got %d entries, want %d: %#v", len(entries), len(want), entries) | ||
| } | ||
| for i := range want { | ||
| if entries[i] != want[i] { | ||
| t.Fatalf("entry %d = %#v, want %#v", i, entries[i], want[i]) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func TestReadManifestRejectsInvalidLine(t *testing.T) { | ||
| dir := t.TempDir() | ||
| manifestPath := filepath.Join(dir, "manifest.tsv") | ||
| if err := os.WriteFile(manifestPath, []byte("github.com/DataDog/datadog-agent/pkg/foo /path/to/foo.log\n"), 0o644); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| _, err := readManifest(manifestPath) | ||
| if err == nil || !strings.Contains(err.Error(), "invalid manifest line") { | ||
| t.Fatalf("expected invalid manifest error, got %v", err) | ||
| } | ||
| } | ||
|
|
||
| func TestConvertMultipleLogs(t *testing.T) { | ||
| dir := t.TempDir() | ||
| fooLog := filepath.Join(dir, "foo.log") | ||
| barLog := filepath.Join(dir, "bar.log") | ||
| if err := os.WriteFile(fooLog, []byte("=== RUN TestFoo\n--- PASS: TestFoo (0.01s)\nPASS\n"), 0o644); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| if err := os.WriteFile(barLog, []byte("=== RUN TestBar\n bar_test.go:12: boom\n--- FAIL: TestBar (0.02s)\nFAIL\n"), 0o644); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| var out bytes.Buffer | ||
| err := convert([]manifestEntry{ | ||
| {pkg: "github.com/DataDog/datadog-agent/pkg/foo", logPath: fooLog}, | ||
| {pkg: "github.com/DataDog/datadog-agent/pkg/bar", logPath: barLog}, | ||
| }, &out) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| var events []map[string]any | ||
| for _, line := range strings.Split(strings.TrimSpace(out.String()), "\n") { | ||
| var event map[string]any | ||
| if err := json.Unmarshal([]byte(line), &event); err != nil { | ||
| t.Fatalf("invalid JSON line %q: %v", line, err) | ||
| } | ||
| events = append(events, event) | ||
| } | ||
|
|
||
| assertEvent(t, events, "github.com/DataDog/datadog-agent/pkg/foo", "TestFoo", "pass") | ||
| assertEvent(t, events, "github.com/DataDog/datadog-agent/pkg/foo", "", "pass") | ||
| assertEvent(t, events, "github.com/DataDog/datadog-agent/pkg/bar", "TestBar", "fail") | ||
| assertEvent(t, events, "github.com/DataDog/datadog-agent/pkg/bar", "", "fail") | ||
| } | ||
|
|
||
| func assertEvent(t *testing.T, events []map[string]any, pkg, testName, action string) { | ||
| t.Helper() | ||
| for _, event := range events { | ||
| if event["Package"] == pkg && event["Action"] == action { | ||
| if testName == "" { | ||
| if _, ok := event["Test"]; !ok { | ||
| return | ||
| } | ||
| continue | ||
| } | ||
| if event["Test"] == testName { | ||
| return | ||
| } | ||
| } | ||
| } | ||
| t.Fatalf("did not find event package=%q test=%q action=%q in %#v", pkg, testName, action, events) | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This comment was marked as outdated.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.