diff --git a/Make.config b/Make.config index 9a81df9238..bea6e8b483 100644 --- a/Make.config +++ b/Make.config @@ -200,9 +200,9 @@ MACCATALYST_NUGET_VERSION_NO_METADATA=$(MACCATALYST_NUGET_VERSION)$(NUGET_PREREL MACCATALYST_NUGET_VERSION_FULL=$(MACCATALYST_NUGET_VERSION_NO_METADATA)+$(NUGET_BUILD_METADATA) # Xcode version should have both a major and a minor version (even if the minor version is 0) -XCODE_VERSION=14.3 -XCODE_URL=https://dl.internalx.com/internal-files/xcodes/Xcode_14.3.xip -XCODE_DEVELOPER_ROOT=/Applications/Xcode_14.3.0.app/Contents/Developer +XCODE_VERSION=14.3.1 +XCODE_URL=https://dl.internalx.com/internal-files/xcodes/Xcode_14.3.1.xip +XCODE_DEVELOPER_ROOT=/Applications/Xcode_14.3.1.app/Contents/Developer XCODE_PRODUCT_BUILD_VERSION:=$(shell /usr/libexec/PlistBuddy -c 'Print :ProductBuildVersion' $(XCODE_DEVELOPER_ROOT)/../version.plist 2>/dev/null || echo " $(shell tput setaf 1 2>/dev/null)The required Xcode ($(XCODE_VERSION)) is not installed in $(basename $(basename $(XCODE_DEVELOPER_ROOT)))$(shell tput sgr0 2>/dev/null)" >&2) # Tell both Xcode and our build logic which Xcode we're using. diff --git a/docs/preparing-your-app-for-testflight.md b/docs/preparing-your-app-for-testflight.md new file mode 100644 index 0000000000..306a2a3210 --- /dev/null +++ b/docs/preparing-your-app-for-testflight.md @@ -0,0 +1,172 @@ +# Preparing your App for TestFlight + +This guide shows the process of getting started with using Transporter and the App Connect site to upload your app to the App Store for internal testing via TestFlight. This guide will use the example of publishing a MacCatalyst app to the Mac App Store, but the process is similar for iOS and tvOS apps. + +There is some preparation involved with preparing your Apple Developer account for publishing, such as creating an App Identifier and a Provisioning Profile. The MAUI documentation has [a great guide for that](https://aka.ms/maui-publish-app-store). + +Also, your project will need to have certain configuration values set in the .csproj file, the Info.plist file, and the Entitlements.plist file. + +## Preparing your Project File (.csproj) for App Store submission + +Before you can submit your app to the App Store, you need to make sure that it is correctly configured. This includes: + +- Setting the build to reference the correct `Entitlements.plist` in your project (For example, one for Debug and one for Release). +- Setting the correct `ApplicationId` in your project. +- Setting the correct `ApplicationVersion` in your project. +- Setting the correct `CodesignKey` in your project. +- Setting the correct `PackagingSigningKey` in your project. +- Setting the correct `CodesignEntitlements` in your project. +- Setting the correct `CodesignProvision` in your project. + +Here's an example within a project file that is correctly configured for publishing a MacCatalyst app to the Mac App Store using NET 7.0: + +``` + + + net7.0-maccatalyst + maccatalyst-x64;maccatalyst-arm64 + Exe + enable + true + 14.2 + YourAppName + com.yourcompany.yourappname + 0.1.0 + + + false + + + True + Entitlements.plist + true + true + Apple Development: YOURNAME (*******) + YOUR PROFILE NAME + 3rd Party Mac Developer Installer: YOURNAME (*******) + + +``` + +### Info.plist + +The `Info.plist` file is used by the build system to configure your app and is already generated by your project. It contains info such as the Bundle Display Name and Bundle Identifier. (Please note that values such as the Bundle Identifier are set automatically with the values assigned above and do not have to be set manually. For more information about this, see [this guide](https://github.com/xamarin/xamarin-android/blob/main/Documentation/guides/OneDotNetSingleProject.md#ios-template)) +### Entitlements.plist + +The `Entitlements.plist` file is used by the build system to sign your app. Please see Apple's documentation on Entitlements [here](https://developer.apple.com/documentation/bundleresources/entitlements?language=objc). + +Such common values include: + +- com.apple.security.app-sandbox: Set this to true when working with MacOS or MacCatalyst apps. +- com.apple.security.network.client: Set this to let the forementioned sandboxed app connect to a server process running on the same/another machine (this is useful for web developer tools). + +## Building your App for Publishing + +Build your app for publishing. You can do this by using the dotnet build command. Here is an example for building a MacCatalyst app with .NET 7.0: + +`dotnet build -f:net7.0-maccatalyst -c:Release` + +## App Store Connect + +App Store Connect is the App Store management site. You can use it to to upload your app to the App Store, and to manage your apps. + +### Creating an App in App Store Connect + +Before you can upload your app to the App Store, you need to create an app on App Store Connect. You can create an app on App Store Connect by following these steps: + +1. Go to [https://appstoreconnect.apple.com](https://appstoreconnect.apple.com). +2. Click on "My Apps". +3. Click on the "+" button. +4. Enter the name of your app. +5. Select the platform for your app. +6. Select the primary language for your app. +7. Select the category for your app. Note: It is important that the primary category you select here will be the one +8. Click on "Create". + +### Uploading your App using Transporter + +Once you have created an app on App Store Connect, you can upload your app to the App Store by following these steps: + +1. Go to the Mac App Store and download the Transporter app. +2. Open the Transporter app. +3. Click on "Add". +4. Click on "Add file". +5. Select the `.ipa` or `.pkg` file that you want to upload. +6. Click on "Open". +7. Click on "Upload". + +### Setting up an Internal Test Flight + +Once you have uploaded your app to the App Store, you can set up an internal Test Flight. You can set up an internal Test Flight by following these steps: + +1. Go to [https://appstoreconnect.apple.com](https://appstoreconnect.apple.com). +2. Click on "My Apps". +3. Click on the app that you want to set up an internal Test Flight for. +4. Click on "TestFlight". +5. Click on "Create Beta Group". +6. Enter the name of the internal Test Flight. +7. Click on "Create". + +Note: Careful to add testers to the group directly, and not to an individual build. There seems to be an issue with Apple incorrectly assuming that the XCode version was a beta build and will refuse to add testers to the build. + +### Adding Testers to an Internal Test Flight + +Once you have set up an internal Test Flight, you can add testers to the internal Test Flight. You can add testers to the internal Test Flight by following these steps: + +1. Go to [https://appstoreconnect.apple.com](https://appstoreconnect.apple.com). +2. Click on "My Apps". +3. Click on the app that you want to add testers to. +4. Click on "TestFlight". +5. Click on the internal Test Flight that you want to add testers to. +6. Click on "Add Testers". +7. Click on "Add by Email". +8. Enter the email address of the tester. +9. Click on "Add". + +### Adding a Build to an Internal Test Flight + +Once you have added testers to an internal Test Flight, you can add a build to the internal Test Flight. You can add a build to the internal Test Flight by following these steps: + +1. Go to [https://appstoreconnect.apple.com](https://appstoreconnect.apple.com). +2. Click on "My Apps". +3. Click on the app that you want to add a build to. +4. Click on "TestFlight". +5. Click on the internal Test Flight that you want to add a build to. +6. Click on "Builds". +7. Click on "Add Build". +8. Select the build that you want to add. +9. Click on "Add". + +### Distributing an Internal Test Flight + +Once you have added a build to an internal Test Flight, you can distribute the internal Test Flight. You can distribute the internal Test Flight by following these steps: + +1. Go to [https://appstoreconnect.apple.com](https://appstoreconnect.apple.com). +2. Click on "My Apps". +3. Click on the app that you want to distribute. +4. Click on "TestFlight". +5. Click on the internal Test Flight that you want to distribute. +6. Click on "Distribute External Testers". +7. Click on "Distribute". + +### Downloading an Internal Test Flight + +Once you have distributed an internal Test Flight, you can download the internal Test Flight. You can download the internal Test Flight by following these steps: + +1. Download the "TestFlight" app from the Mac App Store. +2. Open the "TestFlight" app. +3. Click on "Sign In". +4. Enter your Apple ID. +5. Enter your password. +6. Click on "Sign In". +7. Click on the app that you want to download. +8. Click on "Download". + +### Submitting new builds to Internal Test Flight + +Fortunately, the process of submitting new builds to an internal Test Flight is very simple. You can submit new builds to an internal Test Flight by following these steps: + +1. Increment the build number in your project by changing the Bundle Version in the Info.plist file. +2. Build your app. +3. Upload your app to the App Store via Transporter. +4. Open the "TestFlight" app. You should see an Update option for your app. diff --git a/dotnet/Microsoft.MacCatalyst.Sdk/targets/Microsoft.MacCatalyst.Sdk.targets b/dotnet/Microsoft.MacCatalyst.Sdk/targets/Microsoft.MacCatalyst.Sdk.targets index 6cc2e46d0f..d58902ee43 100644 --- a/dotnet/Microsoft.MacCatalyst.Sdk/targets/Microsoft.MacCatalyst.Sdk.targets +++ b/dotnet/Microsoft.MacCatalyst.Sdk/targets/Microsoft.MacCatalyst.Sdk.targets @@ -3,7 +3,16 @@ + $(RunEnvironment) --env '__XAMARIN_DEBUG_MODE__=$(XamarinDebugMode)' + $(RunEnvironment) --env '__XAMARIN_DEBUG_PORT__=$(XamarinDebugPort)' + $(RunEnvironment) --env '__XAMARIN_DEBUG_HOSTS__=$(XamarinDebugHosts)' + $(RunEnvironment) --env '__XAMARIN_DEBUG_CONNECT_TIMEOUT__=$(XamarinDebugConnectTimeout)' + $(OpenArguments) $(RunEnvironment) + $(OpenArguments) --stdout '$(StandardOutputPath)' + $(OpenArguments) --stderr '$(StandardErrorPath)' + $(OpenArguments) --stdin '$(StandardInputPath)' + $(OpenArguments) -n open - "$(TargetDir)/$(AssemblyName).app" --args + $(OpenArguments) "$(TargetDir)/$(AssemblyName).app" --args diff --git a/dotnet/Microsoft.macOS.Sdk/targets/Microsoft.macOS.Sdk.targets b/dotnet/Microsoft.macOS.Sdk/targets/Microsoft.macOS.Sdk.targets index 6cc2e46d0f..d58902ee43 100644 --- a/dotnet/Microsoft.macOS.Sdk/targets/Microsoft.macOS.Sdk.targets +++ b/dotnet/Microsoft.macOS.Sdk/targets/Microsoft.macOS.Sdk.targets @@ -3,7 +3,16 @@ + $(RunEnvironment) --env '__XAMARIN_DEBUG_MODE__=$(XamarinDebugMode)' + $(RunEnvironment) --env '__XAMARIN_DEBUG_PORT__=$(XamarinDebugPort)' + $(RunEnvironment) --env '__XAMARIN_DEBUG_HOSTS__=$(XamarinDebugHosts)' + $(RunEnvironment) --env '__XAMARIN_DEBUG_CONNECT_TIMEOUT__=$(XamarinDebugConnectTimeout)' + $(OpenArguments) $(RunEnvironment) + $(OpenArguments) --stdout '$(StandardOutputPath)' + $(OpenArguments) --stderr '$(StandardErrorPath)' + $(OpenArguments) --stdin '$(StandardInputPath)' + $(OpenArguments) -n open - "$(TargetDir)/$(AssemblyName).app" --args + $(OpenArguments) "$(TargetDir)/$(AssemblyName).app" --args diff --git a/src/Foundation/NSFileManager.cs b/src/Foundation/NSFileManager.cs index 17de2e4159..eb0eed75fa 100644 --- a/src/Foundation/NSFileManager.cs +++ b/src/Foundation/NSFileManager.cs @@ -26,11 +26,15 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // +using CoreFoundation; using ObjCRuntime; + using System; using System.Runtime.InteropServices; using System.Runtime.Versioning; +#nullable enable + namespace Foundation { // This is a convenience enum around a set of native strings. @@ -57,15 +61,15 @@ namespace Foundation { public bool? AppendOnly { get; set; } public bool? Busy { get; set; } public bool? ExtensionHidden { get; set; } - public NSDate CreationDate { get; set; } - public string OwnerAccountName { get; set; } - public string GroupOwnerAccountName { get; set; } + public NSDate? CreationDate { get; set; } + public string? OwnerAccountName { get; set; } + public string? GroupOwnerAccountName { get; set; } public nint? SystemNumber { get; set; } // NSInteger public nuint? DeviceIdentifier { get; set; } // unsigned long public nuint? GroupOwnerAccountID { get; set; } // unsigned long public bool? Immutable { get; set; } - public NSDate ModificationDate { get; set; } + public NSDate? ModificationDate { get; set; } public nuint? OwnerAccountID { get; set; } // unsigned long public nuint? HfsCreatorCode { get; set; } public nuint? HfsTypeCode { get; set; } // unsigned long @@ -83,7 +87,7 @@ namespace Foundation { internal NSDictionary ToDictionary () { NSFileType? type; - NSString v = null; + NSString? v = null; var dict = new NSMutableDictionary (); if (AppendOnly.HasValue) dict.SetObject (NSNumber.FromBoolean (AppendOnly.Value), NSFileManager.AppendOnly); @@ -158,7 +162,8 @@ namespace Foundation { case NSFileProtection.CompleteUntilFirstUserAuthentication: v = NSFileManager.FileProtectionCompleteUntilFirstUserAuthentication; break; } - dict.SetObject (v, NSFileManager.FileProtectionKey); + if (v is not null) + dict.SetObject (v, NSFileManager.FileProtectionKey); } #endif return dict; @@ -222,7 +227,7 @@ namespace Foundation { } #endregion - public static NSFileAttributes FromDictionary (NSDictionary dict) + public static NSFileAttributes? FromDictionary (NSDictionary dict) { if (dict is null) return null; @@ -247,9 +252,7 @@ namespace Foundation { ret.SystemFileNumber = fetch_nuint (dict, NSFileManager.SystemFileNumber); ret.Size = fetch_ulong (dict, NSFileManager.Size); - NSString name; - - name = dict.ObjectForKey (NSFileManager.NSFileType) as NSString; + var name = dict.ObjectForKey (NSFileManager.NSFileType) as NSString; if (name is not null) { NSFileType? type = null; @@ -314,7 +317,7 @@ namespace Foundation { // "The value corresponds to the value of st_dev, as returned by stat(2)" => st_dev is defined to be int32_t in all architectures. public uint Number { get; internal set; } - internal static NSFileSystemAttributes FromDictionary (NSDictionary dict) + internal static NSFileSystemAttributes? FromDictionary (NSDictionary dict) { if (dict is null) return null; @@ -341,108 +344,99 @@ namespace Foundation { [DllImport (Constants.FoundationLibrary)] static extern IntPtr NSUserName (); - public static string UserName { + public static string? UserName { get { - using (var nsstring = ObjCRuntime.Runtime.GetNSObject (NSUserName ())) - return nsstring.ToString (); + return CFString.FromHandle (NSUserName ()); } } [DllImport (Constants.FoundationLibrary)] static extern IntPtr NSFullUserName (); - public static string FullUserName { + public static string? FullUserName { get { - using (var nsstring = ObjCRuntime.Runtime.GetNSObject (NSFullUserName ())) - return nsstring.ToString (); + return CFString.FromHandle (NSFullUserName ()); } } [DllImport (Constants.FoundationLibrary)] static extern IntPtr NSHomeDirectory (); - public static string HomeDirectory { + public static string? HomeDirectory { get { - using (var nsstring = ObjCRuntime.Runtime.GetNSObject (NSHomeDirectory ())) - return nsstring.ToString (); + return CFString.FromHandle (NSHomeDirectory ()); } } [DllImport (Constants.FoundationLibrary)] static extern IntPtr NSHomeDirectoryForUser (/* NSString */IntPtr userName); - public static string GetHomeDirectory (string userName) + public static string? GetHomeDirectory (string userName) { if (userName is null) throw new ArgumentNullException (nameof (userName)); - using (var nsstring = new NSString (userName)) - using (var homeDir = ObjCRuntime.Runtime.GetNSObject (NSHomeDirectoryForUser (nsstring.GetHandle ()))) - return homeDir.ToString (); + var userNamePtr = CFString.CreateNative (userName); + var rv = CFString.FromHandle (NSHomeDirectoryForUser (userNamePtr)); + CFString.ReleaseNative (userNamePtr); + return rv; } [DllImport (Constants.FoundationLibrary)] static extern IntPtr NSTemporaryDirectory (); - public static string TemporaryDirectory { + public static string? TemporaryDirectory { get { - using (var nsstring = ObjCRuntime.Runtime.GetNSObject (NSTemporaryDirectory ())) - return nsstring.ToString (); + return CFString.FromHandle (NSTemporaryDirectory ()); } } public bool SetAttributes (NSFileAttributes attributes, string path, out NSError error) { if (attributes is null) - throw new ArgumentNullException ("attributes"); + throw new ArgumentNullException (nameof (attributes)); return SetAttributes (attributes.ToDictionary (), path, out error); } public bool SetAttributes (NSFileAttributes attributes, string path) { if (attributes is null) - throw new ArgumentNullException ("attributes"); + throw new ArgumentNullException (nameof (attributes)); return SetAttributes (attributes.ToDictionary (), path, out _); } - public bool CreateDirectory (string path, bool createIntermediates, NSFileAttributes attributes, out NSError error) + public bool CreateDirectory (string path, bool createIntermediates, NSFileAttributes? attributes, out NSError error) { - var dict = attributes is null ? null : attributes.ToDictionary (); - return CreateDirectory (path, createIntermediates, dict, out error); + return CreateDirectory (path, createIntermediates, attributes?.ToDictionary (), out error); } - public bool CreateDirectory (string path, bool createIntermediates, NSFileAttributes attributes) + public bool CreateDirectory (string path, bool createIntermediates, NSFileAttributes? attributes) { - NSError error; - var dict = attributes is null ? null : attributes.ToDictionary (); - return CreateDirectory (path, createIntermediates, dict, out error); + return CreateDirectory (path, createIntermediates, attributes?.ToDictionary (), out var _); } - public bool CreateFile (string path, NSData data, NSFileAttributes attributes) + public bool CreateFile (string path, NSData data, NSFileAttributes? attributes) { - var dict = attributes is null ? null : attributes.ToDictionary (); - return CreateFile (path, data, dict); + return CreateFile (path, data, attributes?.ToDictionary ()); } - public NSFileAttributes GetAttributes (string path, out NSError error) + public NSFileAttributes? GetAttributes (string path, out NSError error) { return NSFileAttributes.FromDictionary (_GetAttributes (path, out error)); } - public NSFileAttributes GetAttributes (string path) + public NSFileAttributes? GetAttributes (string path) { - NSError error; - return NSFileAttributes.FromDictionary (_GetAttributes (path, out error)); + return NSFileAttributes.FromDictionary (_GetAttributes (path, out var _)); } - public NSFileSystemAttributes GetFileSystemAttributes (string path) + public NSFileSystemAttributes? GetFileSystemAttributes (string path) { - NSError error; - return NSFileSystemAttributes.FromDictionary (_GetFileSystemAttributes (path, out error)); + return NSFileSystemAttributes.FromDictionary (_GetFileSystemAttributes (path, out var _)); } - public NSFileSystemAttributes GetFileSystemAttributes (string path, out NSError error) + public NSFileSystemAttributes? GetFileSystemAttributes (string path, out NSError error) { return NSFileSystemAttributes.FromDictionary (_GetFileSystemAttributes (path, out error)); } @@ -458,5 +452,32 @@ namespace Foundation { // ignore boolean return value set { ChangeCurrentDirectory (value); } } + + public static NSError SetSkipBackupAttribute (string filename, bool skipBackup) + { + if (filename is null) + throw new ArgumentNullException (nameof (filename)); + + using (var url = NSUrl.FromFilename (filename)) { + url.SetResource (NSUrl.IsExcludedFromBackupKey, (NSNumber) (skipBackup ? 1 : 0), out var error); + return error; + } + } + + public static bool GetSkipBackupAttribute (string filename) + { + return GetSkipBackupAttribute (filename, out var _); + } + + public static bool GetSkipBackupAttribute (string filename, out NSError error) + { + if (filename is null) + throw new ArgumentNullException (nameof (filename)); + + using (var url = NSUrl.FromFilename (filename)) { + url.TryGetResource (NSUrl.IsExcludedFromBackupKey, out var value, out error); + return (error is null) && ((long) ((NSNumber) value) == 1); + } + } } } diff --git a/src/Foundation/NSFileManager.iOS.cs b/src/Foundation/NSFileManager.iOS.cs deleted file mode 100644 index bc0bad6644..0000000000 --- a/src/Foundation/NSFileManager.iOS.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Extension to the NSFileManager class to allow users to set the attribute -// -// Copyright 2011-2012 Xamarin Inc. All rights reserved. -// - -#if !MONOMAC - -using System; -using System.Runtime.InteropServices; -using ObjCRuntime; -using UIKit; - -namespace Foundation { - public unsafe partial class NSFileManager { - - public static NSError SetSkipBackupAttribute (string filename, bool skipBackup) - { - if (filename is null) - throw new ArgumentNullException ("filename"); - - using (NSUrl url = NSUrl.FromFilename (filename)) { - NSError error; - url.SetResource (NSUrl.IsExcludedFromBackupKey, (NSNumber) (skipBackup ? 1 : 0), out error); - return error; - } - } - - public static bool GetSkipBackupAttribute (string filename) - { - NSError err; - - return GetSkipBackupAttribute (filename, out err); - } - - public static bool GetSkipBackupAttribute (string filename, out NSError error) - { - if (filename is null) - throw new ArgumentNullException ("filename"); - - using (NSUrl url = NSUrl.FromFilename (filename)) { - NSObject value; - url.TryGetResource (NSUrl.IsExcludedFromBackupKey, out value, out error); - return (error is null) && ((long) ((NSNumber) value) == 1); - } - } - } -} - -#endif // !MONOMAC diff --git a/src/frameworks.sources b/src/frameworks.sources index ce89266761..0f8fa7306c 100644 --- a/src/frameworks.sources +++ b/src/frameworks.sources @@ -821,7 +821,6 @@ FOUNDATION_SOURCES = \ Foundation/NSFastEnumerationState.cs \ Foundation/NSFastEnumerator.cs \ Foundation/NSFileManager.cs \ - Foundation/NSFileManager.iOS.cs \ Foundation/NSFileManagerDelegate.cs \ Foundation/NSFormatter.cs \ Foundation/NSHost.cs \ diff --git a/tests/bgen/bgen-tests.csproj b/tests/bgen/bgen-tests.csproj index c87dedea5b..83155368a9 100644 --- a/tests/bgen/bgen-tests.csproj +++ b/tests/bgen/bgen-tests.csproj @@ -5,6 +5,7 @@ bgen_tests false + Nullable @@ -29,12 +30,6 @@ BGenTool.cs - - AttributeFactory.ConstructorArguments.cs - - - AttributeFactory.cs - AttributeFactoryTests.cs diff --git a/tests/common/TestRuntime.cs b/tests/common/TestRuntime.cs index 3cf955252d..eb5f9bfa8b 100644 --- a/tests/common/TestRuntime.cs +++ b/tests/common/TestRuntime.cs @@ -478,6 +478,18 @@ partial class TestRuntime { return CheckMacSystemVersion (12, 1); #else throw new NotImplementedException (); +#endif + case 3: +#if __WATCHOS__ + return CheckWatchOSSystemVersion (8, 5); +#elif __TVOS__ + return ChecktvOSSystemVersion (15, 4); +#elif __IOS__ + return CheckiOSSystemVersion (15, 4); +#elif MONOMAC + return CheckMacSystemVersion (12, 3); +#else + throw new NotImplementedException (); #endif default: throw new NotImplementedException (); diff --git a/tests/generator/BGenTool.cs b/tests/generator/BGenTool.cs index c13c9b06b0..a2ba1221b5 100644 --- a/tests/generator/BGenTool.cs +++ b/tests/generator/BGenTool.cs @@ -353,14 +353,14 @@ namespace Xamarin.Tests { Assert.AreEqual (attributes.Value, m.Attributes, "Attributes for {0}", m.FullName); } - public void AssertNoMethod (string typename, string method, string returnType = null, params string [] parameterTypes) + public void AssertNoMethod (string typename, string method, string? returnType = null, params string [] parameterTypes) { var m = FindMethod (typename, method, returnType, parameterTypes); if (m is not null) Assert.Fail ($"Unexpectedly found method '{method}' with signature '{string.Join ("', '", parameterTypes)}' on the type '{typename}'."); } - MethodDefinition? FindMethod (string typename, string method, string returnType, params string [] parameterTypes) + MethodDefinition? FindMethod (string typename, string method, string? returnType, params string [] parameterTypes) { var assembly = LoadAssembly (); var t = assembly.MainModule.Types.FirstOrDefault ((v) => v.FullName == typename); diff --git a/tests/mmptest/src/MMPTest.cs b/tests/mmptest/src/MMPTest.cs index febc867097..37b88dbab4 100644 --- a/tests/mmptest/src/MMPTest.cs +++ b/tests/mmptest/src/MMPTest.cs @@ -611,7 +611,7 @@ namespace Xamarin.MMP.Tests { "Full", }; var rv = TI.TestUnifiedExecutable (test, shouldFail: false); - rv.Messages.AssertWarning (132, $"Unknown optimization: '{opt}'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock, register-protocols, inline-dynamic-registration-supported, static-block-to-delegate-lookup, trim-architectures, inline-is-arm64-calling-convention, cctor-beforefieldinit, custom-attributes-removal, experimental-xforms-product-type."); + rv.Messages.AssertWarning (132, $"Unknown optimization: '{opt}'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock, register-protocols, inline-dynamic-registration-supported, static-block-to-delegate-lookup, trim-architectures, inline-is-arm64-calling-convention, cctor-beforefieldinit, custom-attributes-removal, experimental-xforms-product-type, redirect-class-handles."); rv.Messages.AssertErrorCount (0); }); } diff --git a/tests/monotouch-test/CoreFoundation/BundleTest.cs b/tests/monotouch-test/CoreFoundation/BundleTest.cs index 01aa8caceb..2691e29a42 100644 --- a/tests/monotouch-test/CoreFoundation/BundleTest.cs +++ b/tests/monotouch-test/CoreFoundation/BundleTest.cs @@ -111,7 +111,8 @@ namespace MonoTouchFixtures.CoreFoundation { #else var executableRelativePath = Path.Combine (ExpectedAppName, "monotouchtest"); #endif - Assert.That (main.ExecutableUrl.ToString (), Contains.Substring (executableRelativePath)); + var alternativeRelativePath = executableRelativePath.Replace (ExpectedAppName, "PublicStaging.app"); + Assert.That (main.ExecutableUrl.ToString (), Does.Contain (executableRelativePath).Or.Contain (alternativeRelativePath)); } [Test] @@ -125,7 +126,7 @@ namespace MonoTouchFixtures.CoreFoundation { public void TestResourcesDirectoryUrl () { var main = CFBundle.GetMain (); - Assert.That (main.ResourcesDirectoryUrl.ToString (), Contains.Substring (ExpectedAppName + "/")); + Assert.That (main.ResourcesDirectoryUrl.ToString (), Does.Contain (ExpectedAppName + "/").Or.Contain ("PublicStaging.app/")); } [Test] @@ -146,7 +147,7 @@ namespace MonoTouchFixtures.CoreFoundation { public void TestSupportFilesDirectoryUrl () { var main = CFBundle.GetMain (); - Assert.That (main.SupportFilesDirectoryUrl.ToString (), Contains.Substring (ExpectedAppName + "/")); + Assert.That (main.SupportFilesDirectoryUrl.ToString (), Does.Contain (ExpectedAppName + "/").Or.Contain ("PublicStaging.app/")); } [Test] @@ -160,7 +161,7 @@ namespace MonoTouchFixtures.CoreFoundation { public void TestUrl () { var main = CFBundle.GetMain (); - Assert.That (main.Url.ToString (), Contains.Substring (ExpectedAppName + "/")); + Assert.That (main.Url.ToString (), Does.Contain (ExpectedAppName + "/").Or.Contain ("PublicStaging.app/")); } [Test] diff --git a/tests/monotouch-test/CoreFoundation/UrlTest.cs b/tests/monotouch-test/CoreFoundation/UrlTest.cs index 62438b5856..211270c2e8 100644 --- a/tests/monotouch-test/CoreFoundation/UrlTest.cs +++ b/tests/monotouch-test/CoreFoundation/UrlTest.cs @@ -8,6 +8,7 @@ // using System; +using System.IO; using Foundation; using CoreFoundation; using ObjCRuntime; @@ -29,7 +30,7 @@ namespace MonoTouchFixtures.CoreFoundation { [Test] public void RetainCountFromFile () { - var path = typeof (int).Assembly.Location; + var path = Path.Combine (Path.GetTempPath (), "placeholder.txt"); // the file doesn't have to exist, so just create any filename. using (var url = CFUrl.FromFile (path)) { Assert.That (TestRuntime.CFGetRetainCount (url.Handle), Is.EqualTo ((nint) 1), "RetainCount"); diff --git a/tests/monotouch-test/CoreGraphics/CGImagePropertiesGPSTest.cs b/tests/monotouch-test/CoreGraphics/CGImagePropertiesGPSTest.cs index a0146784b9..bbae026836 100644 --- a/tests/monotouch-test/CoreGraphics/CGImagePropertiesGPSTest.cs +++ b/tests/monotouch-test/CoreGraphics/CGImagePropertiesGPSTest.cs @@ -28,8 +28,8 @@ namespace monotouchtest.CoreGraphics { using (var url = NSUrl.FromFilename (file)) using (var ci = CIImage.FromUrl (url)) { var gps = ci.Properties.Gps; - Assert.AreEqual (expectedLatitude, gps.Latitude, "Invalid or no Latitude value found."); - Assert.AreEqual (expectedLongitude, gps.Longitude, "Invalid or no Longitude value found."); + Assert.AreEqual (expectedLatitude, gps.Latitude, 0.0001f, "Invalid or no Latitude value found."); + Assert.AreEqual (expectedLongitude, gps.Longitude, 0.0001f, "Invalid or no Longitude value found."); Assert.AreEqual (expectedLatitudeRef, gps.LatitudeRef, "Invalid or no LatitudeRef value found."); Assert.AreEqual (expectedLongitudeRef, gps.LongitudeRef, "Invalid or no LongitudeRef value found."); } diff --git a/tests/monotouch-test/Foundation/FileManagerTest.cs b/tests/monotouch-test/Foundation/FileManagerTest.cs index ad17d8ef6a..88217ca3dd 100644 --- a/tests/monotouch-test/Foundation/FileManagerTest.cs +++ b/tests/monotouch-test/Foundation/FileManagerTest.cs @@ -85,8 +85,6 @@ namespace MonoTouchFixtures.Foundation { // aborting is evil, so don't bother aborting the thread, just let it run its course } } - //GetSkipBackupAttribute doesn't exist on Mac -#if !MONOMAC [Test] public void GetSkipBackupAttribute () @@ -113,7 +111,6 @@ namespace MonoTouchFixtures.Foundation { File.Delete (filename); } } -#endif [Test] public void DefaultManager () diff --git a/tests/monotouch-test/Network/NWParametersTest.cs b/tests/monotouch-test/Network/NWParametersTest.cs index 60b962408a..2b31fc83e4 100644 --- a/tests/monotouch-test/Network/NWParametersTest.cs +++ b/tests/monotouch-test/Network/NWParametersTest.cs @@ -391,9 +391,9 @@ namespace MonoTouchFixtures.Network { } [Test] + [Ignore ("Crashes everywhere. Feedback filed: https://github.com/xamarin/maccore/issues/2675")] public void SetPrivacyContextTest () { - TestRuntime.AssertDevice (); TestRuntime.AssertXcodeVersion (13, 0); using (var privacy = NWPrivacyContext.Default) using (var parameters = new NWParameters ()) { diff --git a/tests/monotouch-test/Phase/PhaseObjectTest.cs b/tests/monotouch-test/Phase/PhaseObjectTest.cs index 6fe89b1895..f9796663d6 100644 --- a/tests/monotouch-test/Phase/PhaseObjectTest.cs +++ b/tests/monotouch-test/Phase/PhaseObjectTest.cs @@ -38,7 +38,7 @@ namespace MonoTouchFixtures.Phase { } [Test] - public void RigthTest () + public void RightTest () { var right = PhaseObject.Right; Assert.NotNull (right, "not null"); diff --git a/tests/monotouch-test/System.Net.Http/MessageHandlers.cs b/tests/monotouch-test/System.Net.Http/MessageHandlers.cs index 57649b17e6..01b6b1de3b 100644 --- a/tests/monotouch-test/System.Net.Http/MessageHandlers.cs +++ b/tests/monotouch-test/System.Net.Http/MessageHandlers.cs @@ -672,7 +672,7 @@ namespace MonoTests.System.Net.Http { TestRuntime.RunAsync (DateTime.Now.AddSeconds (30), async () => { try { - var result = await client.GetAsync ($"https://httpbin.org/basic-auth/{validUsername}/{validPassword}"); + var result = await client.GetAsync (NetworkResources.Httpbin.GetBasicAuthUrl (validUsername, validPassword)); httpStatus = result.StatusCode; } catch (Exception e) { ex = e; @@ -695,7 +695,7 @@ namespace MonoTests.System.Net.Http { { var username = "mandel"; var password = "12345678"; - var url = $"https://httpbin.org/basic-auth/{username}/{password}"; + var url = NetworkResources.Httpbin.GetBasicAuthUrl (username, password); // perform two requests, one that will get a 200 with valid creds, one that wont and assert that // the second call does get a 401 // create a http client to use with some creds that we do know are not valid diff --git a/tests/monotouch-test/System.Net.Http/NetworkResources.cs b/tests/monotouch-test/System.Net.Http/NetworkResources.cs index 98279327f0..66f0da266b 100644 --- a/tests/monotouch-test/System.Net.Http/NetworkResources.cs +++ b/tests/monotouch-test/System.Net.Http/NetworkResources.cs @@ -59,6 +59,7 @@ namespace MonoTests.System.Net.Http { public static string GetRelativeRedirectUrl (int count) => $"https://httpbin.org/relative-redirect/{count}"; public static string GetStatusCodeUrl (HttpStatusCode status) => $"http://httpbin.org/status/{(int) status}"; public static string GetSetCookieUrl (string cookie, string value) => $"https://httpbin.org/cookies/set?{cookie}={value}"; + public static string GetBasicAuthUrl (string username, string password) => $"{Url}/basic-auth/{username}/{password}"; } } diff --git a/tests/mtouch/MTouch.cs b/tests/mtouch/MTouch.cs index dc66a01bf1..eab573d088 100644 --- a/tests/mtouch/MTouch.cs +++ b/tests/mtouch/MTouch.cs @@ -1817,7 +1817,7 @@ public class TestApp { mtouch.Linker = MTouchLinker.LinkSdk; mtouch.Optimize = new string [] { "foo" }; mtouch.AssertExecute (MTouchAction.BuildSim, "build"); - mtouch.AssertWarning (132, "Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, inline-runtime-arch, blockliteral-setupblock, register-protocols, inline-dynamic-registration-supported, static-block-to-delegate-lookup, remove-dynamic-registrar, inline-is-arm64-calling-convention, seal-and-devirtualize, cctor-beforefieldinit, custom-attributes-removal, experimental-xforms-product-type, force-rejected-types-removal."); + mtouch.AssertWarning (132, "Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, inline-runtime-arch, blockliteral-setupblock, register-protocols, inline-dynamic-registration-supported, static-block-to-delegate-lookup, remove-dynamic-registrar, inline-is-arm64-calling-convention, seal-and-devirtualize, cctor-beforefieldinit, custom-attributes-removal, experimental-xforms-product-type, force-rejected-types-removal, redirect-class-handles."); } } @@ -3728,7 +3728,8 @@ public partial class NotificationService : UNNotificationServiceExtension mtouch.AssertWarning (2003, "Option '--optimize=custom-attributes-removal' will be ignored since linking is disabled"); mtouch.AssertWarning (2003, "Option '--optimize=experimental-xforms-product-type' will be ignored since linking is disabled"); mtouch.AssertWarning (2003, "Option '--optimize=force-rejected-types-removal' will be ignored since linking is disabled"); - mtouch.AssertWarningCount (16); + mtouch.AssertWarning (2003, "Option '--optimize=redirect-class-handles' will be ignored since the static registrar is not enabled"); + mtouch.AssertWarningCount (17); } using (var mtouch = new MTouchTool ()) { diff --git a/tools/common/Optimizations.cs b/tools/common/Optimizations.cs index ed7fa46b19..361ea1267d 100644 --- a/tools/common/Optimizations.cs +++ b/tools/common/Optimizations.cs @@ -27,6 +27,7 @@ namespace Xamarin.Bundler { "custom-attributes-removal", "experimental-xforms-product-type", "force-rejected-types-removal", + "redirect-class-handles", }; static ApplePlatform [] [] valid_platforms = new ApplePlatform [] [] { @@ -48,6 +49,7 @@ namespace Xamarin.Bundler { /* Opt.CustomAttributesRemoval */ new ApplePlatform [] { ApplePlatform.iOS, ApplePlatform.MacOSX, ApplePlatform.WatchOS, ApplePlatform.TVOS, ApplePlatform.MacCatalyst }, /* Opt.ExperimentalFormsProductType */ new ApplePlatform [] { ApplePlatform.iOS, ApplePlatform.MacOSX, ApplePlatform.WatchOS, ApplePlatform.TVOS, ApplePlatform.MacCatalyst }, /* Opt.ForceRejectedTypesRemoval */ new ApplePlatform [] { ApplePlatform.iOS, ApplePlatform.WatchOS, ApplePlatform.TVOS, ApplePlatform.MacCatalyst }, + /* Opt.RedirectClassHandles */ new ApplePlatform [] { ApplePlatform.iOS, ApplePlatform.MacOSX, ApplePlatform.WatchOS, ApplePlatform.TVOS, ApplePlatform.MacCatalyst }, }; enum Opt { @@ -69,6 +71,7 @@ namespace Xamarin.Bundler { CustomAttributesRemoval, ExperimentalFormsProductType, ForceRejectedTypesRemoval, + RedirectClassHandles, } bool? [] values; @@ -155,6 +158,11 @@ namespace Xamarin.Bundler { set { values [(int) Opt.ForceRejectedTypesRemoval] = value; } } + public bool? RedirectClassHandles { + get { return values [(int) Opt.RedirectClassHandles]; } + set { values [(int) Opt.RedirectClassHandles] = value; } + } + public Optimizations () { values = new bool? [opt_names.Length]; @@ -196,6 +204,7 @@ namespace Xamarin.Bundler { break; // Does not require linker case Opt.RegisterProtocols: case Opt.RemoveDynamicRegistrar: + case Opt.RedirectClassHandles: if (app.Registrar != RegistrarMode.Static && app.Registrar != RegistrarMode.ManagedStatic) { messages.Add (ErrorHelper.CreateWarning (2003, Errors.MT2003, (values [i].Value ? "" : "-"), opt_names [i])); values [i] = false; diff --git a/tools/devops/automation/scripts/run_mac_tests.ps1 b/tools/devops/automation/scripts/run_mac_tests.ps1 new file mode 100644 index 0000000000..d3546e9405 --- /dev/null +++ b/tools/devops/automation/scripts/run_mac_tests.ps1 @@ -0,0 +1,73 @@ +param +( + [Parameter(Mandatory)] + [String] + $GithubToken, + + [Parameter(Mandatory)] + [String] + $RepositoryUri, + + [Parameter(Mandatory)] + [String] + $SourcesDirectory, + + [Parameter(Mandatory)] + [String] + $GithubFailureCommentFile, + + [Parameter(Mandatory)] + [String] + $StatusContext +) + +Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\xamarin-macios\tools\devops\automation\scripts\MaciosCI.psd1 +$statuses = New-GitHubStatusesObjectFromUrl -Url "$RepositoryUri" -Token $GitHubToken + +Write-Host "Found tests" +$testsPath = "$SourcesDirectory/artifacts/mac-test-package/tests" +Write-Host "Tests path is $testsPath" + +# print enviroment +dir env: + +[System.Collections.Generic.List[string]]$failures = @() + +# Claim that the tests timed out before we start +Set-Content -Path "$GithubFailureCommentFile" -Value "Tests timed out" + +$macTest = @("dontlink", "introspection", "linksdk", "linkall", "xammac_tests", "monotouch-test") +foreach ($t in $macTest) { + $testName = "exec-$t" + Write-Host "Execution test $testName" + make -d -C $testsPath $testName -f packaged-macos-tests.mk + if ($LastExitCode -eq 0) { + Write-Host "$t succeeded" + } else { + Write-Host "$t failed with error $LastExitCode" + $failures.Add($t) + } +} +if ($failures.Count -ne 0) { + # post status and comment in the build + $failedTestsStr = [string]::Join(",",$failures) + $statuses.SetStatus("error", "Tests on macOS $StatusContext failed ($failedTestsStr).", "$StatusContext") + # build message + $msg = [System.Text.StringBuilder]::new() + $msg.AppendLine("Failed tests are:") + $msg.AppendLine("") + foreach ($test in $failures) + { + $msg.AppendLine("* $test") + } + + # We failed, so write to the comment file why we failed. + Set-Content -Path "$GithubFailureCommentFile" -Value "$msg" + + exit 1 +} else { + # We succeeded, so remove the failure comment file. + Remove-Item -Path "$GithubFailureCommentFile" + + exit 0 +} diff --git a/tools/devops/automation/templates/governance-checks.yml b/tools/devops/automation/templates/governance-checks.yml index aa50404720..71994afc4d 100644 --- a/tools/devops/automation/templates/governance-checks.yml +++ b/tools/devops/automation/templates/governance-checks.yml @@ -40,7 +40,7 @@ steps: - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: 'Component Detection' -- task: PoliCheck@1 +- task: PoliCheck@2 inputs: inputType: 'Basic' targetType: 'F' @@ -48,7 +48,7 @@ steps: result: 'PoliCheck.xml' optionsUEPATH: '$(System.DefaultWorkingDirectory)/maccore/tools/devops/PoliCheckExclusions.xml' -- task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@1 +- task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@2 displayName: 'Post Analysis' inputs: CredScan: true diff --git a/tools/devops/automation/templates/mac/build.yml b/tools/devops/automation/templates/mac/build.yml index c824dc0736..7cca873ead 100644 --- a/tools/devops/automation/templates/mac/build.yml +++ b/tools/devops/automation/templates/mac/build.yml @@ -162,57 +162,13 @@ steps: displayName: Install dependencies. timeoutInMinutes: 60 -- pwsh: | - Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\xamarin-macios\tools\devops\automation\scripts\MaciosCI.psd1 - $statuses = New-GitHubStatusesObjectFromUrl -Url "$(Build.Repository.Uri)" -Token $(GitHub.Token) - - Write-Host "Found tests" - $testsPath = "$(Build.SourcesDirectory)/artifacts/mac-test-package/tests" - Write-Host "Tests path is $testsPath" - - # print enviroment - dir env: - - [System.Collections.Generic.List[string]]$failures = @() - - # Claim that the tests timed out before we start - Set-Content -Path "$Env:GITHUB_FAILURE_COMMENT_FILE" -Value "Tests timed out" - - $macTest = @("dontlink", "introspection", "linksdk", "linkall", "xammac_tests", "monotouch-test") - foreach ($t in $macTest) { - $testName = "exec-$t" - Write-Host "Execution test $testName" - make -d -C $testsPath $testName -f packaged-macos-tests.mk - if ($LastExitCode -eq 0) { - Write-Host "$t succeeded" - } else { - Write-Host "$t failed with error $LastExitCode" - $failures.Add($t) - } - } - if ($failures.Count -ne 0) { - # post status and comment in the build - $failedTestsStr = [string]::Join(",",$failures) - $statuses.SetStatus("error", "Tests on macOS ${{ parameters.statusContext }} failed ($failedTestsStr).", "${{ parameters.statusContext }}") - # build message - $msg = [System.Text.StringBuilder]::new() - $msg.AppendLine("Failed tests are:") - $msg.AppendLine("") - foreach ($test in $failures) - { - $msg.AppendLine("* $test") - } - - # We failed, so write to the comment file why we failed. - Set-Content -Path "$Env:GITHUB_FAILURE_COMMENT_FILE" -Value "$msg" - - exit 1 - } else { - # We succeeded, so remove the failure comment file. - Remove-Item -Path "$Env:GITHUB_FAILURE_COMMENT_FILE" - - exit 0 - } +- pwsh: >- + $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/automation/scripts/run_mac_tests.ps1 + -GithubToken $(GitHub.Token) + -RepositoryUri "$(Build.Repository.Uri)" + -SourcesDirectory "$(Build.SourcesDirectory)" + -GithubFailureCommentFile "$Env:GITHUB_FAILURE_COMMENT_FILE" + -StatusContext "${{ parameters.statusContext }}" displayName: 'Run tests' timeoutInMinutes: 60 env: diff --git a/tools/devops/automation/templates/tests/run-tests.yml b/tools/devops/automation/templates/tests/run-tests.yml index 684a25ca3f..ad0ecdaffd 100644 --- a/tools/devops/automation/templates/tests/run-tests.yml +++ b/tools/devops/automation/templates/tests/run-tests.yml @@ -301,7 +301,7 @@ steps: displayName: 'Publish Artifact: All binlogs' inputs: targetPath: $(Build.ArtifactStagingDirectory)/all-binlogs - artifactName: ${{ parameters.uploadPrefix }}all-binlogs-test-${{ parameters.testPrefix }}-$(Build.BuildId)-$(System.JobAttempt)' + artifactName: ${{ parameters.uploadPrefix }}all-binlogs-test-${{ parameters.testPrefix }}-$(Build.BuildId)-$(System.JobAttempt) continueOnError: true condition: succeededOrFailed() diff --git a/tools/devops/automation/templates/tests/stage.yml b/tools/devops/automation/templates/tests/stage.yml index 72ae86b3c5..ee647d42bf 100644 --- a/tools/devops/automation/templates/tests/stage.yml +++ b/tools/devops/automation/templates/tests/stage.yml @@ -117,53 +117,59 @@ stages: uploadArtifacts: false enableDotnet: ${{ parameters.enableDotnet }} - - ${{ each label in parameters.simTestsConfigurations }}: - - job: "tests_${{ replace(label, '-', '_') }}" - ${{ if eq(parameters.parseLabels, true) }}: - condition: ne(dependencies.configure.outputs['labels.skip_all_tests'], 'True') - dependsOn: - - ${{ if eq(parameters.parseLabels, true) }}: - - configure - displayName: 'Running ${{ label}} - ${{ parameters.displayName }}' - timeoutInMinutes: 1000 + - job: "tests" + ${{ if eq(parameters.parseLabels, true) }}: + condition: ne(dependencies.configure.outputs['labels.skip_all_tests'], 'True') + dependsOn: + - ${{ if eq(parameters.parseLabels, true) }}: + - configure + displayName: 'Running tests:' + timeoutInMinutes: 1000 + variables: + # old and ugly env var use by jenkins, we do have parts of the code that use it, contains the PR number + PR_ID: $[ dependencies.configure.outputs['labels.pr_number'] ] + # set the branch variable name, this is required by jenkins and we have a lot of scripts that depend on it + BRANCH_NAME: $[ replace(variables['Build.SourceBranch'], 'refs/heads/', '') ] + XHARNESS_LABELS: $[ dependencies.configure.outputs['labels.xharness_labels'] ] + ${{ if eq(parameters.testPool, '') }}: + AgentPoolComputed: $(PRBuildPool) + ${{ else }}: + AgentPoolComputed: ${{ parameters.testPool }} - variables: - # old and ugly env var use by jenkins, we do have parts of the code that use it, contains the PR number - PR_ID: $[ dependencies.configure.outputs['labels.pr_number'] ] - # set the branch variable name, this is required by jenkins and we have a lot of scripts that depend on it - BRANCH_NAME: $[ replace(variables['Build.SourceBranch'], 'refs/heads/', '') ] - XHARNESS_LABELS: $[ dependencies.configure.outputs['labels.xharness_labels'] ] - ${{ if eq(parameters.testPool, '') }}: - AgentPoolComputed: $(PRBuildPool) - ${{ else }}: - AgentPoolComputed: ${{ parameters.testPool }} - - pool: - name: $(AgentPoolComputed) - demands: - - Agent.OS -equals Darwin - - macOS.Name -equals ${{ parameters.macOSName }} - - XcodeChannel -equals ${{ parameters.XcodeChannel }} - - ${{ each demand in parameters.extraBotDemands }}: - - demand - workspace: - clean: all - steps: - - template: build.yml - parameters: - isPR: ${{ parameters.isPR }} - repositoryAlias: ${{ parameters.repositoryAlias }} - commit: ${{ parameters.commit }} - label: ${{ label }} - testsLabels: '${{ parameters.testsLabels }},run-${{ label }}-tests' - statusContext: '${{ parameters.statusContext }} - ${{ label }}' - useXamarinStorage: ${{ parameters.useXamarinStorage }} - vsdropsPrefix: ${{ parameters.vsdropsPrefix }} - keyringPass: ${{ parameters.keyringPass }} - testPrefix: "${{ parameters.testPrefix }}${{ replace(label, '-', '_') }}" - makeTarget: ${{ parameters.makeTarget }} - gitHubToken: ${{ parameters.gitHubToken }} - xqaCertPass: ${{ parameters.xqaCertPass }} + pool: + name: $(AgentPoolComputed) + demands: + - Agent.OS -equals Darwin + - macOS.Name -equals ${{ parameters.macOSName }} + - XcodeChannel -equals ${{ parameters.XcodeChannel }} + - ${{ each demand in parameters.extraBotDemands }}: + - demand + workspace: + clean: all + strategy: + matrix: + ${{ each label in parameters.simTestsConfigurations }}: + ${{ replace(label, '-', '_') }}: + LABEL: ${{ label }} + TESTS_LABELS: '${{ parameters.testsLabels }},run-${{ label }}-tests' + STATUS_CONTEXT: '${{ parameters.statusContext }} - ${{ label }}' + TEST_PREFIX: "${{ parameters.testPrefix }}${{ replace(label, '-', '_') }}" + steps: + - template: build.yml + parameters: + isPR: ${{ parameters.isPR }} + repositoryAlias: ${{ parameters.repositoryAlias }} + commit: ${{ parameters.commit }} + label: $(LABEL) + testsLabels: $(TESTS_LABELS) + statusContext: $(STATUS_CONTEXT) + useXamarinStorage: ${{ parameters.useXamarinStorage }} + vsdropsPrefix: ${{ parameters.vsdropsPrefix }} + keyringPass: ${{ parameters.keyringPass }} + testPrefix: $(TEST_PREFIX) + makeTarget: ${{ parameters.makeTarget }} + gitHubToken: ${{ parameters.gitHubToken }} + xqaCertPass: ${{ parameters.xqaCertPass }} - job: publish_test_results ${{ if eq(parameters.parseLabels, true) }}: @@ -173,17 +179,16 @@ stages: displayName: 'GitHub comment - Publish results' timeoutInMinutes: 1000 dependsOn: # has to wait for the tests to be done AND the data to be uploaded + - tests - ${{ if eq(parameters.parseLabels, true) }}: - configure - - ${{ each label in parameters.simTestsConfigurations }}: - - tests_${{ replace(label, '-', '_') }} variables: GIT_HASH: $[ stageDependencies.build_packages.build.outputs['fix_commit.GIT_HASH'] ] ${{ each label in parameters.simTestsConfigurations }}: # Define the variable FOO from the previous job # Note the use of single quotes! - TESTS_BOT_${{ upper(replace(label, '-', '_')) }}: $[ dependencies.tests_${{ replace(label, '-', '_') }}.outputs['runTests.TESTS_BOT'] ] - TESTS_JOBSTATUS_${{ upper(replace(label, '-', '_')) }}: $[ dependencies.tests_${{ replace(label, '-', '_') }}.outputs['runTests.TESTS_JOBSTATUS'] ] + TESTS_BOT_${{ upper(replace(label, '-', '_')) }}: $[ dependencies.tests.outputs['${{ replace(label, '-', '_')}}.runTests.TESTS_BOT'] ] + TESTS_JOBSTATUS_${{ upper(replace(label, '-', '_')) }}: $[ dependencies.tests.outputs['${{ replace(label, '-', '_')}}.runTests.TESTS_JOBSTATUS'] ] pool: vmImage: 'windows-latest' workspace: diff --git a/tools/devops/automation/templates/windows/build.yml b/tools/devops/automation/templates/windows/build.yml index 6f66fd646e..557a2d985b 100644 --- a/tools/devops/automation/templates/windows/build.yml +++ b/tools/devops/automation/templates/windows/build.yml @@ -152,9 +152,34 @@ steps: --filter Category=Windows ` --verbosity quiet ` --settings $(Build.SourcesDirectory)/xamarin-macios/tests/dotnet/Windows/config.runsettings ` + "--results-directory:$(Build.SourcesDirectory)/xamarin-macios/jenkins-results/" ` + "--logger:console;verbosity=detailed" ` + "--logger:trx;LogFileName=$(Build.SourcesDirectory)/xamarin-macios/jenkins-results/windows-dotnet-tests.trx" ` + "--logger:html;LogFileName=$(Build.SourcesDirectory)/xamarin-macios/jenkins-results/windows-dotnet-tests.html" ` "-bl:$(Build.SourcesDirectory)/xamarin-macios/tests/dotnet/Windows/run-dotnet-tests.binlog" displayName: 'Run .NET tests' +# Archive files for the Html Report so that the report can be easily uploaded as artifacts of the build. +- task: ArchiveFiles@1 + displayName: 'Archive HtmlReport' + inputs: + rootFolder: '$(Build.SourcesDirectory)/xamarin-macios/jenkins-results' + includeRootFolder: false + archiveFile: '$(Build.ArtifactStagingDirectory)/HtmlReport.zip' + continueOnError: true + condition: succeededOrFailed() + +# Create HtmlReport artifact. This serves two purposes: +# 1. It is the way we are going to share the HtmlReport with the publish_html job that is executed on a Windows machine. +# 2. Users can download this if they want. +- task: PublishPipelineArtifact@1 + displayName: 'Publish Artifact: HtmlReport' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)/HtmlReport.zip' + artifactName: '${{ parameters.uploadPrefix }}HtmlReport-windows-integration-$(System.JobAttempt)' + continueOnError: true + condition: succeededOrFailed() + - pwsh: | Write-Host "Run windows tests." Write-Host "Mac agent to be used:"