about summary refs log tree commit diff
path: root/main/civisibility/integrations/gotesting/testingT.go
diff options
context:
space:
mode:
authorManuel Palenzuela Merino <manuel.palenzuela@datadoghq.com>2024-12-05 11:07:35 +0100
committerManuel Palenzuela Merino <manuel.palenzuela@datadoghq.com>2024-12-05 11:08:17 +0100
commitee3c0aa3d71a857ae7fb60f1b98cf129ee6ad71d (patch)
tree8aeaa3b76be6cdcf326acf3627b3d283a8b2c372 /main/civisibility/integrations/gotesting/testingT.go
parentUpdate README.md (diff)
downloadtest-repo-ee3c0aa3d71a857ae7fb60f1b98cf129ee6ad71d.tar.gz
test-repo-ee3c0aa3d71a857ae7fb60f1b98cf129ee6ad71d.tar.bz2
test-repo-ee3c0aa3d71a857ae7fb60f1b98cf129ee6ad71d.zip
Add tests
Diffstat (limited to 'main/civisibility/integrations/gotesting/testingT.go')
-rw-r--r--main/civisibility/integrations/gotesting/testingT.go217
1 files changed, 217 insertions, 0 deletions
diff --git a/main/civisibility/integrations/gotesting/testingT.go b/main/civisibility/integrations/gotesting/testingT.go
new file mode 100644
index 0000000..5b7f117
--- /dev/null
+++ b/main/civisibility/integrations/gotesting/testingT.go
@@ -0,0 +1,217 @@
+// 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 gotesting
+
+import (
+	"context"
+	"fmt"
+	"reflect"
+	"runtime"
+	"sync"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/integrations"
+	"ci-visibility-test-github/main/civisibility/utils"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
+)
+
+var (
+	// ciVisibilityTests holds a map of *testing.T to civisibility.DdTest for tracking tests.
+	ciVisibilityTests = map[*testing.T]integrations.DdTest{}
+
+	// ciVisibilityTestsMutex is a read-write mutex for synchronizing access to ciVisibilityTests.
+	ciVisibilityTestsMutex sync.RWMutex
+)
+
+// T is a type alias for testing.T to provide additional methods for CI visibility.
+type T testing.T
+
+// GetTest is a helper to return *gotesting.T from *testing.T.
+// Internally, it is just a (*gotesting.T)(t) cast.
+func GetTest(t *testing.T) *T {
+	return (*T)(t)
+}
+
+// Run runs f as a subtest of t called name. It runs f in a separate goroutine
+// and blocks until f returns or calls t.Parallel to become a parallel test.
+// Run reports whether f succeeded (or at least did not fail before calling t.Parallel).
+//
+// Run may be called simultaneously from multiple goroutines, but all such calls
+// must return before the outer test function for t returns.
+func (ddt *T) Run(name string, f func(*testing.T)) bool {
+	// Reflect the function to obtain its pointer.
+	fReflect := reflect.Indirect(reflect.ValueOf(f))
+	moduleName, suiteName := utils.GetModuleAndSuiteName(fReflect.Pointer())
+	originalFunc := runtime.FuncForPC(fReflect.Pointer())
+
+	// Increment the test count in the module.
+	atomic.AddInt32(modulesCounters[moduleName], 1)
+
+	// Increment the test count in the suite.
+	atomic.AddInt32(suitesCounters[suiteName], 1)
+
+	t := (*testing.T)(ddt)
+	return t.Run(name, func(t *testing.T) {
+		// Create or retrieve the module, suite, and test for CI visibility.
+		module := session.GetOrCreateModuleWithFramework(moduleName, testFramework, runtime.Version())
+		suite := module.GetOrCreateSuite(suiteName)
+		test := suite.CreateTest(t.Name())
+		test.SetTestFunc(originalFunc)
+		setCiVisibilityTest(t, test)
+		defer func() {
+			if r := recover(); r != nil {
+				// Handle panic and set error information.
+				test.SetErrorInfo("panic", fmt.Sprint(r), utils.GetStacktrace(1))
+				test.Close(integrations.ResultStatusFail)
+				checkModuleAndSuite(module, suite)
+				integrations.ExitCiVisibility()
+				panic(r)
+			} else {
+				// Normal finalization: determine the test result based on its state.
+				if t.Failed() {
+					test.SetTag(ext.Error, true)
+					suite.SetTag(ext.Error, true)
+					module.SetTag(ext.Error, true)
+					test.Close(integrations.ResultStatusFail)
+				} else if t.Skipped() {
+					test.Close(integrations.ResultStatusSkip)
+				} else {
+					test.Close(integrations.ResultStatusPass)
+				}
+				checkModuleAndSuite(module, suite)
+			}
+		}()
+
+		// Execute the original test function.
+		f(t)
+	})
+}
+
+// Context returns the CI Visibility context of the Test span.
+// This may be used to create test's children spans useful for
+// integration tests.
+func (ddt *T) Context() context.Context {
+	t := (*testing.T)(ddt)
+	ciTest := getCiVisibilityTest(t)
+	if ciTest != nil {
+		return ciTest.Context()
+	}
+
+	return context.Background()
+}
+
+// Fail marks the function as having failed but continues execution.
+func (ddt *T) Fail() { ddt.getTWithError("Fail", "failed test").Fail() }
+
+// FailNow marks the function as having failed and stops its execution
+// by calling runtime.Goexit (which then runs all deferred calls in the
+// current goroutine). Execution will continue at the next test or benchmark.
+// FailNow must be called from the goroutine running the test or benchmark function,
+// not from other goroutines created during the test. Calling FailNow does not stop
+// those other goroutines.
+func (ddt *T) FailNow() {
+	t := ddt.getTWithError("FailNow", "failed test")
+	integrations.ExitCiVisibility()
+	t.FailNow()
+}
+
+// Error is equivalent to Log followed by Fail.
+func (ddt *T) Error(args ...any) { ddt.getTWithError("Error", fmt.Sprint(args...)).Error(args...) }
+
+// Errorf is equivalent to Logf followed by Fail.
+func (ddt *T) Errorf(format string, args ...any) {
+	ddt.getTWithError("Errorf", fmt.Sprintf(format, args...)).Errorf(format, args...)
+}
+
+// Fatal is equivalent to Log followed by FailNow.
+func (ddt *T) Fatal(args ...any) { ddt.getTWithError("Fatal", fmt.Sprint(args...)).Fatal(args...) }
+
+// Fatalf is equivalent to Logf followed by FailNow.
+func (ddt *T) Fatalf(format string, args ...any) {
+	ddt.getTWithError("Fatalf", fmt.Sprintf(format, args...)).Fatalf(format, args...)
+}
+
+// Skip is equivalent to Log followed by SkipNow.
+func (ddt *T) Skip(args ...any) { ddt.getTWithSkip(fmt.Sprint(args...)).Skip(args...) }
+
+// Skipf is equivalent to Logf followed by SkipNow.
+func (ddt *T) Skipf(format string, args ...any) {
+	ddt.getTWithSkip(fmt.Sprintf(format, args...)).Skipf(format, args...)
+}
+
+// SkipNow marks the test as having been skipped and stops its execution
+// by calling runtime.Goexit. If a test fails (see Error, Errorf, Fail) and is then skipped,
+// it is still considered to have failed. Execution will continue at the next test or benchmark.
+// SkipNow must be called from the goroutine running the test, not from other goroutines created
+// during the test. Calling SkipNow does not stop those other goroutines.
+func (ddt *T) SkipNow() {
+	t := (*testing.T)(ddt)
+	ciTest := getCiVisibilityTest(t)
+	if ciTest != nil {
+		ciTest.Close(integrations.ResultStatusSkip)
+	}
+
+	t.SkipNow()
+}
+
+// Parallel signals that this test is to be run in parallel with (and only with)
+// other parallel tests. When a test is run multiple times due to use of
+// -test.count or -test.cpu, multiple instances of a single test never run in
+// parallel with each other.
+func (ddt *T) Parallel() { (*testing.T)(ddt).Parallel() }
+
+// Deadline reports the time at which the test binary will have
+// exceeded the timeout specified by the -timeout flag.
+// The ok result is false if the -timeout flag indicates “no timeout” (0).
+func (ddt *T) Deadline() (deadline time.Time, ok bool) {
+	return (*testing.T)(ddt).Deadline()
+}
+
+// Setenv calls os.Setenv(key, value) and uses Cleanup to
+// restore the environment variable to its original value
+// after the test. Because Setenv affects the whole process,
+// it cannot be used in parallel tests or tests with parallel ancestors.
+func (ddt *T) Setenv(key, value string) { (*testing.T)(ddt).Setenv(key, value) }
+
+func (ddt *T) getTWithError(errType string, errMessage string) *testing.T {
+	t := (*testing.T)(ddt)
+	ciTest := getCiVisibilityTest(t)
+	if ciTest != nil {
+		ciTest.SetErrorInfo(errType, errMessage, utils.GetStacktrace(2))
+	}
+	return t
+}
+
+func (ddt *T) getTWithSkip(skipReason string) *testing.T {
+	t := (*testing.T)(ddt)
+	ciTest := getCiVisibilityTest(t)
+	if ciTest != nil {
+		ciTest.CloseWithFinishTimeAndSkipReason(integrations.ResultStatusSkip, time.Now(), skipReason)
+	}
+	return t
+}
+
+// getCiVisibilityTest retrieves the CI visibility test associated with a given *testing.T.
+func getCiVisibilityTest(t *testing.T) integrations.DdTest {
+	ciVisibilityTestsMutex.RLock()
+	defer ciVisibilityTestsMutex.RUnlock()
+
+	if v, ok := ciVisibilityTests[t]; ok {
+		return v
+	}
+
+	return nil
+}
+
+// setCiVisibilityTest associates a CI visibility test with a given *testing.T.
+func setCiVisibilityTest(t *testing.T, ciTest integrations.DdTest) {
+	ciVisibilityTestsMutex.Lock()
+	defer ciVisibilityTestsMutex.Unlock()
+	ciVisibilityTests[t] = ciTest
+}