Skip to content
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

[Mono] Turn Samples into Functional Tests #44016

Closed
Closed
Show file tree
Hide file tree
Changes from 7 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
5 changes: 5 additions & 0 deletions src/libraries/IntegrationTests/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
</PropertyGroup>
<Import Project="..\..\Directory.Build.props"/>
</Project>
53 changes: 53 additions & 0 deletions src/libraries/IntegrationTests/tests/iOS/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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 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(-1);
Comment on lines +42 to +51
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to check some error codes

Copy link
Contributor

Choose a reason for hiding this comment

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

Why is there a 'tests' directory under 'IntegrationTests' ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This follows the structure, which other library directories have, in order to be included in

<ProjectReference Include="$(MSBuildThisFileDirectory)*\tests\**\*.Tests.csproj"

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<OutputPath>bin</OutputPath>
<DebugType>Portable</DebugType>
<TargetFramework>$(NetCoreAppCurrent)</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>
<Optimized Condition="'$(Configuration)' == 'Release'">True</Optimized>
<InvariantGlobalization>true</InvariantGlobalization> <!-- workaround for https://github.com/dotnet/runtime/issues/41866 -->
<RunAnalyzers>false</RunAnalyzers>
</PropertyGroup>

<!-- 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>

<Import Project="$(RepoTasksDir)mobile.tasks\AotCompilerTask\MonoAOTCompiler.props" />
<UsingTask TaskName="AppleAppBuilderTask"
AssemblyFile="$(ArtifactsBinDir)AppleAppBuilder\$(Configuration)\$(NetCoreAppCurrent)\AppleAppBuilder.dll" />

<UsingTask TaskName="MonoAOTCompiler"
AssemblyFile="$(ArtifactsBinDir)MonoAOTCompiler\$(Configuration)\$(NetCoreAppCurrent)\MonoAOTCompiler.dll" />

<Target Name="BuildAppBundle" AfterTargets="CopyFilesToPublishDirectory">
<PropertyGroup>
<AppDir>$(MSBuildThisFileDirectory)$(PublishDir)\app</AppDir>
<IosSimulator Condition="'$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'x86'">iPhone 11</IosSimulator>
</PropertyGroup>

<RemoveDir Directories="$(AppDir)" />

<ItemGroup>
<BundleAssemblies Include="$(MSBuildThisFileDirectory)$(PublishDir)\*.dll" />
<AotInputAssemblies Include="$(MSBuildThisFileDirectory)$(PublishDir)\*.dll">
<AotArguments>@(MonoAOTCompilerDefaultAotArguments, ';')</AotArguments>
<ProcessArguments>@(MonoAOTCompilerDefaultProcessArguments, ';')</ProcessArguments>
</AotInputAssemblies>
</ItemGroup>

<MonoAOTCompiler
Condition="'$(IosSimulator)' == '' or '$(UseAotForSimulator)' == 'true'"
CompilerBinaryPath="$(MicrosoftNetCoreAppRuntimePackDir)native\cross\mono-aot-cross"
Mode="Full"
OutputType="AsmOnly"
Assemblies="@(AotInputAssemblies)"
UseLLVM="$(UseLLVM)"
LLVMPath="$(MicrosoftNetCoreAppRuntimePackDir)native\cross">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
</MonoAOTCompiler>

<AppleAppBuilderTask
Arch="$(TargetArchitecture)"
ProjectName="HelloiOS"
MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)native\include\mono-2.0"
Assemblies="@(BundleAssemblies)"
MainLibraryFileName="Program.dll"
GenerateXcodeProject="True"
BuildAppBundle="True"
DevTeamProvisioning="$(DevTeamProvisioning)"
OutputDirectory="$(AppDir)"
Optimized="$(Optimized)"
UseAotForSimulator="$(UseAotForSimulator)"
AppDir="$(MSBuildThisFileDirectory)$(PublishDir)">
<Output TaskParameter="AppBundlePath" PropertyName="AppBundlePath" />
<Output TaskParameter="XcodeProjectPath" PropertyName="XcodeProjectPath" />
</AppleAppBuilderTask>

<Message Importance="High" Text="Xcode: $(XcodeProjectPath)"/>
<Message Importance="High" Text="App: $(AppBundlePath)"/>

<Exec Condition="'$(IosSimulator)' != ''" Command="xcrun simctl shutdown &quot;$(IosSimulator)&quot;" ContinueOnError="WarnAndContinue" />
<Exec Condition="'$(IosSimulator)' != ''" Command="xcrun simctl boot &quot;$(IosSimulator)&quot;" />
<Exec Condition="'$(IosSimulator)' != ''" Command="open -a Simulator" />
<Exec Condition="'$(IosSimulator)' != ''" Command="xcrun simctl install &quot;$(IosSimulator)&quot; $(AppBundlePath)" />
<Exec Condition="'$(IosSimulator)' != ''" Command="xcrun simctl launch --console booted net.dot.HelloiOS" />
</Target>
</Project>
80 changes: 80 additions & 0 deletions src/libraries/IntegrationTests/tests/iOS/main.m
Original file line number Diff line number Diff line change
@@ -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]));
}
}
4 changes: 4 additions & 0 deletions src/libraries/tests.proj
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Private.Xml.Linq\tests\xNodeReader\System.Xml.Linq.xNodeReader.Tests.csproj" />
</ItemGroup>

<ItemGroup Condition="'$(TargetOS)' != 'iOS' and '$(TargetOS)' != 'tvOS'">
<ProjectExclusions Include="$(MSBuildThisFileDirectory)IntegrationTests\tests\iOS\iOS.Integration.Tests.csproj" />
</ItemGroup>

<ItemGroup Condition="'$(TargetOS)' == 'Browser' and '$(RunDisabledWasmTests)' != 'true'">
<!-- Mono-Browser ignores runtimeconfig.template.json (e.g. for this it has "System.Globalization.EnforceJapaneseEraYearRanges": true) -->
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Globalization.Calendars\tests\CalendarTestWithConfigSwitch\System.Globalization.CalendarsWithConfigSwitch.Tests.csproj" />
Expand Down