// // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // using System.Collections.Specialized; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Versioning; using Xunit.Abstractions; namespace IntegrationTests.Helpers; public class EnvironmentHelper { private static readonly string RuntimeFrameworkDescription = RuntimeInformation.FrameworkDescription.ToLower(); private readonly ITestOutputHelper _output; private readonly int _major; private readonly int _minor; private readonly string? _patch = null; private readonly string _appNamePrepend; private readonly string _runtime; private readonly bool _isCoreClr; private readonly string _testApplicationDirectory; private readonly TargetFrameworkAttribute _targetFramework; private string? _integrationsFileLocation; private string? _profilerFileLocation; public EnvironmentHelper( string testApplicationName, Type anchorType, ITestOutputHelper output, string? testApplicationDirectory = null, bool prependTestApplicationToAppName = true) { TestApplicationName = testApplicationName; _testApplicationDirectory = testApplicationDirectory ?? Path.Combine("test", "test-applications", "integrations"); _targetFramework = Assembly.GetAssembly(anchorType)?.GetCustomAttribute()!; _output = output; var parts = _targetFramework.FrameworkName.Split(','); _runtime = parts[0]; _isCoreClr = _runtime.Equals(EnvironmentTools.CoreFramework); var versionParts = parts[1].Replace("Version=v", string.Empty).Split('.'); _major = int.Parse(versionParts[0]); _minor = int.Parse(versionParts[1]); if (versionParts.Length == 3) { _patch = versionParts[2]; } _appNamePrepend = prependTestApplicationToAppName ? "TestApplication." : string.Empty; SetDefaultEnvironmentVariables(); } public bool DebugModeEnabled { get; set; } = true; public Dictionary CustomEnvironmentVariables { get; set; } = new(); public string TestApplicationName { get; } public string FullTestApplicationName => $"{_appNamePrepend}{TestApplicationName}"; public static bool IsCoreClr() { return RuntimeFrameworkDescription.Contains("core") || Environment.Version.Major >= 5; } public static string GetNukeBuildOutput() { string nukeOutputPath = Path.Combine( EnvironmentTools.GetSolutionDirectory(), "bin", "tracer-home"); if (Directory.Exists(nukeOutputPath)) { return nukeOutputPath; } throw new Exception($"Unable to find Nuke output at: {nukeOutputPath}. Ensure Nuke has run first."); } public static bool IsRunningOnCI() { // https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables // Github sets CI environment variable var env = Environment.GetEnvironmentVariable("CI"); return !string.IsNullOrEmpty(env); } public void SetEnvironmentVariables(StringDictionary environmentVariables) { foreach (var key in CustomEnvironmentVariables.Keys) { environmentVariables[key] = CustomEnvironmentVariables[key]; } } public string GetProfilerPath() { if (_profilerFileLocation != null) { return _profilerFileLocation; } string extension = EnvironmentTools.GetOS() switch { "win" => "dll", "linux" => "so", "osx" => "dylib", _ => throw new PlatformNotSupportedException() }; string fileName = $"OpenTelemetry.AutoInstrumentation.Native.{extension}"; string nukeOutput = GetNukeBuildOutput(); string profilerPath = Path.Combine(nukeOutput, $"{EnvironmentTools.GetClrProfilerDirectoryName()}-{EnvironmentTools.GetPlatform().ToLower()}", fileName); if (File.Exists(profilerPath)) { _profilerFileLocation = profilerPath; _output?.WriteLine($"Found profiler at {_profilerFileLocation}."); return _profilerFileLocation; } throw new Exception($"Unable to find profiler at: {profilerPath}"); } public string GetIntegrationsPath() { if (_integrationsFileLocation != null) { return _integrationsFileLocation; } string fileName = $"integrations.json"; string integrationsPath = Path.Combine(GetNukeBuildOutput(), fileName); if (File.Exists(integrationsPath)) { _integrationsFileLocation = integrationsPath; _output?.WriteLine($"Found integrations at {_profilerFileLocation}."); return _integrationsFileLocation; } throw new Exception($"Unable to find integrations at: {integrationsPath}"); } public string GetTestApplicationPath(string packageVersion = "", string framework = "") { string extension = "exe"; if (IsCoreClr() || _testApplicationDirectory.Contains("aspnet")) { extension = "dll"; } var appFileName = $"{FullTestApplicationName}.{extension}"; var testApplicationPath = Path.Combine(GetTestApplicationApplicationOutputDirectory(packageVersion: packageVersion, framework: framework), appFileName); return testApplicationPath; } public string GetTestApplicationExecutionSource() { string executor; if (_testApplicationDirectory.Contains("aspnet")) { executor = $"C:\\Program Files{(Environment.Is64BitProcess ? string.Empty : " (x86)")}\\IIS Express\\iisexpress.exe"; } else if (IsCoreClr()) { executor = EnvironmentTools.IsWindows() ? "dotnet.exe" : "dotnet"; } else { var appFileName = $"{FullTestApplicationName}.exe"; executor = Path.Combine(GetTestApplicationApplicationOutputDirectory(), appFileName); if (!File.Exists(executor)) { throw new Exception($"Unable to find executing assembly at {executor}"); } } return executor; } public string GetTestApplicationBaseBinDirectory() { var solutionDirectory = EnvironmentTools.GetSolutionDirectory(); var projectDir = Path.Combine( solutionDirectory, _testApplicationDirectory, "bin", $"{FullTestApplicationName}"); return projectDir; } public string GetTestApplicationApplicationOutputDirectory(string packageVersion = "", string framework = "") { var targetFramework = string.IsNullOrEmpty(framework) ? GetTargetFramework() : framework; var baseBinDirectory = GetTestApplicationBaseBinDirectory(); if (_testApplicationDirectory.Contains("aspnet")) { return Path.Combine( baseBinDirectory, EnvironmentTools.GetBuildConfiguration(), "app.publish"); } return Path.Combine( baseBinDirectory, packageVersion, EnvironmentTools.GetPlatform().ToLowerInvariant(), EnvironmentTools.GetBuildConfiguration(), targetFramework); } public string GetTargetFramework() { if (_isCoreClr) { return $"net{_major}.{_minor}"; } return $"net{_major}{_minor}{_patch ?? string.Empty}"; } private static string GetStartupHookOutputPath() { string startupHookOutputPath = Path.Combine( GetNukeBuildOutput(), "net", "OpenTelemetry.AutoInstrumentation.StartupHook.dll"); return startupHookOutputPath; } private static string GetSharedStorePath() { string storePath = Path.Combine( GetNukeBuildOutput(), "store"); return storePath; } private static string GetAdditionalDepsPath() { string additionalDeps = Path.Combine( GetNukeBuildOutput(), "AdditionalDeps"); return additionalDeps; } private void SetDefaultEnvironmentVariables() { string profilerPath = GetProfilerPath(); CustomEnvironmentVariables["DOTNET_STARTUP_HOOKS"] = GetStartupHookOutputPath(); CustomEnvironmentVariables["DOTNET_SHARED_STORE"] = GetSharedStorePath(); CustomEnvironmentVariables["DOTNET_ADDITIONAL_DEPS"] = GetAdditionalDepsPath(); // call TestHelper.EnableBytecodeInstrumentation() to enable CoreCLR Profiler when bytecode instrumentation is needed // it is not enabled by default to make sure that the instrumentations that do not require CoreCLR Profiler are working without it CustomEnvironmentVariables["CORECLR_PROFILER"] = EnvironmentTools.ProfilerClsId; CustomEnvironmentVariables["CORECLR_PROFILER_PATH"] = profilerPath; CustomEnvironmentVariables["COR_ENABLE_PROFILING"] = "1"; CustomEnvironmentVariables["COR_PROFILER"] = EnvironmentTools.ProfilerClsId; CustomEnvironmentVariables["COR_PROFILER_PATH"] = profilerPath; CustomEnvironmentVariables["OTEL_LOG_LEVEL"] = "debug"; CustomEnvironmentVariables["OTEL_DOTNET_AUTO_LOG_DIRECTORY"] = Path.Combine(EnvironmentTools.GetSolutionDirectory(), "build_data", "profiler-logs"); CustomEnvironmentVariables["OTEL_DOTNET_AUTO_HOME"] = GetNukeBuildOutput(); CustomEnvironmentVariables["OTEL_DOTNET_AUTO_INTEGRATIONS_FILE"] = Environment.GetEnvironmentVariable("OTEL_DOTNET_AUTO_INTEGRATIONS_FILE") ?? GetIntegrationsPath(); CustomEnvironmentVariables["OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES"] = "TestApplication.*"; // exporters are disabled by default in order not to have errors in the logs CustomEnvironmentVariables["OTEL_TRACES_EXPORTER"] = "none"; CustomEnvironmentVariables["OTEL_METRICS_EXPORTER"] = "none"; CustomEnvironmentVariables["OTEL_LOGS_EXPORTER"] = "none"; } }