string FindDevice(params string [] valid_classes)
        {
            if (device_xml == null)
            {
                var cachedir = Cache.CreateTemporaryDirectory();
                var xmlpath  = Path.Combine(cachedir, "devices.xml");
                Asserts.RunProcess("/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/bin/mlaunch", $"--listdev={Embedder.Quote (xmlpath)} --output-format=xml", "mlaunch --listdev");
                device_xml = new XmlDocument();
                device_xml.Load(xmlpath);
            }
            var nodes   = device_xml.SelectNodes("/MTouch/Device[" + string.Join(" or ", valid_classes.Select((v) => "DeviceClass = \"" + v + "\"")) + "]/DeviceIdentifier");
            var devices = new List <string> ();

            foreach (XmlNode node in nodes)
            {
                devices.Add(node.InnerText);
            }
            if (devices.Count == 0)
            {
                return(string.Empty);
            }

            devices.Sort(StringComparer.Ordinal);              // Sort all applicable devices so the same test run always runs on the same device.
            return(devices [0]);
        }
        string CompileLibrary(Platform platform, string code = null, string libraryName = null)
        {
            int exitCode;

            if (libraryName == null)
            {
                libraryName = "library";
            }
            var tmpdir   = Xamarin.Cache.CreateTemporaryDirectory();
            var cs_path  = Path.Combine(tmpdir, libraryName + ".cs");
            var dll_path = Path.Combine(tmpdir, libraryName + ".dll");

            if (code == null)
            {
                code = "public class Test { public static void X () {} }";
            }

            File.WriteAllText(cs_path, code);

            if (!Embedder.RunProcess("/Library/Frameworks/Mono.framework/Versions/Current/bin/csc", $"/target:library {Embedder.Quote (cs_path)} /out:{Embedder.Quote (dll_path)}", out exitCode))
            {
                Assert.Fail("Failed to compile test code");
            }

            return(dll_path);
        }
        void RunManagedTests(Platform platform, string test_destination = "", bool debug = true)
        {
            string        dllname;
            string        dlldir;
            string        abi;
            List <string> defines              = new List <string> ();
            var           managedTestCount     = CountTests(Path.Combine(XcodeProjectGenerator.TestsRootDirectory, "objc-cli/libmanaged/Tests/Tests.m"));
            var           xamariniOSTestCount  = CountTests(Path.Combine(XcodeProjectGenerator.TestsRootDirectory, "objcgentest/xcodetemplate/ios/test/iosTests.m"));
            var           xamarinMacTestCount  = CountTests(Path.Combine(XcodeProjectGenerator.TestsRootDirectory, "objcgentest/xcodetemplate/macos/test/macTests.m"));
            var           xamarintvOSTestCount = CountTests(Path.Combine(XcodeProjectGenerator.TestsRootDirectory, "objcgentest/xcodetemplate/tvos/test/tvosTests.m"));

            switch (platform)
            {
            case Platform.macOSFull:
                dlldir  = "macos-full";
                dllname = "managed-macos-full.dll";
                defines.Add("XAMARIN_MAC=1");
                defines.Add("XAMARIN_MAC_FULL=1");
                abi = "x86_64";                 // FIXME: fat XM apps not supported yet
                managedTestCount += xamarinMacTestCount;
                break;

            case Platform.macOSSystem:
                dlldir  = "macos-system";
                dllname = "managed-macos-system.dll";
                defines.Add("XAMARIN_MAC=1");
                defines.Add("XAMARIN_MAC_SYSTEM=1");
                abi = "x86_64";                 // FIXME: fat XM apps not supported yet
                managedTestCount += xamarinMacTestCount;
                break;

            case Platform.macOSModern:
                dlldir  = "macos-modern";
                dllname = "managed-macos-modern.dll";
                defines.Add("XAMARIN_MAC=1");
                defines.Add("XAMARIN_MAC_MODERN=1");
                abi = "x86_64";                 // FIXME: fat XM apps not supported yet
                managedTestCount += xamarinMacTestCount;
                break;

            case Platform.macOS:
                dlldir  = "generic";
                dllname = "managed.dll";
                abi     = "i386,x86_64";
                break;

            case Platform.iOS:
                dlldir  = "ios";
                dllname = "managed-ios.dll";
                defines.Add("XAMARIN_IOS=1");
                abi = "armv7,arm64,i386,x86_64";
                managedTestCount += xamariniOSTestCount;
                break;

            case Platform.tvOS:
                dlldir  = "tvos";
                dllname = "managed-tvos.dll";
                defines.Add("XAMARIN_TVOS=1");
                abi = "arm64,x86_64";
                managedTestCount += xamarintvOSTestCount;
                break;

            default:
                throw new NotImplementedException();
            }
            defines.Add("TEST_FRAMEWORK=1");

            var tmpdir        = Cache.CreateTemporaryDirectory();
            var configuration = debug ? "Debug" : "Release";
            var dll_path      = Path.Combine(XcodeProjectGenerator.TestsRootDirectory, "managed", dlldir, "bin", configuration, dllname);

            // This will build all the managed.dll variants, which is easier than calculating the relative path _as the makefile sees it_ to pass as the target.
            Asserts.RunProcess("make", $"all CONFIG={configuration} -C {Embedder.Quote (Path.Combine (XcodeProjectGenerator.TestsRootDirectory, "managed"))}", "build " + Path.GetFileName(dll_path));

            var outdir      = tmpdir + "/out";
            var projectName = "foo";
            var args        = new List <string> ();

            if (debug)
            {
                args.Add("--debug");
            }
            args.Add(dll_path);
            args.Add("-c");
            args.Add($"--outdir={outdir}");
            args.Add("--target=framework");
            args.Add($"--platform={platform}");
            args.Add($"--abi={abi}");
            Asserts.Generate("generate", args.ToArray());

            var framework_path   = Path.Combine(outdir, Path.GetFileNameWithoutExtension(dll_path) + ".framework");
            var projectDirectory = XcodeProjectGenerator.Generate(platform, tmpdir, projectName, framework_path, defines: defines.ToArray());

            string output;
            var    builddir = Path.Combine(tmpdir, "xcode-build-dir");

            Asserts.RunProcess("xcodebuild", $"test -project {Embedder.Quote (projectDirectory)} -scheme Tests {test_destination} CONFIGURATION_BUILD_DIR={Embedder.Quote (builddir)}", out output, "run xcode tests");
            // assert the number of tests passed, so that we can be sure we actually ran all the tests. Otherwise it's very easy to ignore when a bug is causing tests to not be built.
            Assert.That(output, Does.Match($"Test Suite 'All tests' passed at .*\n\t Executed {managedTestCount} tests, with 0 failures"), "test count");
        }
        public void EM0013()
        {
            var tmpdir  = Xamarin.Cache.CreateTemporaryDirectory();
            var csfile  = Path.Combine(tmpdir, "foo.cs");
            var dllfile = Path.Combine(tmpdir, "foo.dll");

            File.WriteAllText(csfile, @"public class C { public Foundation.NSObject F () {  throw new System.NotImplementedException (); } }");
            Asserts.RunProcess("/Library/Frameworks/Mono.framework/Commands/csc", $"/target:library /out:{Embedder.Quote (dllfile)} {Embedder.Quote (csfile)} -r:/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/lib/mono/Xamarin.iOS/Xamarin.iOS.dll", "compile dll");
            Asserts.ThrowsEmbeddinatorException(13, "Can't find the assembly 'Xamarin.iOS, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065', referenced by 'foo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.", () => Driver.Main2(dllfile, "--platform=tvOS", "--outdir=" + tmpdir));
        }