private void runTest(string testPath) { // Android var testFile = testPath + @"\" + Path.GetFileName(testPath) + ".so"; if (!File.Exists(testFile)) { testFile = testPath + @"\libil2cpp.so"; } // Windows if (!File.Exists(testFile)) { testFile = testPath + @"\" + Path.GetFileName(testPath) + ".dll"; } if (!File.Exists(testFile)) { testFile = testPath + @"\GameAssembly.dll"; } // iOS if (!File.Exists(testFile)) { testFile = testPath + @"\" + Path.GetFileName(testPath); } var inspectors = Il2CppInspector.LoadFromFile(testFile, testPath + @"\global-metadata.dat"); // If null here, there was a problem parsing the files if (inspectors == null) { throw new Exception("Could not understand IL2CPP binary or metadata"); } if (inspectors.Count == 0) { throw new Exception("Could not find any images in the IL2CPP binary"); } // Dump each image in the binary separately int i = 0; foreach (var il2cpp in inspectors) { var model = new TypeModel(il2cpp); var appModel = new AppModel(model, makeDefaultBuild: false).Build(compiler: CppCompilerType.MSVC); var nameSuffix = i++ > 0 ? "-" + (i - 1) : ""; new CSharpCodeStubs(model) { ExcludedNamespaces = Constants.DefaultExcludedNamespaces, SuppressMetadata = false, MustCompile = true }.WriteSingleFile(testPath + $@"\test-result{nameSuffix}.cs"); new JSONMetadata(appModel) .Write(testPath + $@"\test-result{nameSuffix}.json"); new CppScaffolding(appModel) .Write(testPath + $@"\test-cpp-result{nameSuffix}"); var python = new PythonScript(appModel); foreach (var target in PythonScript.GetAvailableTargets()) { python.WriteScriptToFile(testPath + $@"\test-{target.ToLower()}{nameSuffix}.py", target, testPath + $@"\test-cpp-result{nameSuffix}\appdata\il2cpp-types.h", testPath + $@"\test-result{nameSuffix}.json"); } } // Compare test results with expected results for (i = 0; i < inspectors.Count; i++) { var suffix = (i > 0 ? "-" + i : ""); compareFiles(testPath, suffix + ".cs", $"test-result{suffix}.cs"); compareFiles(testPath, suffix + ".json", $"test-result{suffix}.json"); compareFiles(testPath, suffix + ".h", $@"test-cpp-result{suffix}\appdata\il2cpp-types.h"); } }
// Test runner private async Task runTest(string testPath, LoadOptions loadOptions = null) { // Android var testFile = testPath + @"\" + Path.GetFileName(testPath) + ".so"; if (!File.Exists(testFile)) { testFile = testPath + @"\libil2cpp.so"; } // Windows if (!File.Exists(testFile)) { testFile = testPath + @"\" + Path.GetFileName(testPath) + ".dll"; } if (!File.Exists(testFile)) { testFile = testPath + @"\GameAssembly.dll"; } // iOS if (!File.Exists(testFile)) { testFile = testPath + @"\" + Path.GetFileName(testPath); } // Linux process map if (!File.Exists(testFile)) { testFile = Directory.GetFiles(testPath, "*-maps.txt").FirstOrDefault(); } // XAPK (selects latest version assuming lexical order) if (testFile == null) { testFile = Directory.GetFiles(testPath, "*.xapk").LastOrDefault(); } // APK (selects latest version assuming lexical order) (prefer XAPKs) if (testFile == null) { testFile = Directory.GetFiles(testPath, "*.apk").LastOrDefault(); } // Set load options if (loadOptions == null) { loadOptions = new LoadOptions(); } loadOptions.BinaryFilePath = testFile; // Load core plugins PluginManager.Reload(testPath + @"\..\plugins", coreOnly: true); // Set up additional plugins - place desired plugins in 'plugins' sub-folder of test folder try { PluginManager.Reload(testPath + @"\plugins", reset: false); } catch (DirectoryNotFoundException) { } // Handlers for debugging output PluginManager.StatusHandler += (s, e) => { Console.WriteLine("Plugin " + e.Plugin.Name + ": " + e.Text); }; PluginManager.ErrorHandler += (s, e) => { Assert.Fail($"{e.Error.Plugin.Name} throw an exception during {e.Error.Operation}: {e.Error.Exception.Message}.", e); }; // Get plugin options - place desired options in <plugins-id>.options.txt for each plugin in test folder // in the format "key=value", one per line // Use %TESTDIR% to refer to the directory containing the test files foreach (var plugin in PluginManager.AvailablePlugins) { Console.WriteLine("Using plugin: " + plugin.Name); // Enable plugin var ourPlugin = PluginManager.Plugins[plugin.Id]; ourPlugin.Enabled = true; // Look for options var optionsFile = $@"{testPath}\{plugin.Id}-options.txt"; if (File.Exists(optionsFile)) { var options = File.ReadAllLines(optionsFile); // Parse options foreach (var option in options) { var kv = option.Split('=', 2); if (kv.Length == 2) { var key = kv[0].Trim(); var value = kv[1].Trim().Replace("%TESTDIR%", testPath); Console.WriteLine($"Setting option: {key} = {value}"); // Null default values must be castable to object ourPlugin.Plugin.Options.Single(o => o.Name == key).SetFromString(value); } } } PluginManager.OptionsChanged(plugin); } List <Il2CppInspector> inspectors; using (new Benchmark("Load IL2CPP metadata and binary")) try { inspectors = Il2CppInspector.LoadFromFile(testFile, testPath + @"\global-metadata.dat", loadOptions); } catch (FileNotFoundException) { inspectors = Il2CppInspector.LoadFromPackage(new[] { testFile }, loadOptions); } // If null here, there was a problem parsing the files if (inspectors == null) { throw new Exception("Could not understand IL2CPP binary or metadata"); } if (inspectors.Count == 0) { throw new Exception("Could not find any images in the IL2CPP binary"); } // End if we were only testing file load if (!GenerateCpp && !GenerateCS && !GenerateDLL && !GenerateJSON && !GeneratePython) { return; } // Dump each image in the binary separately var imageTasks = inspectors.Select((il2cpp, i) => Task.Run(async() => { TypeModel model; using (new Benchmark("Create .NET type model")) model = new TypeModel(il2cpp); Task <AppModel> appModelTask = null; if (GenerateCpp || GenerateJSON || GeneratePython) { appModelTask = Task.Run(() => { using (new Benchmark("Create application model")) return(new AppModel(model, makeDefaultBuild: false).Build(compiler: CppCompilerType.MSVC)); }); } var nameSuffix = i++ > 0 ? "-" + (i - 1) : ""; var generateTasks = new List <Task>(); var compareTasks = new List <Task>(); if (GenerateCS) { generateTasks.Add(Task.Run(() => { using (new Benchmark("Create C# code stubs")) new CSharpCodeStubs(model) { ExcludedNamespaces = Constants.DefaultExcludedNamespaces, SuppressMetadata = false, MustCompile = true }.WriteSingleFile(testPath + $@"\test-result{nameSuffix}.cs"); compareTasks.Add(Task.Run(() => compareFiles(testPath, nameSuffix + ".cs", $"test-result{nameSuffix}.cs"))); })); } if (GenerateDLL) { generateTasks.Add(Task.Run(() => { using (new Benchmark("Create .NET assembly shims")) new AssemblyShims(model).Write(testPath + $@"\test-dll-result{nameSuffix}"); compareTasks.Add(Task.Run(() => compareBinaryFiles(testPath + $@"\test-dll-result{nameSuffix}", testPath + @"\..\..\TestExpectedResults\dll-" + Path.GetFileName(testPath) + nameSuffix))); })); } AppModel appModel = null; if (appModelTask != null) { appModel = await appModelTask; } if (GenerateJSON || GeneratePython) { generateTasks.Add(Task.Run(() => { using (new Benchmark("Create JSON metadata")) new JSONMetadata(appModel) .Write(testPath + $@"\test-result{nameSuffix}.json"); compareTasks.Add(Task.Run(() => compareFiles(testPath, nameSuffix + ".json", $"test-result{nameSuffix}.json"))); })); } if (GenerateCpp || GeneratePython) { generateTasks.Add(Task.Run(() => { using (new Benchmark("Create C++ scaffolding")) new CppScaffolding(appModel) .Write(testPath + $@"\test-cpp-result{nameSuffix}"); compareTasks.Add(Task.Run(() => compareFiles(testPath, nameSuffix + ".h", $@"test-cpp-result{nameSuffix}\appdata\il2cpp-types.h"))); })); } if (GeneratePython) { generateTasks.Add(Task.Run(() => { var python = new PythonScript(appModel); foreach (var target in PythonScript.GetAvailableTargets()) { python.WriteScriptToFile(testPath + $@"\test-{target.ToLower()}{nameSuffix}.py", target, testPath + $@"\test-cpp-result{nameSuffix}\appdata\il2cpp-types.h", testPath + $@"\test-result{nameSuffix}.json"); } })); } await Task.WhenAll(generateTasks); await Task.WhenAll(compareTasks); })); await Task.WhenAll(imageTasks); }