diff --git a/native/exports.go b/native/exports.go index 1b4603c..2c3f1c7 100644 --- a/native/exports.go +++ b/native/exports.go @@ -426,6 +426,7 @@ import ( civisibility "github.com/DataDog/dd-trace-go/v2/internal/civisibility/integrations" "github.com/DataDog/dd-trace-go/v2/internal/civisibility/utils" "github.com/DataDog/dd-trace-go/v2/internal/civisibility/utils/net" + "github.com/DataDog/dd-trace-go/v2/internal/civisibility/utils/telemetry" ) // ******************************************************************************************************************* @@ -1570,25 +1571,38 @@ func topt_test_set_error(test_id C.topt_TestId, error_type *C.char, error_messag //export topt_test_set_source func topt_test_set_source(test_id C.topt_TestId, file *C.char, start_line *C.int, end_line *C.int) C.Bool { if test, ok := getTest(test_id); ok { + var sFile string + var iStartline, iEndline int + if file != nil { - gFile := C.GoString(file) - gFile = utils.GetRelativePathFromCITagsSourceRoot(gFile) - test.SetTag(constants.TestSourceFile, gFile) + sFile = C.GoString(file) + sFile = utils.GetRelativePathFromCITagsSourceRoot(sFile) + test.SetTag(constants.TestSourceFile, sFile) // get the codeowner of the function codeOwners := utils.GetCodeOwners() if codeOwners != nil { - match, found := codeOwners.Match("/" + gFile) + match, found := codeOwners.Match("/" + sFile) if found { test.SetTag(constants.TestCodeOwners, match.GetOwnersString()) } } } if start_line != nil { - test.SetTag(constants.TestSourceStartLine, int(*start_line)) + iStartline = int(*start_line) + test.SetTag(constants.TestSourceStartLine, iStartline) } if end_line != nil { - test.SetTag(constants.TestSourceEndLine, int(*end_line)) + iEndline = int(*end_line) + test.SetTag(constants.TestSourceEndLine, iEndline) + } + if sFile != "" && iStartline > 0 && iEndline > 0 { + if analyzer := civisibility.GetImpactedTestsAnalyzer(); analyzer != nil { + if analyzer.IsImpacted(test.Name(), sFile, iStartline, iEndline) { + test.SetTag(constants.TestIsModified, "true") + telemetry.ImpactedTestsModified() + } + } } return toBool(true) } @@ -1663,6 +1677,32 @@ func topt_test_set_benchmark_number_data(test_id C.topt_TestId, measure_type *C. return toBool(false) } +// topt_test_log logs a message for a test. +// +// Parameters: +// - test_id: The ID of the test. +// - message: A string message to log. +// - tags: Optional string containing tags associated with the log message. +// +// Returns: +// - C.Bool: True if the message was logged successfully, false if the test was not found or message was nil. +// +//export topt_test_log +func topt_test_log(test_id C.topt_TestId, message *C.char, tags *C.char) C.Bool { + if message == nil { + return toBool(false) + } + var sTags string + if tags != nil { + sTags = C.GoString(tags) + } + if test, ok := getTest(test_id); ok { + test.Log(C.GoString(message), sTags) + return toBool(true) + } + return toBool(false) +} + // ******************************************************************************************************************* // Spans // ******************************************************************************************************************* diff --git a/sdks/python/test-optimization-sdk/setup.py b/sdks/python/test-optimization-sdk/setup.py index 4161c4f..4d31f8e 100644 --- a/sdks/python/test-optimization-sdk/setup.py +++ b/sdks/python/test-optimization-sdk/setup.py @@ -5,7 +5,7 @@ setup( name="test-optimization-sdk", - version="0.0.2", + version="0.0.3", description="Datadog's test optimization SDK for Python", author="Datadog", author_email="info@datadoghq.com", diff --git a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/__init__.py b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/__init__.py index af2df23..f5cb8f0 100644 --- a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/__init__.py +++ b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/__init__.py @@ -26,7 +26,7 @@ TEST_OPTIMIZATION_DOWNLOAD_URL_FORMAT, ) -__version__ = "0.0.1" +__version__ = "0.0.4" __all__ = [ "TestOptimization", diff --git a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/constants.py b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/constants.py index fe1f899..2675571 100644 --- a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/constants.py +++ b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/constants.py @@ -6,4 +6,4 @@ TEST_OPTIMIZATION_SDK_SKIP_NATIVE_INSTALL = "TEST_OPTIMIZATION_SDK_SKIP_NATIVE_INSTALL" TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH = "TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH" TEST_OPTIMIZATION_DEV_MODE = "TEST_OPTIMIZATION_DEV_MODE" -TEST_OPTIMIZATION_DOWNLOAD_URL_FORMAT = "https://github.com/DataDog/test-optimization-native/releases/download/v0.0.3-preview/" \ No newline at end of file +TEST_OPTIMIZATION_DOWNLOAD_URL_FORMAT = "https://github.com/DataDog/test-optimization-native/releases/download/v0.0.4-preview/" \ No newline at end of file diff --git a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/lib.py b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/lib.py index 41c82cc..28ba54e 100644 --- a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/lib.py +++ b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/lib.py @@ -257,6 +257,7 @@ Bool topt_test_close(topt_TestId test_id, topt_TestCloseOptions options); Bool topt_test_set_benchmark_string_data(topt_TestId test_id, char* measure_type, topt_KeyValueArray data); Bool topt_test_set_benchmark_number_data(topt_TestId test_id, char* measure_type, topt_KeyNumberArray data); + Bool topt_test_log(topt_TestId test_id, char* message, char* tags); topt_SpanResult topt_span_create(topt_TslvId parent_id, topt_SpanStartOptions span_options); Bool topt_span_set_string_tag(topt_TslvId span_id, char* key, char* value); @@ -392,6 +393,7 @@ def _get_library_path() -> str: "topt_test_close", "topt_test_set_benchmark_string_data", "topt_test_set_benchmark_number_data", + "topt_test_log", "topt_span_create", "topt_span_set_string_tag", "topt_span_set_number_tag", diff --git a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/test.py b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/test.py index 739675a..9b34a33 100644 --- a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/test.py +++ b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/test.py @@ -21,6 +21,7 @@ topt_send_code_coverage_payload, topt_test_set_benchmark_number_data, topt_test_set_benchmark_string_data, + topt_test_log ) from .utils import get_now from .base import BaseEntity @@ -455,4 +456,37 @@ def set_benchmark_number_data( kn_array[0], )) except Exception as e: - raise RuntimeError(f"Failed to set benchmark number data: {e}") \ No newline at end of file + raise RuntimeError(f"Failed to set benchmark number data: {e}") + + def log(self, message: str, tags: Optional[str]) -> bool: + """Log a message for this test. + + Args: + message: Message to log + tags: Optional tags to log + + Returns: + True if successful, False otherwise + + Raises: + RuntimeError: If the test is already closed + """ + if self._closed: + raise RuntimeError("Cannot log on closed test") + + try: + # Create C strings for the message and tags + message_bytes = message.encode() + tags_bytes = tags.encode() if tags else None + + message_cstr = self._track_c_string(ffi.new("char[]", message_bytes)) + tags_cstr = self._track_c_string(ffi.new("char[]", tags_bytes)) if tags else ffi.NULL + + # Log the message + return bool(topt_test_log( + self.test_id, + message_cstr, + tags_cstr, + )) + except Exception as e: + raise RuntimeError(f"Failed to log the message: {e}") diff --git a/sdks/python/test-optimization-sdk/tests/test_complete.py b/sdks/python/test-optimization-sdk/tests/test_complete.py index 07096b9..5646acd 100644 --- a/sdks/python/test-optimization-sdk/tests/test_complete.py +++ b/sdks/python/test-optimization-sdk/tests/test_complete.py @@ -110,6 +110,8 @@ def test_complete(): pass_test.set_number_tag("Pass-NumberFromPython", 42.0) pass_test.set_test_source("test.py", 6, 58) pass_test.set_coverage_data(["file.py"]) + pass_test.log("Hello world", "tag1=value1,tag2=value2") + pass_test.log("Hello world", None) # Set benchmark data for pass test measurement_data: Dict[str, float] = { diff --git a/sdks/rust/test-optimization-sdk/Cargo.toml b/sdks/rust/test-optimization-sdk/Cargo.toml index c6b4fbd..c11f371 100644 --- a/sdks/rust/test-optimization-sdk/Cargo.toml +++ b/sdks/rust/test-optimization-sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test-optimization-sdk" -version = "0.0.3" +version = "0.0.4" edition = "2021" description = "Datadog's test optimization sdk" license = "Apache-2.0" diff --git a/sdks/rust/test-optimization-sdk/build.rs b/sdks/rust/test-optimization-sdk/build.rs index e8b97ec..0246590 100644 --- a/sdks/rust/test-optimization-sdk/build.rs +++ b/sdks/rust/test-optimization-sdk/build.rs @@ -12,7 +12,7 @@ use ureq::AsSendBody; const TEST_OPTIMIZATION_SDK_SKIP_NATIVE_INSTALL: &str = "TEST_OPTIMIZATION_SDK_SKIP_NATIVE_INSTALL"; const TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH: &str = "TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH"; const TEST_OPTIMIZATION_DEV_MODE: &str = "TEST_OPTIMIZATION_DEV_MODE"; -const TEST_OPTIMIZATION_DOWNLOAD_URL_FORMAT: &str = "https://github.com/DataDog/test-optimization-native/releases/download/v0.0.3-preview/"; +const TEST_OPTIMIZATION_DOWNLOAD_URL_FORMAT: &str = "https://github.com/DataDog/test-optimization-native/releases/download/v0.0.4-preview/"; fn main() { let target = env::var("TARGET").expect("Cargo did not provide TARGET"); diff --git a/sdks/rust/test-optimization-sdk/src/test_optimization/lib/bindings.rs b/sdks/rust/test-optimization-sdk/src/test_optimization/lib/bindings.rs index 83c227f..091ba71 100644 --- a/sdks/rust/test-optimization-sdk/src/test_optimization/lib/bindings.rs +++ b/sdks/rust/test-optimization-sdk/src/test_optimization/lib/bindings.rs @@ -307,6 +307,7 @@ unsafe extern "C" { pub fn topt_test_set_source(test_id: topt_TestId, file: *mut c_char, start_line: *mut c_int, end_line: *mut c_int) -> Bool; pub fn topt_test_set_benchmark_string_data(test_id: topt_TestId, measure_type: *mut c_char, data_array: topt_KeyValueArray) -> Bool; pub fn topt_test_set_benchmark_number_data(test_id: topt_TestId, measure_type: *mut c_char, data_array: topt_KeyNumberArray) -> Bool; + pub fn topt_test_log(test_id: topt_TestId, message: *mut c_char, tags: *mut c_char) -> Bool; // Span functions pub fn topt_span_create(parent_id: topt_TslvId, span_options: topt_SpanStartOptions) -> topt_SpanResult; diff --git a/sdks/rust/test-optimization-sdk/src/test_optimization/test.rs b/sdks/rust/test-optimization-sdk/src/test_optimization/test.rs index 1166015..61a827a 100644 --- a/sdks/rust/test-optimization-sdk/src/test_optimization/test.rs +++ b/sdks/rust/test-optimization-sdk/src/test_optimization/test.rs @@ -297,4 +297,20 @@ impl Test { unsafe { dealloc(kn_array_ptr as *mut u8, layout); } result } + + /// Write a log message for this test + #[allow(dead_code)] + pub fn log(&self, message: impl AsRef, tags: Option>) -> bool { + let message_cstring = CString::new(message.as_ref()).unwrap(); + let tags_cstring = tags.map(|wd| CString::new(wd.as_ref()).unwrap()); + unsafe { + Bool_to_bool(topt_test_log( + self.test_id, + message_cstring.as_ptr() as *mut c_char, + tags_cstring + .as_ref() + .map_or(null_mut(), |s| s.as_ptr() as *mut c_char), + )) + } + } } diff --git a/sdks/rust/test-optimization-sdk/src/tests.rs b/sdks/rust/test-optimization-sdk/src/tests.rs index cdbd295..f8b4673 100644 --- a/sdks/rust/test-optimization-sdk/src/tests.rs +++ b/sdks/rust/test-optimization-sdk/src/tests.rs @@ -73,6 +73,8 @@ fn complete() { pass_test.set_number_tag("Pass-NumberFromRust", 42f64); pass_test.set_test_source("test.rs", &6, &58); pass_test.set_coverage_data(&["file.rs"]); + pass_test.log("Hello world", Some("tag1=value1,tag2=value2")); + pass_test.log("Hello world", None::<&str>); let mut measurement_data: HashMap<&str, f64> = HashMap::new(); measurement_data.insert("data1", 42f64);