using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
namespace AutomaticDoorSystem.Tests
{
///
/// Guards the package's own serialized content (sample scenes and door prefabs) against the
/// two ways it can rot silently:
/// 1. A MonoBehaviour class is deleted or renamed while scenes still reference it - Unity
/// turns those into "missing script" slots that only show up when someone opens the scene.
/// This is what retiring DoorIdentifier risked, so the check now lives in CI instead of
/// in someone's memory.
/// 2. A sample door loses its audio profile, making it silent at runtime with no error.
/// Scoped to this package's folder on purpose: a consuming project's unrelated missing scripts
/// are not this package's failure to report.
///
public class DoorContentIntegrityTests
{
/// Matches every serialized component reference: m_Script: {fileID: n, guid: h, type: 3}.
private static readonly Regex ScriptReference =
new Regex(@"m_Script: \{fileID: -?\d+, guid: ([0-9a-f]{32}), type: \d+\}", RegexOptions.Compiled);
private static string _packageRoot;
[OneTimeSetUp]
public void LocatePackage()
{
_packageRoot = FindPackageRoot();
Assert.IsNotNull(_packageRoot,
"Could not locate the AutomaticDoorSystem package root (no package.json above DoorAuthoring.cs).");
}
[Test]
public void EverySerializedScriptReferenceResolves()
{
var dangling = new List();
foreach (var file in SerializedFiles())
{
var text = File.ReadAllText(file);
foreach (Match match in ScriptReference.Matches(text))
{
var guid = match.Groups[1].Value;
if (string.IsNullOrEmpty(AssetDatabase.GUIDToAssetPath(guid)))
{
dangling.Add($"{ToAssetPath(file)} references missing script guid {guid}");
}
}
}
Assert.IsEmpty(dangling,
"Serialized content points at scripts that no longer exist. Either restore the class or " +
"strip the leftover components from the listed files:\n " + string.Join("\n ", dangling));
}
[Test]
public void EverySampleDoorHasAnAudioConfig()
{
var silent = new List();
var checkedDoors = 0;
foreach (var file in SerializedFiles())
{
if (!file.EndsWith(".prefab")) continue;
var prefab = AssetDatabase.LoadAssetAtPath(ToAssetPath(file));
if (prefab == null) continue;
foreach (var door in prefab.GetComponentsInChildren(true))
{
checkedDoors++;
if (door.doorAudioConfig == null)
{
silent.Add($"{ToAssetPath(file)} -> '{door.name}' (door id {door.doorId})");
}
}
}
Assert.Greater(checkedDoors, 0, "No DoorAuthoring found in the package's prefabs - the scan is not looking where it should.");
Assert.IsEmpty(silent,
"These sample doors have no DoorAudioConfiguration and will be silent at runtime:\n " +
string.Join("\n ", silent));
}
private static IEnumerable SerializedFiles()
{
foreach (var pattern in new[] { "*.unity", "*.prefab" })
{
foreach (var file in Directory.GetFiles(_packageRoot, pattern, SearchOption.AllDirectories))
{
yield return file;
}
}
}
/// Absolute path back to the "Assets/..." or "Packages/..." form AssetDatabase speaks.
private static string ToAssetPath(string absolute)
{
var normalized = absolute.Replace('\\', '/');
var projectRoot = Directory.GetParent(Application.dataPath).FullName.Replace('\\', '/') + "/";
return normalized.StartsWith(projectRoot) ? normalized.Substring(projectRoot.Length) : normalized;
}
///
/// Walks up from DoorAuthoring.cs to the folder holding package.json, so the test works
/// both when the package is embedded under Assets and when it is installed from the registry.
///
private static string FindPackageRoot()
{
foreach (var guid in AssetDatabase.FindAssets("DoorAuthoring t:MonoScript"))
{
var path = AssetDatabase.GUIDToAssetPath(guid);
if (!path.EndsWith("/DoorAuthoring.cs")) continue;
var directory = Directory.GetParent(Path.GetFullPath(path));
while (directory != null)
{
if (File.Exists(Path.Combine(directory.FullName, "package.json")))
{
return directory.FullName;
}
directory = directory.Parent;
}
}
return null;
}
}
}