Skip to content

perf: improve RuntimeSupport integration test performance #1866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<DefineConstants Condition="'$(SkipRuntimeSupportIntegTests)'=='true'">SKIP_RUNTIME_SUPPORT_INTEG_TESTS</DefineConstants>
</PropertyGroup>

Expand All @@ -23,13 +23,13 @@
<PackageReference Include="AWSSDK.IdentityManagement" Version="3.7.402.7" />
<PackageReference Include="AWSSDK.Lambda" Version="3.7.402.3" />
<PackageReference Include="AWSSDK.S3" Version="3.7.103.34" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit" Version="2.9.2" />

<!-- This needs to be referenced to allow testing via AssumeRole credentials -->
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.13" />
Expand All @@ -39,24 +39,4 @@
<ProjectReference Include="..\..\..\src\Amazon.Lambda.APIGatewayEvents\Amazon.Lambda.APIGatewayEvents.csproj" />
</ItemGroup>

<Target Name="PackageTestFunction" BeforeTargets="BeforeBuild">
<Exec Command="dotnet tool install -g Amazon.Lambda.Tools" IgnoreExitCode="true" />

<Exec WorkingDirectory="..\CustomRuntimeFunctionTest" Command="dotnet restore" />
<Exec WorkingDirectory="..\CustomRuntimeFunctionTest" Condition="'$(Architecture)'=='' or '$(Architecture)'=='x86'" Command="dotnet lambda package -c Release --framework net6.0" />
<Exec WorkingDirectory="..\CustomRuntimeFunctionTest" Condition="'$(Architecture)'=='arm64'" Command="dotnet lambda package -c Release --framework net6.0 --function-architecture arm64" />

<Exec WorkingDirectory="..\CustomRuntimeFunctionTest" Command="dotnet restore" />
<Exec WorkingDirectory="..\CustomRuntimeFunctionTest" Condition="'$(Architecture)'=='' or '$(Architecture)'=='x86'" Command="dotnet lambda package -c Release --framework net8.0" />
<Exec WorkingDirectory="..\CustomRuntimeFunctionTest" Condition="'$(Architecture)'=='arm64'" Command="dotnet lambda package -c Release --framework net8.0 --function-architecture arm64" />

<Exec WorkingDirectory="..\CustomRuntimeAspNetCoreMinimalApiTest" Command="dotnet restore" />
<Exec WorkingDirectory="..\CustomRuntimeAspNetCoreMinimalApiTest" Condition="'$(Architecture)'=='' or '$(Architecture)'=='x86'" Command="dotnet lambda package -c Release --framework net6.0" />
<Exec WorkingDirectory="..\CustomRuntimeAspNetCoreMinimalApiTest" Condition="'$(Architecture)'=='arm64'" Command="dotnet lambda package -c Release --framework net6.0 --function-architecture arm64" />

<Exec WorkingDirectory="..\CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest" Command="dotnet restore" />
<Exec WorkingDirectory="..\CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest" Condition="'$(Architecture)'=='' or '$(Architecture)'=='x86'" Command="dotnet lambda package -c Release --framework net6.0" />
<Exec WorkingDirectory="..\CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest" Condition="'$(Architecture)'=='arm64'" Command="dotnet lambda package -c Release --framework net6.0 --function-architecture arm64" />
</Target>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

namespace Amazon.Lambda.RuntimeSupport.IntegrationTests
{
[Collection("Integration Tests")]
public class CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest : BaseCustomRuntimeTest
{
public CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

namespace Amazon.Lambda.RuntimeSupport.IntegrationTests
{
[Collection("Integration Tests")]
public class CustomRuntimeAspNetCoreMinimalApiTest : BaseCustomRuntimeTest
{
public CustomRuntimeAspNetCoreMinimalApiTest()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

namespace Amazon.Lambda.RuntimeSupport.IntegrationTests
{
[Collection("Integration Tests")]
public class CustomRuntimeNET6Tests : CustomRuntimeTests
{
public CustomRuntimeNET6Tests()
Expand All @@ -49,6 +50,7 @@ public async Task TestAllNET6HandlersAsync()
}
}

[Collection("Integration Tests")]
public class CustomRuntimeNET8Tests : CustomRuntimeTests
{
public CustomRuntimeNET8Tests()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Amazon.Lambda.RuntimeSupport.IntegrationTests.Helpers;

public static class CommandLineWrapper
{
public static async Task Run(string command, string arguments, string workingDirectory, CancellationToken cancellationToken = default)
{
var processStartInfo = new ProcessStartInfo
{
FileName = command,
Arguments = arguments,
WorkingDirectory = workingDirectory,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};

using (var process = new Process { StartInfo = processStartInfo, EnableRaisingEvents = true })
{
var tcs = new TaskCompletionSource<bool>();

// Handle process exit event
process.Exited += (sender, args) =>
{
tcs.TrySetResult(true);
};

try
{
// Attach event handlers
process.OutputDataReceived += (sender, args) =>
{
if (!string.IsNullOrEmpty(args.Data))
{
Console.WriteLine(args.Data);
}
};

process.ErrorDataReceived += (sender, args) =>
{
if (!string.IsNullOrEmpty(args.Data))
{
Console.WriteLine(args.Data);
}
};

// Start the process
process.Start();

// Begin asynchronous read operations
process.BeginOutputReadLine();
process.BeginErrorReadLine();

// Wait for the process to exit or cancellation
var completedTask = await Task.WhenAny(tcs.Task, Task.Delay(Timeout.Infinite, cancellationToken));

if (completedTask == tcs.Task)
{
// Process exited normally
await tcs.Task; // Just to propagate any exceptions
}
else
{
// Cancellation requested
if (!process.HasExited)
{
process.Kill();
}
throw new OperationCanceledException(cancellationToken);
}
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex);
if (!process.HasExited)
{
process.Kill();
}
}

Assert.True(process.ExitCode == 0, $"Command '{command} {arguments}' failed.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace Amazon.Lambda.RuntimeSupport.IntegrationTests.Helpers;

public static class LambdaToolsHelper
{
private static readonly string FunctionArchitecture = RuntimeInformation.OSArchitecture == System.Runtime.InteropServices.Architecture.Arm64 ? "arm64" : "x86_64";

public static string GetTempTestAppDirectory(string workingDirectory, string testAppPath)
{
var customTestAppPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(customTestAppPath);

var currentDir = new DirectoryInfo(workingDirectory);
CopyDirectory(currentDir, customTestAppPath);

return Path.Combine(customTestAppPath, testAppPath);
}

public static async Task<string> InstallLambdaTools()
{
var customToolPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(customToolPath);
await CommandLineWrapper.Run(
"dotnet",
$"tool install Amazon.Lambda.Tools --tool-path {customToolPath}",
Directory.GetCurrentDirectory());
return customToolPath;
}

public static async Task LambdaPackage(string toolPath, string framework, string workingDirectory)
{
string lambdaToolPath = Path.Combine(toolPath, "dotnet-lambda");
await CommandLineWrapper.Run(
lambdaToolPath,
$"package -c Release --framework {framework} --function-architecture {FunctionArchitecture}",
workingDirectory);
}

public static void CleanUp(string toolPath)
{
if (!string.IsNullOrEmpty(toolPath) && Directory.Exists(toolPath))
{
Directory.Delete(toolPath, true);
}
}

/// <summary>
/// <see cref="https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories"/>
/// </summary>
private static void CopyDirectory(DirectoryInfo dir, string destDirName)
{
if (!dir.Exists)
{
throw new DirectoryNotFoundException($"Source directory does not exist or could not be found: {dir.FullName}");
}

var dirs = dir.GetDirectories();

Directory.CreateDirectory(destDirName);

var files = dir.GetFiles();
foreach (var file in files)
{
var tempPath = Path.Combine(destDirName, file.Name);
file.CopyTo(tempPath, false);
}

foreach (var subdir in dirs)
{
var tempPath = Path.Combine(destDirName, subdir.Name);
var subDir = new DirectoryInfo(subdir.FullName);
CopyDirectory(subDir, tempPath);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Xunit;

namespace Amazon.Lambda.RuntimeSupport.IntegrationTests;

[CollectionDefinition("Integration Tests")]
public class IntegrationTestCollection : ICollectionFixture<IntegrationTestFixture>
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Amazon.Lambda.RuntimeSupport.IntegrationTests.Helpers;
using Xunit;

namespace Amazon.Lambda.RuntimeSupport.IntegrationTests;

public class IntegrationTestFixture : IAsyncLifetime
{
private readonly List<string> _tempPaths = new();

public async Task InitializeAsync()
{
var testAppPath = LambdaToolsHelper.GetTempTestAppDirectory(
"../../../../../../..",
"Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/CustomRuntimeFunctionTest");
var toolPath = await LambdaToolsHelper.InstallLambdaTools();
_tempPaths.AddRange([testAppPath, toolPath] );
await LambdaToolsHelper.LambdaPackage(toolPath, "net6.0", testAppPath);
await LambdaToolsHelper.LambdaPackage(toolPath, "net8.0", testAppPath);

testAppPath = LambdaToolsHelper.GetTempTestAppDirectory(
"../../../../../../..",
"Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/CustomRuntimeAspNetCoreMinimalApiTest");
toolPath = await LambdaToolsHelper.InstallLambdaTools();
_tempPaths.AddRange([testAppPath, toolPath] );
await LambdaToolsHelper.LambdaPackage(toolPath, "net6.0", testAppPath);

testAppPath = LambdaToolsHelper.GetTempTestAppDirectory(
"../../../../../../..",
"Libraries/test/Amazon.Lambda.RuntimeSupport.Tests/CustomRuntimeAspNetCoreMinimalApiCustomSerializerTest");
toolPath = await LambdaToolsHelper.InstallLambdaTools();
_tempPaths.AddRange([testAppPath, toolPath] );
await LambdaToolsHelper.LambdaPackage(toolPath, "net6.0", testAppPath);
}

public Task DisposeAsync()
{
foreach (var tempPath in _tempPaths)
{
LambdaToolsHelper.CleanUp(tempPath);
}

return Task.CompletedTask;
}
}
4 changes: 2 additions & 2 deletions buildtools/build.proj
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,8 @@
<Exec Command="dotnet test -c $(Configuration)" WorkingDirectory="..\Libraries\test\Amazon.Lambda.Annotations.SourceGenerators.Tests"/>
</Target>
<Target Name="run-integ-tests">
<Exec Command="dotnet test -c $(Configuration)" WorkingDirectory="..\Libraries\test\Amazon.Lambda.RuntimeSupport.Tests\Amazon.Lambda.RuntimeSupport.IntegrationTests"/>
<Exec Command="dotnet test -c $(Configuration)" WorkingDirectory="..\Libraries\test\TestServerlessApp.IntegrationTests"/>
<Exec Command="dotnet test -c $(Configuration) --logger &quot;console;verbosity=detailed&quot;" WorkingDirectory="..\Libraries\test\Amazon.Lambda.RuntimeSupport.Tests\Amazon.Lambda.RuntimeSupport.IntegrationTests"/>
<Exec Command="dotnet test -c $(Configuration) --logger &quot;console;verbosity=detailed&quot;" WorkingDirectory="..\Libraries\test\TestServerlessApp.IntegrationTests"/>
</Target>
<Target Name="create-nuget-packages-cicd" DependsOnTargets="build-project-packages">
<Exec Command="$(PackCommand)" WorkingDirectory="..\Libraries\src\%(LibraryName.FileName)"/>
Expand Down
3 changes: 0 additions & 3 deletions buildtools/ci.buildspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ phases:
commands:
# The tests need .NET 3.1, 6 and 8. .NET6 is installed by default. .NET8 is added in the runtime-versions. .NET 3.1 is installed manually.
- curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 3.1
# Mono is needed to run the unit tests on Linux
- curl https://download.mono-project.com/repo/centos8-stable.repo | tee /etc/yum.repos.d/mono-stable.repo
- dnf install -y mono-complete mono-devel
build:
commands:
- dotnet msbuild buildtools/build.proj /t:unit-tests /p:Cicd=true
Expand Down
Loading