[mono] Add functional tests for the supported platforms (#46286)
authorMaxim Lipnin <v-maxlip@microsoft.com>
Wed, 6 Jan 2021 19:01:22 +0000 (22:01 +0300)
committerGitHub <noreply@github.com>
Wed, 6 Jan 2021 19:01:22 +0000 (14:01 -0500)
Part of https://github.com/dotnet/runtime/issues/43865

The functional tests:

- live under src/tests/FunctionalTests directory.
- are built as a part of the library tests build.
- are isolated from the build/test setup used for the tests in src/tests
- run on CI

41 files changed:
src/libraries/sendtohelixhelp.proj
src/libraries/tests.proj
src/mono/netcore/sample/Android/AndroidSampleApp.csproj
src/mono/netcore/sample/wasm/browser/Makefile
src/mono/netcore/sample/wasm/browser/Wasm.Browser.Sample.csproj [moved from src/mono/netcore/sample/wasm/browser/WasmSample.csproj with 91% similarity]
src/mono/netcore/sample/wasm/browser/index.html
src/mono/netcore/sample/wasm/console/Makefile
src/mono/netcore/sample/wasm/console/Wasm.Console.Sample.csproj [moved from src/mono/netcore/sample/wasm/console/WasmSample.csproj with 95% similarity]
src/mono/wasm/build/WasmApp.targets
src/tests/Common/dirs.proj
src/tests/FunctionalTests/Android/Emulator/AOT/README.md [new file with mode: 0644]
src/tests/FunctionalTests/Android/Emulator/Interpreter/Android.Emulator.Interpreter.Test.csproj [new file with mode: 0644]
src/tests/FunctionalTests/Android/Emulator/Interpreter/Program.cs [new file with mode: 0644]
src/tests/FunctionalTests/Android/common.props [new file with mode: 0644]
src/tests/FunctionalTests/Directory.Build.props [new file with mode: 0644]
src/tests/FunctionalTests/Directory.Build.targets [new file with mode: 0644]
src/tests/FunctionalTests/README.md [new file with mode: 0644]
src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs [new file with mode: 0644]
src/tests/FunctionalTests/iOS/Simulator/AOT/README.md [new file with mode: 0644]
src/tests/FunctionalTests/iOS/Simulator/AOT/iOS.Simulator.Aot.Test.csproj [new file with mode: 0644]
src/tests/FunctionalTests/iOS/Simulator/AOT/main.m [new file with mode: 0644]
src/tests/FunctionalTests/iOS/Simulator/Interpreter/Program.cs [new file with mode: 0644]
src/tests/FunctionalTests/iOS/Simulator/Interpreter/README.md [new file with mode: 0644]
src/tests/FunctionalTests/iOS/Simulator/Interpreter/iOS.Simulator.Interpreter.Test.csproj [new file with mode: 0644]
src/tests/FunctionalTests/iOS/Simulator/Interpreter/main.m [new file with mode: 0644]
src/tests/FunctionalTests/iOS/common.props [new file with mode: 0644]
src/tests/FunctionalTests/iOS/common.targets [new file with mode: 0644]
src/tests/FunctionalTests/wasm/AOT/browser/Program.cs [new file with mode: 0644]
src/tests/FunctionalTests/wasm/AOT/browser/Wasm.Aot.Browser.Test.csproj [new file with mode: 0644]
src/tests/FunctionalTests/wasm/AOT/browser/index.html [new file with mode: 0644]
src/tests/FunctionalTests/wasm/AOT/browser/runtime.js [new file with mode: 0644]
src/tests/FunctionalTests/wasm/AOT/console/Program.cs [new file with mode: 0644]
src/tests/FunctionalTests/wasm/AOT/console/Wasm.Aot.Console.Test.csproj [new file with mode: 0644]
src/tests/FunctionalTests/wasm/Interpreter/browser/Program.cs [new file with mode: 0644]
src/tests/FunctionalTests/wasm/Interpreter/browser/Wasm.Interpreter.Browser.Test.csproj [new file with mode: 0644]
src/tests/FunctionalTests/wasm/Interpreter/browser/index.html [new file with mode: 0644]
src/tests/FunctionalTests/wasm/Interpreter/browser/runtime.js [new file with mode: 0644]
src/tests/FunctionalTests/wasm/Interpreter/console/Program.cs [new file with mode: 0644]
src/tests/FunctionalTests/wasm/Interpreter/console/Wasm.Interpreter.Console.Test.csproj [new file with mode: 0644]
src/tests/FunctionalTests/wasm/common.props [new file with mode: 0644]
src/tests/FunctionalTests/wasm/common.targets [new file with mode: 0644]

index 6f29d17..3f29c4b 100644 (file)
     </XHarnessAppBundleToTest>
   </ItemDefinitionGroup>
 
+  <ItemDefinitionGroup Condition="'$(TargetOS)' == 'Android'">
+    <XHarnessApkToTest>
+        <Targets Condition="'$(TargetArchitecture)' == 'arm'">armeabi-v7a</Targets>
+        <Targets Condition="'$(TargetArchitecture)' == 'arm64'">arm64-v8a</Targets>
+        <Targets Condition="'$(TargetArchitecture)' == 'x64'">x86_64</Targets>
+        <Targets Condition="'$(TargetArchitecture)' == 'x86'">x86</Targets>
+        <AndroidInstrumentationName>net.dot.MonoRunner</AndroidInstrumentationName>
+        <TestTimeout>$(_workItemTimeout)</TestTimeout>
+    </XHarnessApkToTest>
+  </ItemDefinitionGroup>
   <!--
     Create all the Helix data to start a set of jobs. Create a set of work items, one for each libraries
     test assembly. All will have the same command line. Note that this target is listed in the
     </ItemGroup>
 
     <ItemGroup Condition="'$(TargetOS)' == 'Android'">
-      <!-- We have to define this temp item list because of a bug in MSBuild that would prevent access %(Filename) right away -->
       <_apks Include="$(TestArchiveTestsRoot)**/*.apk" />
       <XHarnessApkToTest Include="@(_apks)">
-        <Targets Condition="'$(TargetArchitecture)' == 'arm'">armeabi-v7a</Targets>
-        <Targets Condition="'$(TargetArchitecture)' == 'arm64'">arm64-v8a</Targets>
-        <Targets Condition="'$(TargetArchitecture)' == 'x64'">x86_64</Targets>
-        <Targets Condition="'$(TargetArchitecture)' == 'x86'">x86</Targets>
         <AndroidPackageName>net.dot.%(Filename)</AndroidPackageName>
-        <AndroidInstrumentationName>net.dot.MonoRunner</AndroidInstrumentationName>
-
-        <TestTimeout>$(_workItemTimeout)</TestTimeout>
+      </XHarnessApkToTest>
+      <_runonlyApks Include="$(TestArchiveRoot)runonly/**/*.apk" />
+      <XHarnessApkToTest Include="@(_runonlyApks)">
+        <AndroidPackageName>net.dot.%(Filename)</AndroidPackageName>
         <!-- The android sample returns 42 so it should be considered by xharness as a success -->
-        <ExpectedExitCode Condition="'%(Filename)' == 'HelloAndroid'">42</ExpectedExitCode>
+        <ExpectedExitCode>42</ExpectedExitCode>
       </XHarnessApkToTest>
     </ItemGroup>
 
       <HelixWorkItem Include="@(_RunOnlyWorkItem -> '%(FileName)')" >
         <PayloadArchive>%(Identity)</PayloadArchive>
         <!-- No RunTests script generated for the sample project so we just use the direct command -->
-        <Command>dotnet exec $XHARNESS_CLI_PATH wasm $XHARNESS_COMMAND  --app=. --engine=V8  --engine-arg=--stack-trace-limit=1000 --js-file=runtime.js --output-directory=$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output -- --run WasmSample.dll</Command>
+        <Command>dotnet exec $XHARNESS_CLI_PATH wasm $XHARNESS_COMMAND  --app=. --engine=V8  --engine-arg=--stack-trace-limit=1000 --js-file=runtime.js --output-directory=$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output -- --run %(FileName).dll</Command>
       </HelixWorkItem>
     </ItemGroup>
 
       <HelixWorkItem Include="@(_RunOnlyWorkItem -> '%(FileName)')" >
         <PayloadArchive>%(Identity)</PayloadArchive>
         <!-- No RunTests script generated for the sample project so we just use the direct command -->
-        <Command>dotnet exec $XHARNESS_CLI_PATH wasm $XHARNESS_COMMAND  --app=. --browser=Chrome  --html-file=index.html --output-directory=$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output -- WasmSample.dll --testing</Command>
+        <Command>dotnet exec $XHARNESS_CLI_PATH wasm $XHARNESS_COMMAND  --app=. --browser=Chrome  --html-file=index.html --output-directory=$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output -- %(FileName).dll --testing</Command>
       </HelixWorkItem>
     </ItemGroup>
 
index 782db42..9783f7c 100644 (file)
                       Exclude="@(ProjectExclusions)"
                       BuildInParallel="false"
                       Condition="'$(TestTrimming)' == 'true'" />
-    <ProjectReference Include="$(MonoProjectRoot)netcore\sample\Android\AndroidSampleApp.csproj"
-                      BuildInParallel="false"
-                      Condition="'$(ArchiveTests)' == 'true' and '$(TargetOS)' == 'Android'" />
+  </ItemGroup>
+
+  <ItemGroup Condition="'$(ArchiveTests)' == 'true' and ('$(TargetOS)' == 'iOS' or '$(TargetOS)' == 'tvOS')">
     <ProjectReference Include="$(MonoProjectRoot)netcore\sample\iOS\Program.csproj"
-                      BuildInParallel="false"
-                      Condition="'$(ArchiveTests)' == 'true' and ('$(TargetOS)' == 'iOS' or '$(TargetOS)' == 'tvOS')" />
-    <ProjectReference Include="$(MonoProjectRoot)netcore\sample\wasm\**\WasmSample.csproj"
-                      BuildInParallel="false"
-                      Condition="'$(ArchiveTests)' == 'true' and '$(TargetOS)' == 'Browser'" />
+                      BuildInParallel="false" />
+    <ProjectReference Include="$(RepoRoot)\src\tests\FunctionalTests\iOS\**\*.Test.csproj"
+                      BuildInParallel="false" />
+  </ItemGroup>
+  
+  <ItemGroup Condition="'$(ArchiveTests)' == 'true' and '$(TargetOS)' == 'Android'">
+    <ProjectReference Include="$(MonoProjectRoot)netcore\sample\Android\AndroidSampleApp.csproj"
+                      BuildInParallel="false" />
+    <ProjectReference Include="$(RepoRoot)\src\tests\FunctionalTests\Android\**\*.Test.csproj"
+                      BuildInParallel="false" />
+  </ItemGroup>
+  
+  <ItemGroup Condition="'$(ArchiveTests)' == 'true' and '$(TargetOS)' == 'Browser'">
+    <ProjectReference Include="$(MonoProjectRoot)netcore\sample\wasm\**\*.Sample.csproj"
+                      BuildInParallel="false" />
+    <ProjectReference Include="$(RepoRoot)\src\tests\FunctionalTests\wasm\**\*.Test.csproj"
+                      BuildInParallel="false" />
   </ItemGroup>
 
   <Target Name="GenerateMergedCoverageReport"
index 42355b2..20806f7 100644 (file)
       <!-- AnyCPU as Platform-->
       <OSPlatformConfig>$(TargetOS).AnyCPU.$(Configuration)</OSPlatformConfig>
       <!-- <OSPlatformConfig>$(TargetOS).$(Platform).$(Configuration)</OSPlatformConfig> -->
-      <TestArchiveRoot>$(ArtifactsDir)helix/</TestArchiveRoot>
-      <TestArchiveTestsRoot>$(TestArchiveRoot)tests/</TestArchiveTestsRoot>
-      <TestArchiveTestsDir>$(TestArchiveTestsRoot)$(OSPlatformConfig)/</TestArchiveTestsDir>
+      <HelixArchiveRoot>$(ArtifactsDir)helix/</HelixArchiveRoot>
+      <HelixArchiveRunOnlyRoot>$(HelixArchiveRoot)runonly/</HelixArchiveRunOnlyRoot>
+      <HelixArchiveRunOnlyAppsDir>$(HelixArchiveRunOnlyRoot)$(OSPlatformConfig)/</HelixArchiveRunOnlyAppsDir>
     </PropertyGroup>
     <ItemGroup>
-      <_appFiles Include="$(ApkBundlePath)" />
+      <_apkFiles Include="$(ApkBundlePath)" />
     </ItemGroup>
-    <Copy SourceFiles="@(_appFiles)"
-          DestinationFolder="$(TestArchiveTestsDir)" />
+    <Copy SourceFiles="@(_apkFiles)"
+          DestinationFolder="$(HelixArchiveRunOnlyAppsDir)/%(RecursiveDir)" />
 
     <Message Importance="High" Text="ApkBundlePath: $(ApkBundlePath)"/>
     <Message Importance="High" Text="TestArchiveTestsDir: $(TestArchiveTestsDir)"/>
index f5d5713..8a243d4 100644 (file)
@@ -16,7 +16,7 @@ CONFIG?=Release
 all: build
 
 build:
-       EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) publish $(DOTNET_Q_ARGS) $(DOTNET_RUN_AOT_COMPILATION_ARGS) /p:Configuration=$(CONFIG) /p:TargetArchitecture=wasm /p:TargetOS=Browser $(MSBUILD_ARGS) WasmSample.csproj
+       EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) publish $(DOTNET_Q_ARGS) $(DOTNET_RUN_AOT_COMPILATION_ARGS) /p:Configuration=$(CONFIG) /p:TargetArchitecture=wasm /p:TargetOS=Browser $(MSBUILD_ARGS) Wasm.Browser.Sample.csproj
 
 clean:
        rm -rf bin
   <Target Name="PrepareForWasmBuild" BeforeTargets="WasmBuildApp">
     <PropertyGroup>
         <WasmAppDir>$(AppDir)</WasmAppDir>
-        <WasmAOTDir>$(MSBuildProjectDirectory)\$(PublishDir)\</WasmAOTDir>
-        <WasmMainAssemblyPath>$(MSBuildProjectDirectory)\bin\WasmSample.dll</WasmMainAssemblyPath>
+        <WasmBuildDir>$(MSBuildProjectDirectory)\$(PublishDir)\</WasmBuildDir>
         <WasmMainJSPath>runtime.js</WasmMainJSPath>
-        <WasmMainAssemblyPath>$(WasmAotDir)WasmSample.dll</WasmMainAssemblyPath>
+        <WasmMainAssemblyPath>$(WasmBuildDir)$(AssemblyName).dll</WasmMainAssemblyPath>
     </PropertyGroup>
     <ItemGroup>
       <WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
@@ -71,7 +70,7 @@
       <HelixArchiveRoot>$(ArtifactsDir)helix/</HelixArchiveRoot>
       <HelixArchiveRunOnlyRoot>$(HelixArchiveRoot)runonly/</HelixArchiveRunOnlyRoot>
       <HelixArchiveRunOnlyAppsDir>$(HelixArchiveRunOnlyRoot)$(OSPlatformConfig)/browser/</HelixArchiveRunOnlyAppsDir>
-      <ZippedApp>$(OutputPath)/WasmBrowserSample.zip</ZippedApp>
+      <ZippedApp>$(OutputPath)$(AssemblyName).zip</ZippedApp>
     </PropertyGroup>
     <ZipDirectory SourceDirectory="$(AppDir)" DestinationFile="$(ZippedApp)" />
     <Copy SourceFiles="$(ZippedApp)" DestinationFolder="$(HelixArchiveRunOnlyAppsDir)" />
index ad5cca3..3fc644b 100644 (file)
@@ -36,7 +36,7 @@
 
       var App = {
         init: function () {
-          var ret = BINDING.call_static_method("[WasmSample] Sample.Test:TestMeaning", []);
+          var ret = BINDING.call_static_method("[Wasm.Browser.Sample] Sample.Test:TestMeaning", []);
           document.getElementById("out").innerHTML = ret;
 
           if (is_testing)
index 7bf3273..f07b469 100644 (file)
@@ -17,10 +17,10 @@ CONFIG?=Release
 all: build
 
 build:
-       EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) publish $(DOTNET_Q_ARGS) $(DOTNET_RUN_AOT_COMPILATION_ARGS) $(DOTNET_EXTRA_ARGS) /p:TargetArchitecture=wasm /p:TargetOS=Browser /p:Configuration=$(CONFIG) $(MSBUILD_ARGS) WasmSample.csproj
+       EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) publish $(DOTNET_Q_ARGS) $(DOTNET_RUN_AOT_COMPILATION_ARGS) $(DOTNET_EXTRA_ARGS) /p:TargetArchitecture=wasm /p:TargetOS=Browser /p:Configuration=$(CONFIG) $(MSBUILD_ARGS) Wasm.Console.Sample.csproj
 
 clean:
        rm -rf bin
 
 run:
-       cd bin/$(CONFIG)/AppBundle && ~/.jsvu/v8 --expose_wasm runtime.js -- $(DOTNET_MONO_LOG_LEVEL) --run WasmSample.dll
+       cd bin/$(CONFIG)/AppBundle && ~/.jsvu/v8 --expose_wasm runtime.js -- $(DOTNET_MONO_LOG_LEVEL) --run Wasm.Console.Sample.dll
@@ -41,7 +41,7 @@
       <WasmAppDir>$(AppBundleDir)</WasmAppDir>
       <WasmBuildDir>$(MSBuildProjectDirectory)\$(PublishDir)\</WasmBuildDir>
       <WasmMainJSPath>$(MonoProjectRoot)\wasm\runtime-test.js</WasmMainJSPath>
-      <WasmMainAssemblyPath>$(WasmBuildDir)WasmSample.dll</WasmMainAssemblyPath>
+      <WasmMainAssemblyPath>$(WasmBuildDir)$(AssemblyName).dll</WasmMainAssemblyPath>
       <WasmGenerateRunV8Script>true</WasmGenerateRunV8Script>
     </PropertyGroup>
     <ItemGroup>
@@ -64,7 +64,7 @@
       <HelixArchiveRoot>$(ArtifactsDir)helix/</HelixArchiveRoot>
       <HelixArchiveRunOnlyRoot>$(HelixArchiveRoot)runonly/</HelixArchiveRunOnlyRoot>
       <HelixArchiveRunOnlyAppsDir>$(HelixArchiveRunOnlyRoot)$(OSPlatformConfig)/console/</HelixArchiveRunOnlyAppsDir>
-      <ZippedApp>$(OutputPath)/WasmConsoleSample.zip</ZippedApp>
+      <ZippedApp>$(OutputPath)$(AssemblyName).zip</ZippedApp>
     </PropertyGroup>
     <ZipDirectory SourceDirectory="$(AppBundleDir)" DestinationFile="$(ZippedApp)" />
     <Copy SourceFiles="$(ZippedApp)" DestinationFolder="$(HelixArchiveRunOnlyAppsDir)" />
index a3f2815..e8ee914 100644 (file)
@@ -48,7 +48,7 @@
       UseAotDataFile="false"
       AotModulesTablePath="$(WasmBuildDir)driver-gen.c"
       UseLLVM="true"
-        DisableParallelAot="true"
+      DisableParallelAot="true"
       LLVMPath="$(EMSDK_PATH)\upstream\bin">
       <Output TaskParameter="CompiledAssemblies" ItemName="_WasmAssemblies" />
     </MonoAOTCompiler>
     <ItemGroup>
       <_managedAppAssemblies Include="$(WasmAppDir)managed\*.dll"/>
     </ItemGroup>
-    <Exec Condition="'$(RunAOTCompilation)' == 'true'" Command="mono-cil-strip %(_managedAppAssemblies.Identity)" />
+    <!-- '$(ArchiveTests)' != 'true' is to skip on CI for now -->
+    <Exec Condition="'$(RunAOTCompilation)' == 'true' and '$(ArchiveTests)' != 'true'" Command="mono-cil-strip %(_managedAppAssemblies.Identity)" />
   </Target>
 
   <Target Name="_WasmBuildNative" DependsOnTargets="_WasmAotCompileApp" Condition="'$(WasmBuildNative)' == 'true'">
     <PropertyGroup>
       <EmccFlags>$(EmccFlags) -s DISABLE_EXCEPTION_CATCHING=0</EmccFlags>
       <EmccFlags Condition="'$(RunAOTCompilation)' == 'true'">$(EmccFlags) -DENABLE_AOT=1 -DDRIVER_GEN=1</EmccFlags>
+      <_EmsdkEnvSourceCommand>source $(EMSDK_PATH)/emsdk_env.sh</_EmsdkEnvSourceCommand>
+      <_EmccCommand>$(_EmsdkEnvSourceCommand) &amp;&amp; emcc</_EmccCommand>
     </PropertyGroup>
 
     <ReadLinesFromFile File="$(RuntimeEmccVersionFile)">
         <Output TaskParameter="Lines" PropertyName="RuntimeEmccVersion" />
     </ReadLinesFromFile>
 
-    <Exec Command="source $(EMSDK_PATH)/emsdk_env.sh &amp;&amp; emcc --version | head -1 > emcc-version.txt" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" />
+    <Exec Command="bash -c '$(_EmccCommand) --version | head -1 > emcc-version.txt'" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" />
     <ReadLinesFromFile File="$(EmccPublishVersionFile)">
         <Output TaskParameter="Lines" PropertyName="EmccVersion" />
     </ReadLinesFromFile>
    <PropertyGroup>
      <_WasmIncludeDir>$(MicrosoftNetCoreAppRuntimePackRidDir)native/include</_WasmIncludeDir>
      <_WasmSrcDir>$(MicrosoftNetCoreAppRuntimePackRidDir)native/src</_WasmSrcDir>
-     <_WasmEmcc>source $(EMSDK_PATH)/emsdk_env.sh &amp;&amp; emcc</_WasmEmcc>
      <EmccCFlags>$(EmccFlags) -DCORE_BINDINGS -DGEN_PINVOKE=1 -I$(WasmBuildDir) -I$(_WasmIncludeDir)/mono-2.0 -I$(_WasmIncludeDir)/wasm</EmccCFlags>
      <EmccLDFlags>$(EmccFlags) -s TOTAL_MEMORY=536870912</EmccLDFlags>
    </PropertyGroup>
 
-    <Exec Command="$(_WasmEmcc) $(EmccCFlags) $(_WasmSrcDir)/driver.c -c -o driver.o" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" StandardOutputImportance="Low" />
-    <Exec Command="$(_WasmEmcc) $(EmccCFlags) $(_WasmSrcDir)/corebindings.c -c -o corebindings.o" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" StandardOutputImportance="Low" />
-    <Exec Command="$(_WasmEmcc) $(EmccCFlags) $(_WasmSrcDir)/pinvoke.c -c -o pinvoke.o" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" StandardOutputImportance="Low" />
-    <Exec Command="$(_WasmEmcc) $(EmccLDFlags) --js-library $(_WasmSrcDir)/library_mono.js --js-library $(_WasmSrcDir)/binding_support.js --js-library $(_WasmSrcDir)/dotnet_support.js --js-library $(_WasmSrcDir)/pal_random.js @(_WasmAssemblies->'%(LlvmBitcodeFile)', ' ') @(_WasmObjects, ' ') -o dotnet.js" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" StandardOutputImportance="Low" />
-    <Exec Condition="'$(WasmNativeStrip)' == 'true'" Command="$(EMSDK_PATH)/upstream/bin/wasm-opt --strip-dwarf dotnet.wasm -o dotnet.wasm" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" />
+    <Exec Command="bash -c '$(_EmccCommand) $(EmccCFlags) $(_WasmSrcDir)/driver.c -c -o driver.o'" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" StandardOutputImportance="Low" />
+    <Exec Command="bash -c '$(_EmccCommand) $(EmccCFlags) $(_WasmSrcDir)/corebindings.c -c -o corebindings.o'" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" StandardOutputImportance="Low" />
+    <Exec Command="bash -c '$(_EmccCommand) $(EmccCFlags) $(_WasmSrcDir)/pinvoke.c -c -o pinvoke.o'" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" StandardOutputImportance="Low" />
+    <Exec Command="bash -c '$(_EmccCommand) $(EmccLDFlags) --js-library $(_WasmSrcDir)/library_mono.js --js-library $(_WasmSrcDir)/binding_support.js --js-library $(_WasmSrcDir)/dotnet_support.js --js-library $(_WasmSrcDir)/pal_random.js @(_WasmAssemblies->'%(LlvmBitcodeFile)', ' ') @(_WasmObjects, ' ') -o dotnet.js'" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" StandardOutputImportance="Low" />
+    <Exec Condition="'$(WasmNativeStrip)' == 'true'" Command="bash -c '$(EMSDK_PATH)/upstream/bin/wasm-opt --strip-dwarf dotnet.wasm -o dotnet.wasm'" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(WasmBuildDir)" />
   </Target>
 
   <Target Name="_GenerateRunV8Script">
index 27b5afa..e12d882 100644 (file)
@@ -9,6 +9,7 @@
     <ItemGroup>
       <DisabledProjects Include="$(TestRoot)*\**\cs_template.csproj" />
       <DisabledProjects Include="$(TestRoot)Common\**\*.*proj" />
+      <DisabledProjects Include="$(TestRoot)FunctionalTests\**\*.csproj" /> <!-- They need to be isolated from the existing setup -->
       <DisabledProjects Include="$(TestRoot)GC\Performance\Framework\GCPerfTestFramework.csproj" />
       <DisabledProjects Include="$(TestRoot)Loader\classloader\generics\regressions\DD117522\Test.csproj" />
       <DisabledProjects Include="$(TestRoot)Loader\classloader\generics\GenericMethods\VSW491668.csproj" /> <!-- issue 5501 -->
diff --git a/src/tests/FunctionalTests/Android/Emulator/AOT/README.md b/src/tests/FunctionalTests/Android/Emulator/AOT/README.md
new file mode 100644 (file)
index 0000000..55f5010
--- /dev/null
@@ -0,0 +1 @@
+TO-DO: add the test case for AOT mode when https://github.com/dotnet/runtime/pull/43535 has been completed.
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/Android/Emulator/Interpreter/Android.Emulator.Interpreter.Test.csproj b/src/tests/FunctionalTests/Android/Emulator/Interpreter/Android.Emulator.Interpreter.Test.csproj
new file mode 100644 (file)
index 0000000..496926a
--- /dev/null
@@ -0,0 +1,81 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <Import Project="..\..\common.props" />
+
+  <!-- Redirect 'dotnet publish' to in-tree runtime pack -->
+  <Target Name="TrickRuntimePackLocation" AfterTargets="ProcessFrameworkReferences">
+    <ItemGroup>
+      <RuntimePack>
+        <PackageDirectory>$(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration)</PackageDirectory>
+      </RuntimePack>
+    </ItemGroup>
+    <Message Text="Packaged ID: %(RuntimePack.PackageDirectory)" Importance="high" />
+  </Target>
+
+  <UsingTask TaskName="AndroidAppBuilderTask" AssemblyFile="$(AndroidAppBuilderTasksAssemblyPath)"/>
+
+  <Target Name="BuildApp" AfterTargets="CopyFilesToPublishDirectory">
+    <PropertyGroup>
+      <StripDebugSymbols>False</StripDebugSymbols>
+      <StripDebugSymbols Condition="'$(Configuration)' == 'Release'">True</StripDebugSymbols>
+      <AdbTool>$(ANDROID_SDK_ROOT)\platform-tools\adb</AdbTool>
+      <ApkDir>$(PublishDir)apk\</ApkDir>
+    </PropertyGroup>
+
+    <RemoveDir Directories="$(ApkDir)" />
+
+    <PropertyGroup>
+      <AndroidAbi Condition="'$(TargetArchitecture)' == 'arm64'">arm64-v8a</AndroidAbi>
+      <AndroidAbi Condition="'$(TargetArchitecture)' == 'arm'">armeabi-v7a</AndroidAbi>
+      <AndroidAbi Condition="'$(TargetArchitecture)' == 'x64'">x86_64</AndroidAbi>
+      <AndroidAbi Condition="'$(TargetArchitecture)' == 'x86'">x86</AndroidAbi>
+    </PropertyGroup>
+
+    <!-- TODO: delete once we switch to Android Crypto -->
+    <Copy Condition="'$(ANDROID_OPENSSL_AAR)' != ''"
+          SourceFiles="$(ANDROID_OPENSSL_AAR)\prefab\modules\crypto\libs\android.$(AndroidAbi)\libcrypto.so"
+          DestinationFolder="$(PublishDir)" SkipUnchangedFiles="true"/>
+    <Copy Condition="'$(ANDROID_OPENSSL_AAR)' != ''"
+          SourceFiles="$(ANDROID_OPENSSL_AAR)\prefab\modules\ssl\libs\android.$(AndroidAbi)\libssl.so"
+          DestinationFolder="$(PublishDir)" SkipUnchangedFiles="true"/>
+
+    <AndroidAppBuilderTask
+        RuntimeIdentifier="$(RuntimeIdentifier)"
+        SourceDir="$(PublishDir)"
+        ProjectName="Android.Emulator.Interpreter.Test"
+        ForceInterpreter="True"
+        MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)\native\include\mono-2.0"
+        MainLibraryFileName="Android.Emulator.Interpreter.Test.dll"
+        StripDebugSymbols="$(StripDebugSymbols)"
+        OutputDir="$(ApkDir)">
+        <Output TaskParameter="ApkBundlePath" PropertyName="ApkBundlePath" />
+        <Output TaskParameter="ApkPackageId" PropertyName="ApkPackageId" />
+    </AndroidAppBuilderTask>
+
+    <Message Importance="High" Text="Apk:       $(ApkBundlePath)"/>
+    <Message Importance="High" Text="PackageId: $(ApkPackageId)"/>
+  </Target>
+
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+  </ItemGroup>
+
+  <Target Name="CopySampleAppToHelixTestDir" 
+          AfterTargets="Build"
+          DependsOnTargets="Publish;BuildApp" >
+    <PropertyGroup>
+      <OSPlatformConfig>$(TargetOS).AnyCPU.$(Configuration)</OSPlatformConfig>
+      <HelixArchiveRoot>$(ArtifactsDir)helix/</HelixArchiveRoot>
+      <HelixArchiveRunOnlyRoot>$(HelixArchiveRoot)runonly/</HelixArchiveRunOnlyRoot>
+      <HelixArchiveRunOnlyAppsDir>$(HelixArchiveRunOnlyRoot)$(OSPlatformConfig)/</HelixArchiveRunOnlyAppsDir>
+    </PropertyGroup>
+    <ItemGroup>
+      <_apkFiles Include="$(ApkBundlePath)" />
+    </ItemGroup>
+    <Copy SourceFiles="@(_apkFiles)"
+          DestinationFolder="$(HelixArchiveRunOnlyAppsDir)/%(RecursiveDir)" />
+
+    <Message Importance="High" Text="ApkBundlePath: $(ApkBundlePath)"/>
+    <Message Importance="High" Text="TestArchiveTestsDir: $(TestArchiveTestsDir)"/>
+  </Target>
+</Project>
diff --git a/src/tests/FunctionalTests/Android/Emulator/Interpreter/Program.cs b/src/tests/FunctionalTests/Android/Emulator/Interpreter/Program.cs
new file mode 100644 (file)
index 0000000..7dcc0f3
--- /dev/null
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+public static class Program
+{
+    public static int Main(string[] args)
+    {
+        Console.WriteLine("Hello, Android!"); // logcat
+        return 42;
+    }
+}
diff --git a/src/tests/FunctionalTests/Android/common.props b/src/tests/FunctionalTests/Android/common.props
new file mode 100644 (file)
index 0000000..714af35
--- /dev/null
@@ -0,0 +1,13 @@
+<Project>
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <OutputPath>$(AssemblyBinDirOutputPath)</OutputPath>
+    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+    <TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
+    <EnableTargetingPackDownload>false</EnableTargetingPackDownload>
+    <RuntimeIdentifier>android-$(TargetArchitecture)</RuntimeIdentifier>
+    <PublishTrimmed>true</PublishTrimmed>
+    <TrimMode>Link</TrimMode>
+    <MicrosoftNetCoreAppRuntimePackDir>$(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration)\runtimes\android-$(TargetArchitecture)\</MicrosoftNetCoreAppRuntimePackDir>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/Directory.Build.props b/src/tests/FunctionalTests/Directory.Build.props
new file mode 100644 (file)
index 0000000..31c34b9
--- /dev/null
@@ -0,0 +1,9 @@
+<Project>
+  <PropertyGroup>
+    <RunAnalyzers>false</RunAnalyzers>
+    <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+
+  <Import Project="..\..\mono\Directory.Build.props" />
+</Project>
diff --git a/src/tests/FunctionalTests/Directory.Build.targets b/src/tests/FunctionalTests/Directory.Build.targets
new file mode 100644 (file)
index 0000000..1ceb64b
--- /dev/null
@@ -0,0 +1,3 @@
+<Project>
+  <Import Project="..\..\mono\Directory.Build.targets" />
+</Project>
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/README.md b/src/tests/FunctionalTests/README.md
new file mode 100644 (file)
index 0000000..08bc44e
--- /dev/null
@@ -0,0 +1,7 @@
+# Functional tests
+
+This directory contains the functional tests for the supported platforms.  
+
+Currently the functional tests build is incorporated into the library tests build.  
+
+The functional tests run in CI.  
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/AOT/Program.cs
new file mode 100644 (file)
index 0000000..e6f8b89
--- /dev/null
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+
+// it's not part of the BCL but runtime needs it for native-to-managed callbacks in AOT
+// To be replaced with NativeCallableAttribute
+public class MonoPInvokeCallbackAttribute : Attribute
+{
+    public MonoPInvokeCallbackAttribute(Type delegateType) { }
+}
+
+public static class Program
+{
+    // Defined in main.m
+    [DllImport("__Internal")]
+    private extern static void ios_set_text(string value);
+
+    [DllImport("__Internal")]
+    private extern static void ios_register_button_click(Action action);
+
+    private static Action buttonClickHandler = null;
+
+    private static int counter = 0;
+
+    // Called by native code, see main.m
+    [MonoPInvokeCallback(typeof(Action))]
+    private static void OnButtonClick()
+    {
+        ios_set_text("OnButtonClick! #" + counter++);
+    }
+
+    public static async Task<int> Main(string[] args)
+    {
+        // Register a managed callback (will be called by UIButton, see main.m)
+        // Also, keep the handler alive so GC won't collect it.
+        ios_register_button_click(buttonClickHandler = OnButtonClick);
+
+        const string msg = "Hello World!\n.NET 5.0";
+        for (int i = 0; i < msg.Length; i++)
+        {
+            // a kind of an animation
+            ios_set_text(msg.Substring(0, i + 1));
+            await Task.Delay(100);
+        }
+
+        Console.WriteLine("Done!");
+        await Task.Delay(5000);
+        return 42;
+    }
+}
diff --git a/src/tests/FunctionalTests/iOS/Simulator/AOT/README.md b/src/tests/FunctionalTests/iOS/Simulator/AOT/README.md
new file mode 100644 (file)
index 0000000..5bda04e
--- /dev/null
@@ -0,0 +1,4 @@
+# iOS functional test running on simulator in AOT mode
+
+Simulator: iPhone 11
+
diff --git a/src/tests/FunctionalTests/iOS/Simulator/AOT/iOS.Simulator.Aot.Test.csproj b/src/tests/FunctionalTests/iOS/Simulator/AOT/iOS.Simulator.Aot.Test.csproj
new file mode 100644 (file)
index 0000000..9cc1038
--- /dev/null
@@ -0,0 +1,66 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <Import Project="..\..\common.props" />
+
+  <Import Project="$(RepoTasksDir)AotCompilerTask\MonoAOTCompiler.props" />
+  <UsingTask TaskName="AppleAppBuilderTask"
+             AssemblyFile="$(AppleAppBuilderTasksAssemblyPath)" />
+
+  <UsingTask TaskName="MonoAOTCompiler"
+             AssemblyFile="$(MonoAOTCompilerTasksAssemblyPath)" />
+
+  <Target Name="BuildAppBundle" AfterTargets="CopyFilesToPublishDirectory">
+    <PropertyGroup>
+      <AppDir>$(PublishDir)\app</AppDir>
+      <IosSimulator Condition="'$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'x86'">iPhone 11</IosSimulator>
+      <Optimized Condition="'$(Configuration)' == 'Release'">True</Optimized>
+    </PropertyGroup>
+
+    <RemoveDir Directories="$(AppDir)" />
+
+    <ItemGroup>
+      <AotInputAssemblies Include="$(PublishDir)\*.dll">
+        <AotArguments>@(MonoAOTCompilerDefaultAotArguments, ';')</AotArguments>
+        <ProcessArguments>@(MonoAOTCompilerDefaultProcessArguments, ';')</ProcessArguments>
+      </AotInputAssemblies>
+    </ItemGroup>
+
+    <Message Importance="High" Text="Output publish dir: $(PublishDir)"/>
+
+    <MonoAOTCompiler
+        CompilerBinaryPath="$(MicrosoftNetCoreAppRuntimePackDir)native\cross\mono-aot-cross"
+        Mode="Full"
+        OutputType="AsmOnly"
+        Assemblies="@(AotInputAssemblies)"
+        AotModulesTablePath="$(AppDir)\modules.m"
+        AotModulesTableLanguage="ObjC"
+        UseLLVM="True"
+        LLVMPath="$(MicrosoftNetCoreAppRuntimePackDir)native\cross">
+        <Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
+    </MonoAOTCompiler>
+
+    <AppleAppBuilderTask
+        TargetOS="$(TargetOS)"
+        Arch="$(TargetArchitecture)"
+        ProjectName="iOS.Simulator.Aot.Test"
+        MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)native\include\mono-2.0"
+        Assemblies="@(BundleAssemblies)"
+        MainLibraryFileName="iOS.Simulator.Aot.Test.dll"
+        GenerateXcodeProject="True"
+        BuildAppBundle="True"
+        DevTeamProvisioning="$(DevTeamProvisioning)"
+        OutputDirectory="$(AppDir)"
+        Optimized="$(Optimized)"
+        ForceAOT="True"
+        ForceInterpreter="$(MonoForceInterpreter)"
+        AppDir="$(PublishDir)">
+        <Output TaskParameter="AppBundlePath" PropertyName="AppBundlePath" />
+        <Output TaskParameter="XcodeProjectPath" PropertyName="XcodeProjectPath" />
+    </AppleAppBuilderTask>
+
+    <Message Importance="High" Text="Xcode: $(XcodeProjectPath)"/>
+    <Message Importance="High" Text="App:   $(AppBundlePath)"/>
+  </Target>
+
+  <Import Project="..\..\common.targets" />
+</Project>
diff --git a/src/tests/FunctionalTests/iOS/Simulator/AOT/main.m b/src/tests/FunctionalTests/iOS/Simulator/AOT/main.m
new file mode 100644 (file)
index 0000000..0ab407e
--- /dev/null
@@ -0,0 +1,80 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#import <UIKit/UIKit.h>
+#import "runtime.h"
+
+@interface ViewController : UIViewController
+@end
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+@property (strong, nonatomic) UIWindow *window;
+@property (strong, nonatomic) ViewController *controller;
+@end
+
+@implementation AppDelegate
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+    self.controller = [[ViewController alloc] initWithNibName:nil bundle:nil];
+    self.window.rootViewController = self.controller;
+    [self.window makeKeyAndVisible];
+    return YES;
+}
+@end
+
+UILabel *label;
+void (*clickHandlerPtr)(void);
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    
+    label = [[UILabel alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+    label.textColor = [UIColor greenColor];
+    label.font = [UIFont boldSystemFontOfSize: 30];
+    label.numberOfLines = 2;
+    label.textAlignment = NSTextAlignmentCenter;
+    [self.view addSubview:label];
+    
+    UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoDark];
+    [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
+    [button setFrame:CGRectMake(50, 300, 200, 50)];
+    [button setTitle:@"Click me" forState:UIControlStateNormal];
+    [button setExclusiveTouch:YES];
+    [self.view addSubview:button];
+
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        mono_ios_runtime_init ();
+    });
+}
+-(void) buttonClicked:(UIButton*)sender
+{
+    if (clickHandlerPtr)
+        clickHandlerPtr();
+}
+
+@end
+
+// called from C# sample
+void
+ios_register_button_click (void* ptr)
+{
+    clickHandlerPtr = ptr;
+}
+
+// called from C# sample
+void
+ios_set_text (const char* value)
+{
+    NSString* nsstr = [NSString stringWithUTF8String:strdup(value)];
+    dispatch_async(dispatch_get_main_queue(), ^{
+        label.text = nsstr;
+    });
+}
+
+int main(int argc, char * argv[]) {
+    @autoreleasepool {
+        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+    }
+}
diff --git a/src/tests/FunctionalTests/iOS/Simulator/Interpreter/Program.cs b/src/tests/FunctionalTests/iOS/Simulator/Interpreter/Program.cs
new file mode 100644 (file)
index 0000000..e6f8b89
--- /dev/null
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+
+// it's not part of the BCL but runtime needs it for native-to-managed callbacks in AOT
+// To be replaced with NativeCallableAttribute
+public class MonoPInvokeCallbackAttribute : Attribute
+{
+    public MonoPInvokeCallbackAttribute(Type delegateType) { }
+}
+
+public static class Program
+{
+    // Defined in main.m
+    [DllImport("__Internal")]
+    private extern static void ios_set_text(string value);
+
+    [DllImport("__Internal")]
+    private extern static void ios_register_button_click(Action action);
+
+    private static Action buttonClickHandler = null;
+
+    private static int counter = 0;
+
+    // Called by native code, see main.m
+    [MonoPInvokeCallback(typeof(Action))]
+    private static void OnButtonClick()
+    {
+        ios_set_text("OnButtonClick! #" + counter++);
+    }
+
+    public static async Task<int> Main(string[] args)
+    {
+        // Register a managed callback (will be called by UIButton, see main.m)
+        // Also, keep the handler alive so GC won't collect it.
+        ios_register_button_click(buttonClickHandler = OnButtonClick);
+
+        const string msg = "Hello World!\n.NET 5.0";
+        for (int i = 0; i < msg.Length; i++)
+        {
+            // a kind of an animation
+            ios_set_text(msg.Substring(0, i + 1));
+            await Task.Delay(100);
+        }
+
+        Console.WriteLine("Done!");
+        await Task.Delay(5000);
+        return 42;
+    }
+}
diff --git a/src/tests/FunctionalTests/iOS/Simulator/Interpreter/README.md b/src/tests/FunctionalTests/iOS/Simulator/Interpreter/README.md
new file mode 100644 (file)
index 0000000..b34e705
--- /dev/null
@@ -0,0 +1,3 @@
+# iOS functional test running on simulator in Interpreter mode
+
+Simulator: iPhone 11
diff --git a/src/tests/FunctionalTests/iOS/Simulator/Interpreter/iOS.Simulator.Interpreter.Test.csproj b/src/tests/FunctionalTests/iOS/Simulator/Interpreter/iOS.Simulator.Interpreter.Test.csproj
new file mode 100644 (file)
index 0000000..9c01811
--- /dev/null
@@ -0,0 +1,45 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  
+  <Import Project="..\..\common.props" />
+
+  <UsingTask TaskName="AppleAppBuilderTask"
+             AssemblyFile="$(AppleAppBuilderTasksAssemblyPath)" />
+
+  <Target Name="BuildAppBundle" AfterTargets="CopyFilesToPublishDirectory">
+    <PropertyGroup>
+      <AppDir>$(PublishDir)\app</AppDir>
+      <IosSimulator Condition="'$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'x86'">iPhone 11</IosSimulator>
+      <Optimized Condition="'$(Configuration)' == 'Release'">True</Optimized>
+    </PropertyGroup>
+
+    <RemoveDir Directories="$(AppDir)" />
+
+    <ItemGroup>
+      <BundleAssemblies Include="$(PublishDir)\*.dll" />
+    </ItemGroup>
+
+    <AppleAppBuilderTask
+        TargetOS="$(TargetOS)"
+        Arch="$(TargetArchitecture)"
+        ProjectName="iOS.Simulator.Interpreter.Test"
+        MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)native\include\mono-2.0"
+        Assemblies="@(BundleAssemblies)"
+        MainLibraryFileName="iOS.Simulator.Interpreter.Test.dll"
+        GenerateXcodeProject="True"
+        BuildAppBundle="True"
+        DevTeamProvisioning="$(DevTeamProvisioning)"
+        OutputDirectory="$(AppDir)"
+        Optimized="$(Optimized)"
+        ForceAOT="False"
+        ForceInterpreter="$(MonoForceInterpreter)"
+        AppDir="$(PublishDir)">
+        <Output TaskParameter="AppBundlePath" PropertyName="AppBundlePath" />
+        <Output TaskParameter="XcodeProjectPath" PropertyName="XcodeProjectPath" />
+    </AppleAppBuilderTask>
+
+    <Message Importance="High" Text="Xcode: $(XcodeProjectPath)"/>
+    <Message Importance="High" Text="App:   $(AppBundlePath)"/>
+  </Target>
+
+  <Import Project="..\..\common.targets" />
+</Project>
diff --git a/src/tests/FunctionalTests/iOS/Simulator/Interpreter/main.m b/src/tests/FunctionalTests/iOS/Simulator/Interpreter/main.m
new file mode 100644 (file)
index 0000000..0ab407e
--- /dev/null
@@ -0,0 +1,80 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#import <UIKit/UIKit.h>
+#import "runtime.h"
+
+@interface ViewController : UIViewController
+@end
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+@property (strong, nonatomic) UIWindow *window;
+@property (strong, nonatomic) ViewController *controller;
+@end
+
+@implementation AppDelegate
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+    self.controller = [[ViewController alloc] initWithNibName:nil bundle:nil];
+    self.window.rootViewController = self.controller;
+    [self.window makeKeyAndVisible];
+    return YES;
+}
+@end
+
+UILabel *label;
+void (*clickHandlerPtr)(void);
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    
+    label = [[UILabel alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+    label.textColor = [UIColor greenColor];
+    label.font = [UIFont boldSystemFontOfSize: 30];
+    label.numberOfLines = 2;
+    label.textAlignment = NSTextAlignmentCenter;
+    [self.view addSubview:label];
+    
+    UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoDark];
+    [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
+    [button setFrame:CGRectMake(50, 300, 200, 50)];
+    [button setTitle:@"Click me" forState:UIControlStateNormal];
+    [button setExclusiveTouch:YES];
+    [self.view addSubview:button];
+
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        mono_ios_runtime_init ();
+    });
+}
+-(void) buttonClicked:(UIButton*)sender
+{
+    if (clickHandlerPtr)
+        clickHandlerPtr();
+}
+
+@end
+
+// called from C# sample
+void
+ios_register_button_click (void* ptr)
+{
+    clickHandlerPtr = ptr;
+}
+
+// called from C# sample
+void
+ios_set_text (const char* value)
+{
+    NSString* nsstr = [NSString stringWithUTF8String:strdup(value)];
+    dispatch_async(dispatch_get_main_queue(), ^{
+        label.text = nsstr;
+    });
+}
+
+int main(int argc, char * argv[]) {
+    @autoreleasepool {
+        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+    }
+}
diff --git a/src/tests/FunctionalTests/iOS/common.props b/src/tests/FunctionalTests/iOS/common.props
new file mode 100644 (file)
index 0000000..c353556
--- /dev/null
@@ -0,0 +1,14 @@
+<Project>
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <OutputPath>$(AssemblyBinDirOutputPath)</OutputPath>
+    <DebugType>Portable</DebugType>
+    <TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
+    <TargetOS>iOS</TargetOS>
+    <MicrosoftNetCoreAppRuntimePackDir>$(ArtifactsBinDir)microsoft.netcore.app.runtime.ios-$(TargetArchitecture)\$(Configuration)\runtimes\ios-$(TargetArchitecture)\</MicrosoftNetCoreAppRuntimePackDir>
+    <EnableTargetingPackDownload>false</EnableTargetingPackDownload>
+    <RuntimeIdentifier>ios-$(TargetArchitecture)</RuntimeIdentifier>
+    <PublishTrimmed>true</PublishTrimmed>
+    <TrimMode>Link</TrimMode>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/iOS/common.targets b/src/tests/FunctionalTests/iOS/common.targets
new file mode 100644 (file)
index 0000000..60ed0c2
--- /dev/null
@@ -0,0 +1,29 @@
+<Project>
+
+  <!-- Redirect 'dotnet publish' to in-tree runtime pack -->
+  <Target Name="TrickRuntimePackLocation" AfterTargets="ProcessFrameworkReferences">
+    <ItemGroup>
+      <RuntimePack>
+        <PackageDirectory>$(ArtifactsBinDir)microsoft.netcore.app.runtime.ios-$(TargetArchitecture)\$(Configuration)</PackageDirectory>
+      </RuntimePack>
+    </ItemGroup>
+    <Message Text="Packaged ID: %(RuntimePack.PackageDirectory)" Importance="high" />
+  </Target>
+
+  <Target Name="CopySampleAppToHelixTestDir" AfterTargets="Build" DependsOnTargets="Publish;BuildAppBundle" >
+    <PropertyGroup>
+      <OSPlatformConfig>$(TargetOS).AnyCPU.$(Configuration)</OSPlatformConfig>
+      <HelixArchiveRoot>$(ArtifactsDir)helix/</HelixArchiveRoot>
+      <HelixArchiveRunOnlyRoot>$(HelixArchiveRoot)runonly/</HelixArchiveRunOnlyRoot>
+      <HelixArchiveRunOnlyAppsDir>$(HelixArchiveRunOnlyRoot)$(OSPlatformConfig)/</HelixArchiveRunOnlyAppsDir>
+    </PropertyGroup>
+    <ItemGroup>
+      <_appFiles Include="$(AppBundlePath)/../**/*" />
+    </ItemGroup>
+    <Copy SourceFiles="@(_appFiles)"
+          DestinationFolder="$(HelixArchiveRunOnlyAppsDir)/%(RecursiveDir)" />
+
+    <Message Importance="High" Text="HelixArchiveRunOnlyAppsDir: $(HelixArchiveRunOnlyAppsDir)"/>
+  </Target>
+
+</Project>
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/wasm/AOT/browser/Program.cs b/src/tests/FunctionalTests/wasm/AOT/browser/Program.cs
new file mode 100644 (file)
index 0000000..743f481
--- /dev/null
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Sample
+{
+    public class Test
+    {
+        public static void Main(string[] args)
+        {
+            Console.WriteLine ("Hello, World!");
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static int TestMeaning()
+        {
+            return 42;
+        }
+    }
+}
diff --git a/src/tests/FunctionalTests/wasm/AOT/browser/Wasm.Aot.Browser.Test.csproj b/src/tests/FunctionalTests/wasm/AOT/browser/Wasm.Aot.Browser.Test.csproj
new file mode 100644 (file)
index 0000000..ebd72fe
--- /dev/null
@@ -0,0 +1,35 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <RunAOTCompilation>true</RunAOTCompilation>
+    <WasmModeSpecificPath>\aot\browser\</WasmModeSpecificPath>
+  </PropertyGroup>
+
+  <Import Project="..\..\common.props" />
+
+  <Target Name="PrepareForWasmBuild" BeforeTargets="WasmBuildApp">
+    <PropertyGroup>
+        <WasmAppDir>$(AppDir)</WasmAppDir>
+        <WasmBuildDir>$(PublishDir)</WasmBuildDir>
+        <WasmMainJSPath>runtime.js</WasmMainJSPath>
+        <WasmMainAssemblyPath>$(WasmBuildDir)$(AssemblyName).dll</WasmMainAssemblyPath>
+    </PropertyGroup>
+    <ItemGroup>
+      <WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
+    </ItemGroup>
+  </Target>
+
+  <Target Name="AfterWasmBuildApp" AfterTargets="WasmBuildApp">
+    <Copy SourceFiles="$(OutDir)\index.html" DestinationFolder="$(AppDir)" />
+  </Target>
+
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Content Include="index.html">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+  <Import Project="..\..\common.targets" />
+
+  <Import Project="$(MonoProjectRoot)\wasm\build\WasmApp.targets" />
+</Project>
diff --git a/src/tests/FunctionalTests/wasm/AOT/browser/index.html b/src/tests/FunctionalTests/wasm/AOT/browser/index.html
new file mode 100644 (file)
index 0000000..9b527ec
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!--  Licensed to the .NET Foundation under one or more agreements. -->
+<!-- The .NET Foundation licenses this file to you under the MIT license. -->
+<html>
+  <head>
+    <title>TESTS</title>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  </head>
+  <body onload="onLoad()">
+    <h3 id="header">Wasm Browser Sample</h3>
+    Result from Sample.Test.TestMeaning: <span id="out"></span>
+    <script type='text/javascript'>
+      var is_testing = false;
+      var onLoad = function() {
+        var url = new URL(decodeURI(window.location));
+        let args = url.searchParams.getAll('arg');
+        is_testing = args !== undefined && (args.find(arg => arg == '--testing') !== undefined);
+      };
+
+      var test_exit = function(exit_code)
+      {
+        if (!is_testing) {
+          console.log(`test_exit: ${exitCode}`);
+          return;
+        }
+
+        /* Set result in a tests_done element, to be read by xharness */
+        var tests_done_elem = document.createElement("label");
+        tests_done_elem.id = "tests_done";
+        tests_done_elem.innerHTML = exit_code.toString();
+        document.body.appendChild(tests_done_elem);
+
+        console.log(`WASM EXIT ${exit_code}`);
+      };
+
+      var App = {
+        init: function () {
+          var ret = BINDING.call_static_method("[Wasm.Aot.Browser.Test] Sample.Test:TestMeaning", []);
+          document.getElementById("out").innerHTML = ret;
+
+          if (is_testing)
+          {
+            console.debug(`ret: ${ret}`);
+            let exit_code = ret == 42 ? 0 : 1;
+            test_exit(exit_code);
+          }
+        },
+      };
+    </script>
+    <script type="text/javascript" src="mono-config.js"></script>
+    <script type="text/javascript" src="runtime.js"></script>
+
+    <script defer src="dotnet.js"></script>
+
+  </body>
+</html>
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/wasm/AOT/browser/runtime.js b/src/tests/FunctionalTests/wasm/AOT/browser/runtime.js
new file mode 100644 (file)
index 0000000..a39b0b9
--- /dev/null
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+var Module = { 
+    onRuntimeInitialized: function () {
+        config.loaded_cb = function () {
+            try {
+                App.init ();
+            } catch (error) {
+                test_exit(1);
+                throw (error);
+            }
+        };
+        config.fetch_file_cb = function (asset) {
+            return fetch (asset, { credentials: 'same-origin' });
+        }
+
+        try
+        {
+            MONO.mono_load_runtime_and_bcl_args (config);
+        } catch (error) {
+            test_exit(1);
+            throw(error);
+        }
+    },
+};
diff --git a/src/tests/FunctionalTests/wasm/AOT/console/Program.cs b/src/tests/FunctionalTests/wasm/AOT/console/Program.cs
new file mode 100644 (file)
index 0000000..6af1fa6
--- /dev/null
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Threading.Tasks;
+
+public class Test
+{
+    public static async Task<int> Main(string[] args)
+    {
+        await Task.Delay(1);
+        Console.WriteLine("Hello World!");
+        for (int i = 0; i < args.Length; i++) {
+            Console.WriteLine($"args[{i}] = {args[i]}");
+        }
+        return args.Length;
+    }
+}
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/wasm/AOT/console/Wasm.Aot.Console.Test.csproj b/src/tests/FunctionalTests/wasm/AOT/console/Wasm.Aot.Console.Test.csproj
new file mode 100644 (file)
index 0000000..d9ececb
--- /dev/null
@@ -0,0 +1,29 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <RunAOTCompilation>true</RunAOTCompilation>
+    <WasmModeSpecificPath>\aot\console\</WasmModeSpecificPath>
+  </PropertyGroup>
+
+  <Import Project="..\..\common.props" />
+
+  <Target Name="PrepareForWasmBuild" BeforeTargets="WasmBuildApp">
+    <PropertyGroup>
+      <WasmAppDir>$(AppDir)</WasmAppDir>
+      <WasmBuildDir>$(PublishDir)</WasmBuildDir>
+      <WasmMainJSPath>$(MonoProjectRoot)\wasm\runtime-test.js</WasmMainJSPath>
+      <WasmMainAssemblyPath>$(WasmBuildDir)$(AssemblyName).dll</WasmMainAssemblyPath>
+      <WasmGenerateRunV8Script>true</WasmGenerateRunV8Script>
+    </PropertyGroup>
+    <ItemGroup>
+      <WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
+    </ItemGroup>
+  </Target>
+
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+  </ItemGroup>
+
+  <Import Project="..\..\common.targets" />
+
+  <Import Project="$(MonoProjectRoot)\wasm\build\WasmApp.targets" />
+</Project>
diff --git a/src/tests/FunctionalTests/wasm/Interpreter/browser/Program.cs b/src/tests/FunctionalTests/wasm/Interpreter/browser/Program.cs
new file mode 100644 (file)
index 0000000..743f481
--- /dev/null
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Sample
+{
+    public class Test
+    {
+        public static void Main(string[] args)
+        {
+            Console.WriteLine ("Hello, World!");
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static int TestMeaning()
+        {
+            return 42;
+        }
+    }
+}
diff --git a/src/tests/FunctionalTests/wasm/Interpreter/browser/Wasm.Interpreter.Browser.Test.csproj b/src/tests/FunctionalTests/wasm/Interpreter/browser/Wasm.Interpreter.Browser.Test.csproj
new file mode 100644 (file)
index 0000000..b4cb152
--- /dev/null
@@ -0,0 +1,34 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <RunAOTCompilation>false</RunAOTCompilation>
+    <WasmModeSpecificPath>\interpreter\browser\</WasmModeSpecificPath>
+  </PropertyGroup>
+
+  <Import Project="..\..\common.props" />
+
+  <Target Name="PrepareForWasmBuild" BeforeTargets="WasmBuildApp">
+    <PropertyGroup>
+        <WasmAppDir>$(AppDir)</WasmAppDir>
+        <WasmMainJSPath>runtime.js</WasmMainJSPath>
+        <WasmMainAssemblyPath>$(PublishDir)$(AssemblyName).dll</WasmMainAssemblyPath>
+    </PropertyGroup>
+    <ItemGroup>
+      <WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
+    </ItemGroup>
+  </Target>
+
+  <Target Name="AfterWasmBuildApp" AfterTargets="WasmBuildApp">
+    <Copy SourceFiles="$(OutDir)\index.html" DestinationFolder="$(AppDir)" />
+  </Target>
+
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Content Include="index.html">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+  <Import Project="..\..\common.targets" />
+
+  <Import Project="$(MonoProjectRoot)\wasm\build\WasmApp.targets" />
+</Project>
diff --git a/src/tests/FunctionalTests/wasm/Interpreter/browser/index.html b/src/tests/FunctionalTests/wasm/Interpreter/browser/index.html
new file mode 100644 (file)
index 0000000..d264b4c
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!--  Licensed to the .NET Foundation under one or more agreements. -->
+<!-- The .NET Foundation licenses this file to you under the MIT license. -->
+<html>
+  <head>
+    <title>TESTS</title>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  </head>
+  <body onload="onLoad()">
+    <h3 id="header">Wasm Browser Sample</h3>
+    Result from Sample.Test.TestMeaning: <span id="out"></span>
+    <script type='text/javascript'>
+      var is_testing = false;
+      var onLoad = function() {
+        var url = new URL(decodeURI(window.location));
+        let args = url.searchParams.getAll('arg');
+        is_testing = args !== undefined && (args.find(arg => arg == '--testing') !== undefined);
+      };
+
+      var test_exit = function(exit_code)
+      {
+        if (!is_testing) {
+          console.log(`test_exit: ${exitCode}`);
+          return;
+        }
+
+        /* Set result in a tests_done element, to be read by xharness */
+        var tests_done_elem = document.createElement("label");
+        tests_done_elem.id = "tests_done";
+        tests_done_elem.innerHTML = exit_code.toString();
+        document.body.appendChild(tests_done_elem);
+
+        console.log(`WASM EXIT ${exit_code}`);
+      };
+
+      var App = {
+        init: function () {
+          var ret = BINDING.call_static_method("[Wasm.Interpreter.Browser.Test] Sample.Test:TestMeaning", []);
+          document.getElementById("out").innerHTML = ret;
+
+          if (is_testing)
+          {
+            console.debug(`ret: ${ret}`);
+            let exit_code = ret == 42 ? 0 : 1;
+            test_exit(exit_code);
+          }
+        },
+      };
+    </script>
+    <script type="text/javascript" src="mono-config.js"></script>
+    <script type="text/javascript" src="runtime.js"></script>
+
+    <script defer src="dotnet.js"></script>
+
+  </body>
+</html>
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/wasm/Interpreter/browser/runtime.js b/src/tests/FunctionalTests/wasm/Interpreter/browser/runtime.js
new file mode 100644 (file)
index 0000000..a39b0b9
--- /dev/null
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+var Module = { 
+    onRuntimeInitialized: function () {
+        config.loaded_cb = function () {
+            try {
+                App.init ();
+            } catch (error) {
+                test_exit(1);
+                throw (error);
+            }
+        };
+        config.fetch_file_cb = function (asset) {
+            return fetch (asset, { credentials: 'same-origin' });
+        }
+
+        try
+        {
+            MONO.mono_load_runtime_and_bcl_args (config);
+        } catch (error) {
+            test_exit(1);
+            throw(error);
+        }
+    },
+};
diff --git a/src/tests/FunctionalTests/wasm/Interpreter/console/Program.cs b/src/tests/FunctionalTests/wasm/Interpreter/console/Program.cs
new file mode 100644 (file)
index 0000000..6af1fa6
--- /dev/null
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Threading.Tasks;
+
+public class Test
+{
+    public static async Task<int> Main(string[] args)
+    {
+        await Task.Delay(1);
+        Console.WriteLine("Hello World!");
+        for (int i = 0; i < args.Length; i++) {
+            Console.WriteLine($"args[{i}] = {args[i]}");
+        }
+        return args.Length;
+    }
+}
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/wasm/Interpreter/console/Wasm.Interpreter.Console.Test.csproj b/src/tests/FunctionalTests/wasm/Interpreter/console/Wasm.Interpreter.Console.Test.csproj
new file mode 100644 (file)
index 0000000..c41cd2b
--- /dev/null
@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <RunAOTCompilation>false</RunAOTCompilation>
+    <WasmModeSpecificPath>\interpreter\console\</WasmModeSpecificPath>
+  </PropertyGroup>
+
+  <Import Project="..\..\common.props" />
+
+  <Target Name="PrepareForWasmBuild" BeforeTargets="WasmBuildApp">
+    <PropertyGroup>
+      <WasmAppDir>$(AppDir)</WasmAppDir>
+      <WasmMainJSPath>$(MonoProjectRoot)\wasm\runtime-test.js</WasmMainJSPath>
+      <WasmMainAssemblyPath>$(PublishDir)$(AssemblyName).dll</WasmMainAssemblyPath>
+      <WasmGenerateRunV8Script>true</WasmGenerateRunV8Script>
+    </PropertyGroup>
+    <ItemGroup>
+      <WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
+    </ItemGroup>
+  </Target>
+
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+  </ItemGroup>
+
+  <Import Project="..\..\common.targets" />
+
+  <Import Project="$(MonoProjectRoot)\wasm\build\WasmApp.targets" />
+</Project>
diff --git a/src/tests/FunctionalTests/wasm/common.props b/src/tests/FunctionalTests/wasm/common.props
new file mode 100644 (file)
index 0000000..b63d0f0
--- /dev/null
@@ -0,0 +1,27 @@
+<Project>
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <OutputPath>$(RepoRoot)artifacts\bin\$(AssemblyName)</OutputPath>
+    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+    <TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
+    <RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
+    <TargetArchitecture>wasm</TargetArchitecture>
+    <TargetOS>Browser</TargetOS>
+    <MicrosoftNetCoreAppRuntimePackRidDir>$(ArtifactsBinDir)microsoft.netcore.app.runtime.browser-wasm\$(Configuration)\runtimes\browser-wasm\</MicrosoftNetCoreAppRuntimePackRidDir>
+    <BuildDir>$(MSBuildThisFileDirectory)obj\$(Configuration)\wasm</BuildDir>
+    <AppDir>$(OutputPath)\$(Configuration)\AppBundle\</AppDir> 
+    <PublishTrimmed>true</PublishTrimmed>
+    <TrimMode>link</TrimMode>
+    <EnableTargetingPackDownload>false</EnableTargetingPackDownload>
+    <RunAnalyzers>false</RunAnalyzers>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)' != 'Debug'">
+    <!-- Runtime feature defaults to trim unnecessary code -->
+    <EventSourceSupport>false</EventSourceSupport>
+    <UseSystemResourceKeys>true</UseSystemResourceKeys>
+    <EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
+    <HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
+    <DebuggerSupport>false</DebuggerSupport>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/src/tests/FunctionalTests/wasm/common.targets b/src/tests/FunctionalTests/wasm/common.targets
new file mode 100644 (file)
index 0000000..206093f
--- /dev/null
@@ -0,0 +1,27 @@
+<Project>
+
+  <!-- Redirect 'dotnet publish' to in-tree runtime pack -->
+  <Target Name="TrickRuntimePackLocation" AfterTargets="ProcessFrameworkReferences">
+    <ItemGroup>
+      <RuntimePack>
+        <PackageDirectory>$(ArtifactsBinDir)microsoft.netcore.app.runtime.browser-wasm\$(Configuration)</PackageDirectory>
+      </RuntimePack>
+    </ItemGroup>
+    <Message Text="Packaged ID: %(RuntimePack.PackageDirectory)" Importance="high" />
+  </Target>
+  
+  <Target Name="CopySampleAppToHelixTestDir" AfterTargets="Build" DependsOnTargets="Publish" >
+    <PropertyGroup>
+      <!-- Helix properties -->
+      <!-- AnyCPU as Platform-->
+      <OSPlatformConfig>$(TargetOS).AnyCPU.$(Configuration)</OSPlatformConfig>
+      <HelixArchiveRoot>$(ArtifactsDir)helix/</HelixArchiveRoot>
+      <HelixArchiveRunOnlyRoot>$(HelixArchiveRoot)runonly/</HelixArchiveRunOnlyRoot>
+      <HelixArchiveRunOnlyAppsDir>$(HelixArchiveRunOnlyRoot)$(OSPlatformConfig)$(WasmModeSpecificPath)/</HelixArchiveRunOnlyAppsDir>
+      <ZippedApp>$(OutputPath)$(AssemblyName).zip</ZippedApp>
+    </PropertyGroup>
+    <ZipDirectory SourceDirectory="$(AppDir)" DestinationFile="$(ZippedApp)" />
+    <Copy SourceFiles="$(ZippedApp)" DestinationFolder="$(HelixArchiveRunOnlyAppsDir)" />
+  </Target>
+  
+</Project>
\ No newline at end of file