using System; using AssetBank.Editor.SchemaConverter.Data.Output; using AssetBank.Editor.Tools; using AssetBank.Settings; using System.Diagnostics; using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; using UnityEditor; using UnityEngine; using Debug = UnityEngine.Debug; namespace AssetBank.Editor.SchemaConverter.Tests { [TestFixture] public class SchemaConverterTests { private const string SampleScenePath = "Assets/Scenes/SampleScene.unity"; private string _tempDirectory; private string _rawJsonPath; private string _trimmedJsonPath; private string _hierarchyJsonPath; private string _hierarchyAndPrefabsJsonPath; private string _fullGraphJsonPath; [SetUp] public void SetUp() { _tempDirectory = Path.Combine("Library", "SchemaConverterTestTemp"); Directory.CreateDirectory(_tempDirectory); _rawJsonPath = Path.Combine(_tempDirectory, "raw_scene.json"); _trimmedJsonPath = Path.Combine(_tempDirectory, "trimmed_scene.json"); _hierarchyJsonPath = Path.Combine(_tempDirectory, "hierarchy_only.json"); _hierarchyAndPrefabsJsonPath = Path.Combine(_tempDirectory, "hierarchy_and_prefabs.json"); _fullGraphJsonPath = Path.Combine(_tempDirectory, "full_graph.json"); } [TearDown] public void TearDown() { if (Directory.Exists(_tempDirectory)) { Directory.Delete(_tempDirectory, true); } } [Test, Order(1)] public void CanConvertSceneToRawJson() { var pythonExe = FindPythonExecutable(); var conversionScript = GetConversionScriptPath(); Assert.IsNotNull(pythonExe, "Python executable not found."); Assert.IsNotNull(conversionScript, "Conversion script 'convert_scene.py' not found."); var success = ExecutePythonConversion(SampleScenePath, _rawJsonPath, pythonExe, conversionScript); Assert.IsTrue(success, "Python conversion script failed to execute."); Assert.IsTrue(File.Exists(_rawJsonPath), "Raw JSON file was not created."); Assert.Greater(new FileInfo(_rawJsonPath).Length, 0, "Raw JSON file is empty."); Debug.Log($"Successfully created raw JSON at: {_rawJsonPath}"); } [Test, Order(2)] public void CanTrimRawJson() { // Ensure the first step has run and created the file. if (!File.Exists(_rawJsonPath)) { CanConvertSceneToRawJson(); } Assert.IsTrue(File.Exists(_rawJsonPath), "Prerequisite step 'CanConvertSceneToRawJson' failed to produce an output file."); var rawJson = File.ReadAllText(_rawJsonPath); var settings = ProjectExporterSettings.GetOrCreateSettings(); var trimmedJson = JsonDataTrimmer.Process(rawJson, settings); File.WriteAllText(_trimmedJsonPath, trimmedJson); Assert.IsTrue(File.Exists(_trimmedJsonPath), "Trimmed JSON file was not created."); Assert.Greater(new FileInfo(_trimmedJsonPath).Length, 0, "Trimmed JSON file is empty."); Debug.Log($"Successfully created trimmed JSON at: {_trimmedJsonPath}"); } [Test, Order(3)] public void CanProcessHierarchyOnly() { // Ensure the prerequisite step has run. if (!File.Exists(_trimmedJsonPath)) { CanTrimRawJson(); } Assert.IsTrue(File.Exists(_trimmedJsonPath), "Prerequisite step 'CanTrimRawJson' failed to produce an output file."); var trimmedJson = File.ReadAllText(_trimmedJsonPath); // Run the conversion but only use the hierarchy part of the result. var sceneGraph = JsonSchemaConverter.ConvertToSceneGraph(trimmedJson); var hierarchyJson = JsonConvert.SerializeObject(sceneGraph.hierarchy, Formatting.Indented); File.WriteAllText(_hierarchyJsonPath, hierarchyJson); Assert.IsTrue(File.Exists(_hierarchyJsonPath), "Hierarchy-only JSON file was not created."); Assert.Greater(new FileInfo(_hierarchyJsonPath).Length, 2, "Hierarchy-only JSON file is empty (should be at least '[]')."); Debug.Log($"Successfully created hierarchy-only JSON at: {_hierarchyJsonPath}"); } [Test, Order(4)] public void CanProcessHierarchyAndPrefabs() { // Ensure the prerequisite step has run. if (!File.Exists(_trimmedJsonPath)) { CanTrimRawJson(); } Assert.IsTrue(File.Exists(_trimmedJsonPath), "Prerequisite step 'CanTrimRawJson' failed to produce an output file."); var trimmedJson = File.ReadAllText(_trimmedJsonPath); // Run the conversion and serialize the hierarchy and prefabs sections. var sceneGraph = JsonSchemaConverter.ConvertToSceneGraph(trimmedJson); var result = new JObject { ["hierarchy"] = JToken.FromObject(sceneGraph.hierarchy), ["prefabs"] = JToken.FromObject(sceneGraph.prefabs) }; var outputJson = result.ToString(Formatting.Indented); File.WriteAllText(_hierarchyAndPrefabsJsonPath, outputJson); Assert.IsTrue(File.Exists(_hierarchyAndPrefabsJsonPath), "Hierarchy and prefabs JSON file was not created."); Assert.Greater(new FileInfo(_hierarchyAndPrefabsJsonPath).Length, 2, "Hierarchy and prefabs JSON file is empty."); Debug.Log($"Successfully created hierarchy and prefabs JSON at: {_hierarchyAndPrefabsJsonPath}"); } [Test, Order(5)] public void CanProcessFullGraphSuccessfully() { // Ensure the prerequisite step has run. if (!File.Exists(_trimmedJsonPath)) { CanTrimRawJson(); } Assert.IsTrue(File.Exists(_trimmedJsonPath), "Prerequisite step 'CanTrimRawJson' failed to produce an output file."); var trimmedJson = File.ReadAllText(_trimmedJsonPath); // This test now asserts that the conversion completes WITHOUT throwing an exception. string finalJson = null; Assert.DoesNotThrow(() => { finalJson = JsonSchemaConverter.Convert(trimmedJson); }, "The conversion process threw an unexpected exception."); File.WriteAllText(_fullGraphJsonPath, finalJson); Assert.IsTrue(File.Exists(_fullGraphJsonPath), "Full graph JSON file was not created."); Assert.Greater(new FileInfo(_fullGraphJsonPath).Length, 2, "Full graph JSON file is empty."); // A good final check is to ensure no warnings were generated. var sceneGraph = JsonConvert.DeserializeObject(finalJson); Assert.IsNull(sceneGraph.metadata.warnings, "The conversion generated unexpected warnings."); Debug.Log($"Successfully created full graph JSON at: {_fullGraphJsonPath}"); } #region Helper Methods private string FindPythonExecutable() { var projectRoot = Path.GetDirectoryName(Application.dataPath); var venvPath = Path.Combine(projectRoot, "venv", "bin", "python3"); if (File.Exists(venvPath)) return venvPath; venvPath = Path.Combine(projectRoot, "venv", "bin", "python"); if (File.Exists(venvPath)) return venvPath; // Fallback for system python return "python3"; } private string GetConversionScriptPath() { var guids = AssetDatabase.FindAssets("convert_scene"); if (guids.Length == 0) return null; var projectRoot = Path.GetDirectoryName(Application.dataPath); return Path.Combine(projectRoot, AssetDatabase.GUIDToAssetPath(guids[0])); } private bool ExecutePythonConversion(string assetPath, string jsonOutputPath, string pythonExe, string scriptPath) { var projectRoot = Path.GetDirectoryName(Application.dataPath); var absoluteAssetPath = Path.Combine(projectRoot, assetPath); var arguments = $"\"{scriptPath}\" \"{absoluteAssetPath}\" \"{jsonOutputPath}\""; var process = new Process { StartInfo = new ProcessStartInfo { FileName = pythonExe, Arguments = arguments, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true } }; process.Start(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); if (process.ExitCode == 0) return true; Debug.LogError($"Failed to convert {assetPath}. Error: {error}"); return false; } #endregion } }