diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs index 752ab0126453..c79b3575760f 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs @@ -1058,6 +1058,85 @@ private void GetKnownILCompilerPackVersion(TestAsset testAsset, string targetFra .Single(); } + [RequiresMSBuildVersionFact("17.0.0.32901")] + public void NativeAot_app_publishes_with_app_and_package_satellite_assemblies() + { + var projectName = "NativeAotAppWithSatellites"; + var testProject = CreateHelloWorldTestProject(ToolsetInfo.CurrentTargetFramework, projectName, true); + testProject.RecordProperties("NETCoreSdkPortableRuntimeIdentifier"); + testProject.AdditionalProperties["PublishAot"] = "true"; + testProject.AdditionalProperties["SatelliteResourceLanguages"] = "fr;de"; + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + testProject.AdditionalProperties["StripSymbols"] = "true"; + } + testProject.PackageReferences.Add(new TestPackageReference("System.Spatial", "5.8.3")); + + // Override Program.cs to look up a French resource string at runtime + testProject.SourceFiles[$"{projectName}.cs"] = $@" +using System; +using System.Globalization; +using System.Resources; +class Test +{{ + static int Main() + {{ + var rm = new ResourceManager(""{projectName}.Strings"", typeof(Test).Assembly); + var value = rm.GetString(""HelloWorld"", CultureInfo.GetCultureInfo(""fr"")); + Console.WriteLine(value); + return value == ""Bonjour"" ? 0 : 1; + }} +}}"; + + // Neutral-culture fallback resource + testProject.EmbeddedResources["Strings.resx"] = @" + + text/microsoft-resx + 2.0 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Hello +"; + + // French satellite resource + testProject.EmbeddedResources["Strings.fr.resx"] = @" + + text/microsoft-resx + 2.0 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Bonjour +"; + + var testAsset = TestAssetsManager.CreateTestProject(testProject); + var publishCommand = new PublishCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + publishCommand + .Execute("/p:UseCurrentRuntimeIdentifier=true", "/p:SelfContained=true") + .Should().Pass(); + + var buildProperties = testProject.GetPropertyValues(testAsset.TestRoot, ToolsetInfo.CurrentTargetFramework); + var rid = buildProperties["NETCoreSdkPortableRuntimeIdentifier"]; + var publishDirectory = publishCommand.GetOutputDirectory(targetFramework: ToolsetInfo.CurrentTargetFramework, runtimeIdentifier: rid).FullName; + + // NativeAOT embeds app-defined satellite assemblies into the native binary, so they + // should NOT appear as separate files in the publish directory (dotnet/runtime#124191). + File.Exists(Path.Combine(publishDirectory, "fr", $"{projectName}.resources.dll")).Should().BeFalse("fr app satellite should be embedded, not published separately"); + + // Satellite for a non-selected culture (ja) should be absent + File.Exists(Path.Combine(publishDirectory, "ja", "System.Spatial.resources.dll")).Should().BeFalse("ja System.Spatial satellite should be filtered out"); + + // Published output should be a native image + var publishedExe = Path.Combine(publishDirectory, $"{projectName}{Constants.ExeSuffix}"); + File.Exists(publishedExe).Should().BeTrue(); + IsNativeImage(publishedExe).Should().BeTrue(); + + // Running the native image should resolve the French resource correctly + new RunExeCommand(Log, publishedExe) + .Execute() + .Should().Pass() + .And.HaveStdOutContaining("Bonjour"); + } + private void CheckIlcVersions(TestAsset testAsset, string targetFramework, string rid, string expectedVersion, bool useRuntimePackLayout) { // Compiler version matches expected version