221 lines
8.9 KiB
C#
221 lines
8.9 KiB
C#
// <copyright file="TestHelper.cs" company="OpenTelemetry Authors">
|
|
// 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.
|
|
// </copyright>
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using DotNet.Testcontainers.Builders;
|
|
using DotNet.Testcontainers.Containers;
|
|
using FluentAssertions;
|
|
using IntegrationTests.Helpers.Models;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace IntegrationTests.Helpers;
|
|
|
|
public abstract class TestHelper
|
|
{
|
|
// Warning: Long timeouts can cause integer overflow!
|
|
private static readonly TimeSpan DefaultProcessTimeout = TimeSpan.FromMinutes(5);
|
|
|
|
protected TestHelper(string testApplicationName, ITestOutputHelper output)
|
|
: this(new EnvironmentHelper(testApplicationName, typeof(TestHelper), output), output)
|
|
{
|
|
}
|
|
|
|
protected TestHelper(EnvironmentHelper environmentHelper, ITestOutputHelper output)
|
|
{
|
|
EnvironmentHelper = environmentHelper;
|
|
Output = output;
|
|
|
|
Output.WriteLine($"Platform: {EnvironmentTools.GetPlatform()}");
|
|
Output.WriteLine($"Configuration: {EnvironmentTools.GetBuildConfiguration()}");
|
|
Output.WriteLine($"TargetFramework: {EnvironmentHelper.GetTargetFramework()}");
|
|
Output.WriteLine($".NET Core: {EnvironmentHelper.IsCoreClr()}");
|
|
Output.WriteLine($"Profiler DLL: {EnvironmentHelper.GetProfilerPath()}");
|
|
}
|
|
|
|
protected EnvironmentHelper EnvironmentHelper { get; }
|
|
|
|
protected ITestOutputHelper Output { get; }
|
|
|
|
public async Task<Container> StartContainerAsync(TestSettings testSettings, int webPort)
|
|
{
|
|
// get path to test application that the profiler will attach to
|
|
string testApplicationName = $"testapplication-{EnvironmentHelper.TestApplicationName.ToLowerInvariant()}";
|
|
|
|
string networkName = DockerNetworkHelper.IntegrationTestsNetworkName;
|
|
string networkId = await DockerNetworkHelper.SetupIntegrationTestsNetworkAsync();
|
|
|
|
string logPath = EnvironmentHelper.IsRunningOnCI()
|
|
? Path.Combine(Environment.GetEnvironmentVariable("GITHUB_WORKSPACE"), "build_data", "profiler-logs")
|
|
: Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), @"OpenTelemetry .NET AutoInstrumentation", "logs");
|
|
|
|
Directory.CreateDirectory(logPath);
|
|
|
|
Output.WriteLine("Collecting docker logs to: " + logPath);
|
|
|
|
var agentPort = testSettings.TracesSettings?.Port ?? testSettings.MetricsSettings?.Port;
|
|
var builder = new TestcontainersBuilder<TestcontainersContainer>()
|
|
.WithImage(testApplicationName)
|
|
.WithCleanUp(cleanUp: true)
|
|
.WithOutputConsumer(Consume.RedirectStdoutAndStderrToConsole())
|
|
.WithName($"{testApplicationName}-{agentPort}-{webPort}")
|
|
.WithNetwork(networkId, networkName)
|
|
.WithPortBinding(webPort, 80)
|
|
.WithBindMount(logPath, "c:/inetpub/wwwroot/logs")
|
|
.WithBindMount(EnvironmentHelper.GetNukeBuildOutput(), "c:/opentelemetry");
|
|
|
|
string agentBaseUrl = $"http://{DockerNetworkHelper.IntegrationTestsGateway}:{agentPort}";
|
|
string agentHealthzUrl = $"{agentBaseUrl}/healthz";
|
|
|
|
if (testSettings.TracesSettings != null)
|
|
{
|
|
string zipkinEndpoint = $"{agentBaseUrl}/api/v2/spans";
|
|
Output.WriteLine($"Zipkin Endpoint: {zipkinEndpoint}");
|
|
|
|
builder = builder.WithEnvironment("OTEL_EXPORTER_ZIPKIN_ENDPOINT", zipkinEndpoint);
|
|
}
|
|
|
|
if (testSettings.MetricsSettings != null)
|
|
{
|
|
Output.WriteLine($"Otlp Endpoint: {agentBaseUrl}");
|
|
builder = builder.WithEnvironment("OTEL_EXPORTER_OTLP_ENDPOINT", agentBaseUrl);
|
|
builder = builder.WithEnvironment("OTEL_METRIC_EXPORT_INTERVAL", "1000");
|
|
builder = builder.WithEnvironment("OTEL_DOTNET_AUTO_METRICS_ENABLED_INSTRUMENTATIONS", "AspNet");
|
|
}
|
|
|
|
var container = builder.Build();
|
|
var wasStarted = container.StartAsync().Wait(TimeSpan.FromMinutes(5));
|
|
|
|
wasStarted.Should().BeTrue($"Container based on {testApplicationName} has to be operational for the test.");
|
|
|
|
Output.WriteLine($"Container was started successfully.");
|
|
|
|
PowershellHelper.RunCommand($"docker exec {container.Name} curl -v {agentHealthzUrl}", Output);
|
|
|
|
var webAppHealthzUrl = $"http://localhost:{webPort}/healthz";
|
|
var webAppHealthzResult = await HealthzHelper.TestHealtzAsync(webAppHealthzUrl, "IIS WebApp", Output);
|
|
|
|
webAppHealthzResult.Should().BeTrue("IIS WebApp health check never returned OK.");
|
|
|
|
Output.WriteLine($"IIS WebApp was started successfully.");
|
|
|
|
return new Container(container);
|
|
}
|
|
|
|
/// <summary>
|
|
/// StartTestApplication starts the test application
|
|
// and returns the Process instance for further interaction.
|
|
/// </summary>
|
|
public Process StartTestApplication(int traceAgentPort = 0, string arguments = null, string packageVersion = "", int aspNetCorePort = 0, string framework = "", bool enableStartupHook = true)
|
|
{
|
|
var testSettings = new TestSettings
|
|
{
|
|
Arguments = arguments,
|
|
PackageVersion = packageVersion,
|
|
AspNetCorePort = aspNetCorePort,
|
|
Framework = framework,
|
|
EnableStartupHook = enableStartupHook
|
|
};
|
|
|
|
if (traceAgentPort != 0)
|
|
{
|
|
testSettings.TracesSettings = new() { Port = traceAgentPort };
|
|
}
|
|
|
|
return StartTestApplication(testSettings);
|
|
}
|
|
|
|
/// <summary>
|
|
/// RunTestApplication starts the test application, wait up to DefaultProcessTimeout.
|
|
/// Assertion exceptions are thrown if it timed out or the exit code is non-zero.
|
|
/// </summary>
|
|
public void RunTestApplication(int traceAgentPort = 0, int metricsAgentPort = 0, string arguments = null, string packageVersion = "", string framework = "", int aspNetCorePort = 5000, bool enableStartupHook = true)
|
|
{
|
|
var testSettings = new TestSettings
|
|
{
|
|
Arguments = arguments,
|
|
PackageVersion = packageVersion,
|
|
AspNetCorePort = aspNetCorePort,
|
|
Framework = framework,
|
|
EnableStartupHook = enableStartupHook
|
|
};
|
|
|
|
if (traceAgentPort != 0)
|
|
{
|
|
testSettings.TracesSettings = new() { Port = traceAgentPort };
|
|
}
|
|
|
|
if (metricsAgentPort != 0)
|
|
{
|
|
testSettings.MetricsSettings = new() { Port = metricsAgentPort };
|
|
}
|
|
|
|
RunTestApplication(testSettings);
|
|
}
|
|
|
|
protected void EnableDebugMode()
|
|
{
|
|
EnvironmentHelper.DebugModeEnabled = true;
|
|
}
|
|
|
|
protected void SetEnvironmentVariable(string key, string value)
|
|
{
|
|
EnvironmentHelper.CustomEnvironmentVariables.Add(key, value);
|
|
}
|
|
|
|
private void RunTestApplication(TestSettings testSettings)
|
|
{
|
|
using var process = StartTestApplication(testSettings);
|
|
Output.WriteLine($"ProcessName: " + process.ProcessName);
|
|
using var helper = new ProcessHelper(process);
|
|
|
|
bool processTimeout = !process.WaitForExit((int)DefaultProcessTimeout.TotalMilliseconds);
|
|
if (processTimeout)
|
|
{
|
|
process.Kill();
|
|
}
|
|
|
|
Output.WriteLine($"ProcessId: " + process.Id);
|
|
Output.WriteLine($"Exit Code: " + process.ExitCode);
|
|
Output.WriteResult(helper);
|
|
|
|
processTimeout.Should().BeFalse("Test application timed out");
|
|
process.ExitCode.Should().Be(0, $"Test application exited with non-zero exit code");
|
|
}
|
|
|
|
private Process StartTestApplication(TestSettings testSettings)
|
|
{
|
|
// get path to test application that the profiler will attach to
|
|
string testApplicationPath = EnvironmentHelper.GetTestApplicationPath(testSettings.PackageVersion, testSettings.Framework);
|
|
if (!File.Exists(testApplicationPath))
|
|
{
|
|
throw new Exception($"application not found: {testApplicationPath}");
|
|
}
|
|
|
|
Output.WriteLine($"Starting Application: {testApplicationPath}");
|
|
var executable = EnvironmentHelper.IsCoreClr() ? EnvironmentHelper.GetTestApplicationExecutionSource() : testApplicationPath;
|
|
var args = EnvironmentHelper.IsCoreClr() ? $"{testApplicationPath} {testSettings.Arguments ?? string.Empty}" : testSettings.Arguments;
|
|
|
|
return InstrumentedProcessHelper.StartInstrumentedProcess(
|
|
executable,
|
|
EnvironmentHelper,
|
|
args,
|
|
testSettings);
|
|
}
|
|
}
|