Skip to content

[tests] Extract support for building tests outside the repo into re-usable props+targets#4923

Merged
radical merged 23 commits intomicrosoft:mainfrom
radical:tests-out-of-repo-targets
Jul 17, 2024
Merged

[tests] Extract support for building tests outside the repo into re-usable props+targets#4923
radical merged 23 commits intomicrosoft:mainfrom
radical:tests-out-of-repo-targets

Conversation

@radical
Copy link
Copy Markdown
Member

@radical radical commented Jul 16, 2024

What

Extract bits required for running tests outside of repo into Aspire.RepoTesting.{props,targets}. And consolidate the various support bits (tasks/targets) spread around the repo. When running tests outside the repo they need some extra msbuild bits to allow building with aspire nugets instead of direct ProjectReferences to source projects.

Why

There is a need to have some other projects run out-of-repo too, for example, Aspire.Hosting.Testing.Tests, and upcoming Aspire.Playground.Tests.

Details

This provides support for running tests outside of the repo, for example on a helix agent.

  • For this you need the source of the tests, and any dependencies.

  • Instead of direct ProjectReferences to the various Aspire hosting, and component projects use
    @(AspireProjectOrPackageReference).

    • These are converted to ProjectReference when $(TestsRunningOutsideOfRepo) != true.
    • But converted to PackageReference when $(TestsRunningOutsideOfRepo) == true.
  • To allow building such test projects, the build is isolated and patched to build outside the
    repo by adding appropriate Directory.Build.{props,targets}, and Directory.Packages.props

    • and using a custom nuget.config which resolves the Aspire packages from the locally built packages
    • and a Directory.Packages.Versions.props is generated with PackageVersions taken from the repo
      • This also adds properties named in @(PropertyForHelixRun) from the repo, like $(NetCurrent)

Also:

  • Update other project to track the new changes
Microsoft Reviewers: Open in CodeFlow

radical added 11 commits July 16, 2024 15:31
…evant properties, and remove old custom tasks
.. `Aspire.Testing.Repo.{props,targets}`.

From the comments:
```
    This provides support for running tests outside of the repo, for example on a helix agent.
    - For this you need the source of the tests, and any dependencies.
    - Instead of direct `ProjectReferences` to the various Aspire hosting, and component projects use
      `@(ComponentReferenceForTests)`, and @(NonComponentReferenceForTests)`.
      - These are converted to `ProjectReference` when `$(TestsRunningOutsideOfRepo) != true`.
      - But converted to `PackageReference` when `$(TestsRunningOutsideOfRepo) == true`.

    - To allow building such test projects, the build is isolated and patched to build outside the
      repo by adding appropriate `Directory.Build.{props,targets}`, and `Directory.Packages.props`
      - and using a custom `nuget.config` which resolves the Aspire packages from the locally built packages
      - and a `Directory.Packages.Versions.props` is generated with PackageVersions taken from the repo
        - This also adds properties named in `@(PropertyForHelixRun)` from the repo, like `$(NetCurrent)`
```
.. which were there essentially to import Hosting.props/targets .
Instead, now the `Aspire.Testing.Repo.{props,targets}` are imported by
default, which decide how to get the hosting targets.
- `TestsRunningOutsideOfRepo` now means that the whole project builds
  from source outside the repo.
  - But for `EndToEnd` tests only supporting test project is built in
    that mode.
  - So, update the property and the corresponding `#define` to reflect
    that

- And track changes from adding the new props/targets
@radical radical force-pushed the tests-out-of-repo-targets branch from 8616c15 to 1fe6b17 Compare July 16, 2024 19:34
@radical radical marked this pull request as ready for review July 16, 2024 20:11
@radical radical requested review from eerhardt, joperezr and sebastienros and removed request for eerhardt, joperezr and mitchdenny July 16, 2024 20:11
Copy link
Copy Markdown
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions

Comment on lines +10 to +11
<TestsSharedDir>$([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory), 'tests', 'Shared'))</TestsSharedDir>
<TestsSharedRepoTestingDir>$([MSBuild]::NormalizeDirectory($(TestsSharedDir), 'RepoTesting'))</TestsSharedRepoTestingDir>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) can/should these go into tests/Directory.Build.props?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to put these here to be accessible from playground apps, which would get similar changes to remove the hosting targets import.

Comment on lines +34 to +35
<!-- Used for running one helix job per test class -->
<Target Name="_ExtractTestClassNames"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we want one helix job per test class? That seems extreme, isn't it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used for workload tests, where the tests are expensive. I'm just making it available for other projects.


<ItemGroup Condition="'$(_BuildForTestsRunningOutsideOfRepo)' == 'true'">
<None Include="..\testproject\**\*" Link="testassets\testproject\%(RecursiveDir)%(FileName)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
<None Include="..\.editorconfig" Link="testassets\%(FileName)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should copying the tests.editorconfig be part of the "infra"?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exists in main. IIRC, this was become some test seemed to indirectly depend on this file being available. So, we are copying it here as part of preparing the output dir to be usable for out-of-repo builds.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I didn't hit a need for this other projects that use out-of-repo (like future playground, and hosting.testing tests).

@@ -1,8 +1,7 @@
<Project>
<PropertyGroup>
<IsAspireHost>true</IsAspireHost>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to make it so this stays defined in the .csproj?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can keep it there also! It is needed basically for this in tests/Shared/RepoTesting/Aspire.Testing.Repo.props which gets imported early - before the project file itself:

  <!-- IsAspireHost property might not be available here, so allow infering that from the project name -->
  <ImportGroup Condition="'$(RepoRoot)' != 'null' and '$(TestsRunningOutsideOfRepo)' != 'true' and ($(MSBuildProjectName.EndsWith('AppHost')) or '$(IsAspireHost)' == 'true')">
    <Import Project="$(RepoRoot)src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.props" Condition="Exists('$(RepoRoot)src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.props')" />
  </ImportGroup>

We are using ($(MSBuildProjectName.EndsWith('AppHost')) or '$(IsAspireHost)' == 'true') to determine whether it is an aspire host or not. And in this case the name does not help. Open to any suggestions of course.

</ItemGroup>

<ItemGroup Label="For tests">
<PackageVersion Include="xunit" Version="$(XunitVersion)" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are the xunit packages needed to be defined here, but not any other external packages?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a test project is built in the repo, Arcade adds these references automatically. But when we are building outside the repo, the Arcade targets are not used, thus we need to explicitly add these.

Any other external packages needed by a project would be added by those projects themselves. We add PackageVersion for them in ./tests/Shared/RepoTesting/Directory.Packages.Helix.props. Hm this should be added in a common README.md too.

<PackageReference Include="Aspire.Hosting.SqlServer" />
<PackageReference Include="Aspire.Hosting.Valkey" />
<ItemGroup>
<NonComponentReferenceForTests Include="Aspire.Hosting.Azure" IsAspireProjectResource="false" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this named NonComponentReferenceForTests? What does "Component" matter?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lack of a better name! Please suggest one :)

Copy link
Copy Markdown
Member

@eerhardt eerhardt Jul 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe drop Component and just have ReferenceForTests. Or OutsideOfRepoReference?

AbstractReference

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some offline discussion with @eerhardt I was able to combine the two into a single item AspireProjectOrPackageReference. Feel free to suggest a different name though :)

radical added 5 commits July 16, 2024 19:16
…gets

# Conflicts:
#	tests/testproject/TestProject.AppHost/TestProject.AppHost.csproj
…gets

# Conflicts:
#	tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj
@radical radical requested a review from eerhardt July 17, 2024 18:04
Copy link
Copy Markdown
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment on lines +18 to +19
<_BuildForTestsRunningOutsideOfRepo Condition="'$(TestsRunningOutsideOfRepo)' == 'true' or '$(ContinuousIntegrationBuild)' == 'true'">true</_BuildForTestsRunningOutsideOfRepo>
<DeployOutsideOfRepoSupportFiles>$(_BuildForTestsRunningOutsideOfRepo)</DeployOutsideOfRepoSupportFiles>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<_BuildForTestsRunningOutsideOfRepo Condition="'$(TestsRunningOutsideOfRepo)' == 'true' or '$(ContinuousIntegrationBuild)' == 'true'">true</_BuildForTestsRunningOutsideOfRepo>
<DeployOutsideOfRepoSupportFiles>$(_BuildForTestsRunningOutsideOfRepo)</DeployOutsideOfRepoSupportFiles>
<DeployOutsideOfRepoSupportFilesCondition="'$(TestsRunningOutsideOfRepo)' == 'true' or '$(ContinuousIntegrationBuild)' == 'true'">true</DeployOutsideOfRepoSupportFiles>

Why do we need 2 separate properties for the same thing?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition is needed in multiple places, and it clarifies that this project is being built for running tests out-of-repo vs project itself being prepared to run out of repo.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging the PR, but if we decide for a change here then I can do it in the follow up.

<clear />
<add key="built-local" value="%BUILT_NUGETS_PATH%" />
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What comes from here?

Copy link
Copy Markdown
Member Author

@radical radical Jul 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't recall why I had to add that one :( I will remove this in the follow up PR, and see what fails.
Also, with using a single nuget.org for sources this will coming from the root nuget.config .

<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)*.cs" Link="WorkloadTestingCommon" />

<_DataFile Include="$(MSBuildThisFileDirectory)data\**\*" Link="testassets\%(RecursiveDir)%(FileName)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call this something other than data? That doesn't seem like a great name.

TestSupportFiles maybe? I see we call it TestAssets elsewhere. Maybe that would be a good name for the folder in the repo as well?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added as follow up in #4946

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the 3rd time we have a nuget.config copied in the repo. Is there a way to use the root and just tweak it in places we need it to be special?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we had a task doing that earlier because we needed to put in the path to the built nugets. But now we use %BUILT_NUGETS_PATH% as the source in the nuget.config for tests, so it always resolves it from the environment variable.

I'll bring the task back to get the sources from the root nuget.config, add the source for built-nugets, and add the package source mapping as needed.
Added this as follow up work - #4946 .

@radical radical merged commit 8605f40 into microsoft:main Jul 17, 2024
@radical radical deleted the tests-out-of-repo-targets branch July 17, 2024 21:50
@github-actions github-actions bot locked and limited conversation to collaborators Aug 17, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-integrations Issues pertaining to Aspire Integrations packages testing ☑️

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants