1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
// 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 2024 Datadog, Inc.
package integrations
import (
"context"
"fmt"
"runtime"
"strings"
"time"
"ci-visibility-test-github/main/civisibility/constants"
"ci-visibility-test-github/main/civisibility/utils"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)
// Test
// Ensures that tslvTest implements the DdTest interface.
var _ DdTest = (*tslvTest)(nil)
// tslvTest implements the DdTest interface and represents an individual test within a suite.
type tslvTest struct {
ciVisibilityCommon
suite *tslvTestSuite
name string
}
// createTest initializes a new test within a given suite.
func createTest(suite *tslvTestSuite, name string, startTime time.Time) DdTest {
if suite == nil {
return nil
}
operationName := "test"
if suite.module.framework != "" {
operationName = fmt.Sprintf("%s.%s", strings.ToLower(suite.module.framework), operationName)
}
resourceName := fmt.Sprintf("%s.%s", suite.name, name)
// Test tags should include suite, module, and session tags so the backend can calculate the suite, module, and session fingerprint from the test.
testTags := append(suite.tags, tracer.Tag(constants.TestName, name))
testOpts := append(fillCommonTags([]tracer.StartSpanOption{
tracer.ResourceName(resourceName),
tracer.SpanType(constants.SpanTypeTest),
tracer.StartTime(startTime),
}), testTags...)
span, ctx := tracer.StartSpanFromContext(context.Background(), operationName, testOpts...)
if suite.module.session != nil {
span.SetTag(constants.TestSessionIDTag, fmt.Sprint(suite.module.session.sessionID))
}
span.SetTag(constants.TestModuleIDTag, fmt.Sprint(suite.module.moduleID))
span.SetTag(constants.TestSuiteIDTag, fmt.Sprint(suite.suiteID))
t := &tslvTest{
suite: suite,
name: name,
ciVisibilityCommon: ciVisibilityCommon{
startTime: startTime,
tags: testTags,
span: span,
ctx: ctx,
},
}
// Ensure to close everything before CI visibility exits. In CI visibility mode, we try to never lose data.
PushCiVisibilityCloseAction(func() { t.Close(ResultStatusFail) })
return t
}
// Name returns the name of the test.
func (t *tslvTest) Name() string { return t.name }
// Suite returns the suite to which the test belongs.
func (t *tslvTest) Suite() DdTestSuite { return t.suite }
// Close closes the test with the given status and sets the finish time to the current time.
func (t *tslvTest) Close(status TestResultStatus) { t.CloseWithFinishTime(status, time.Now()) }
// CloseWithFinishTime closes the test with the given status and finish time.
func (t *tslvTest) CloseWithFinishTime(status TestResultStatus, finishTime time.Time) {
t.CloseWithFinishTimeAndSkipReason(status, finishTime, "")
}
// CloseWithFinishTimeAndSkipReason closes the test with the given status, finish time, and skip reason.
func (t *tslvTest) CloseWithFinishTimeAndSkipReason(status TestResultStatus, finishTime time.Time, skipReason string) {
t.mutex.Lock()
defer t.mutex.Unlock()
if t.closed {
return
}
switch status {
case ResultStatusPass:
t.span.SetTag(constants.TestStatus, constants.TestStatusPass)
case ResultStatusFail:
t.span.SetTag(constants.TestStatus, constants.TestStatusFail)
case ResultStatusSkip:
t.span.SetTag(constants.TestStatus, constants.TestStatusSkip)
}
if skipReason != "" {
t.span.SetTag(constants.TestSkipReason, skipReason)
}
t.span.Finish(tracer.FinishTime(finishTime))
t.closed = true
}
// SetError sets an error on the test and marks the suite and module as having an error.
func (t *tslvTest) SetError(err error) {
t.ciVisibilityCommon.SetError(err)
t.Suite().SetTag(ext.Error, true)
t.Suite().Module().SetTag(ext.Error, true)
}
// SetErrorInfo sets detailed error information on the test and marks the suite and module as having an error.
func (t *tslvTest) SetErrorInfo(errType string, message string, callstack string) {
t.ciVisibilityCommon.SetErrorInfo(errType, message, callstack)
t.Suite().SetTag(ext.Error, true)
t.Suite().Module().SetTag(ext.Error, true)
}
// SetTestFunc sets the function to be tested and records its source location.
func (t *tslvTest) SetTestFunc(fn *runtime.Func) {
if fn == nil {
return
}
file, line := fn.FileLine(fn.Entry())
file = utils.GetRelativePathFromCITagsSourceRoot(file)
t.SetTag(constants.TestSourceFile, file)
t.SetTag(constants.TestSourceStartLine, line)
codeOwners := utils.GetCodeOwners()
if codeOwners != nil {
match, found := codeOwners.Match("/" + file)
if found {
t.SetTag(constants.TestCodeOwners, match.GetOwnersString())
}
}
}
// SetBenchmarkData sets benchmark data for the test.
func (t *tslvTest) SetBenchmarkData(measureType string, data map[string]any) {
t.span.SetTag(constants.TestType, constants.TestTypeBenchmark)
for k, v := range data {
t.span.SetTag(fmt.Sprintf("benchmark.%s.%s", measureType, k), v)
}
}
|