Skip to content

Commit 4dc7ec2

Browse files
Test coverage for typetree parsing in metadata
1 parent 75eb7be commit 4dc7ec2

1 file changed

Lines changed: 133 additions & 1 deletion

File tree

Analyzer.Tests/FileDetectionTests.cs

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using System.Linq;
34
using NUnit.Framework;
45
using UnityDataTools.Analyzer.Util;
56
using UnityDataTools.FileSystem;
@@ -189,6 +190,42 @@ public void TryParseMetadata_PlayerDataLevel0_ReturnsExpectedValues()
189190
Assert.That(metadata.UnityVersion, Is.EqualTo("2022.1.20f1"), "Unity version should be 2022.1.20f1");
190191
Assert.That(metadata.TargetPlatform, Is.EqualTo(2u), "Target platform should be 2 (Windows Standalone)");
191192
Assert.IsTrue(metadata.EnableTypeTree, "EnableTypeTree should be true");
193+
194+
// --- TypeTree counts ---
195+
Assert.That(metadata.TypeTreeCount, Is.EqualTo(10), "Should have 10 regular type entries");
196+
Assert.That(metadata.SerializedReferenceTypeTreeCount, Is.EqualTo(0), "Should have 0 SerializeReference type entries");
197+
Assert.IsNotNull(metadata.TypeTrees, "TypeTrees should be populated");
198+
Assert.That(metadata.TypeTrees.Length, Is.EqualTo(10));
199+
200+
// --- Per-entry invariants for a player scene with no MonoBehaviours ---
201+
// All types are native Unity types: inline TypeTrees, no script IDs, no ref-type fields.
202+
foreach (var entry in metadata.TypeTrees)
203+
{
204+
Assert.IsTrue(entry.InlineTypeTree,
205+
$"InlineTypeTree should be true (persistentTypeID={entry.PersistentTypeID})");
206+
Assert.IsFalse(entry.OldTypeHash.IsZero,
207+
$"OldTypeHash should not be zero (persistentTypeID={entry.PersistentTypeID})");
208+
Assert.IsTrue(entry.TypeTreeContentHash.IsZero,
209+
$"TypeTreeContentHash should be zero for version < 23 (persistentTypeID={entry.PersistentTypeID})");
210+
Assert.Greater(entry.TypeTreeSerializedSize, 0u,
211+
$"TypeTreeSerializedSize should be non-zero (persistentTypeID={entry.PersistentTypeID})");
212+
Assert.Greater(entry.PersistentTypeID, 0,
213+
$"PersistentTypeID should be positive for native types (got {entry.PersistentTypeID})");
214+
Assert.AreNotEqual(114, entry.PersistentTypeID,
215+
"No MonoBehaviour types expected in this scene");
216+
Assert.That(entry.ScriptTypeIndex, Is.EqualTo((short)-1),
217+
$"ScriptTypeIndex should be -1 for native types (persistentTypeID={entry.PersistentTypeID})");
218+
Assert.IsTrue(entry.ScriptID.IsZero,
219+
$"ScriptID should be zero for native types (persistentTypeID={entry.PersistentTypeID})");
220+
Assert.That(entry.ClassName, Is.EqualTo(string.Empty),
221+
$"ClassName should be empty for non-ref types (persistentTypeID={entry.PersistentTypeID})");
222+
Assert.That(entry.Namespace, Is.EqualTo(string.Empty),
223+
$"Namespace should be empty for non-ref types (persistentTypeID={entry.PersistentTypeID})");
224+
Assert.That(entry.AssemblyName, Is.EqualTo(string.Empty),
225+
$"AssemblyName should be empty for non-ref types (persistentTypeID={entry.PersistentTypeID})");
226+
Assert.That(entry.TypeDependencies.Length, Is.EqualTo(0),
227+
$"TypeDependencies should be empty (persistentTypeID={entry.PersistentTypeID})");
228+
}
192229
}
193230

194231
[Test]
@@ -210,6 +247,101 @@ public void TryParseMetadata_PlayerNoTypeTreeLevel1_ReturnsExpectedValues()
210247
Assert.That(metadata.UnityVersion, Is.EqualTo("6000.0.65f1"), "Unity version should be 6000.0.65f1");
211248
Assert.That(metadata.TargetPlatform, Is.EqualTo(19u), "Target platform should be 19 (Windows Standalone x64)");
212249
Assert.IsFalse(metadata.EnableTypeTree, "EnableTypeTree should be false for a no-type-tree build");
250+
251+
// Even when TypeTrees are not stored inline, the metadata still records the full list of
252+
// types used in the file along with their oldTypeHash values. The hashes allow the runtime
253+
// to verify type compatibility against its built-in type definitions at load time.
254+
Assert.That(metadata.TypeTreeCount, Is.EqualTo(6), "Should have 6 type entries");
255+
Assert.IsNotNull(metadata.TypeTrees, "TypeTrees should be populated");
256+
Assert.That(metadata.TypeTrees.Length, Is.EqualTo(6));
257+
258+
foreach (var entry in metadata.TypeTrees)
259+
{
260+
Assert.Greater(entry.PersistentTypeID, 0,
261+
$"PersistentTypeID should be positive (got {entry.PersistentTypeID})");
262+
Assert.IsFalse(entry.OldTypeHash.IsZero,
263+
$"OldTypeHash should not be zero (persistentTypeID={entry.PersistentTypeID})");
264+
Assert.IsFalse(entry.InlineTypeTree,
265+
$"InlineTypeTree should be false when EnableTypeTree=false (persistentTypeID={entry.PersistentTypeID})");
266+
Assert.IsTrue(entry.TypeTreeContentHash.IsZero,
267+
$"TypeTreeContentHash should be zero for this version < 23 file (persistentTypeID={entry.PersistentTypeID})");
268+
}
269+
}
270+
271+
[Test]
272+
public void TryParseMetadata_V22PrefabWithSerializedReference_ReturnsExpectedTypeTreeData()
273+
{
274+
var testFile = Path.Combine(m_TestDataPath, "AssetBundleTypeTreeVariations", "v22",
275+
"prefab_with_serializedreference.serializedfile");
276+
277+
bool headerResult = SerializedFileDetector.TryDetectSerializedFile(testFile, out var headerInfo);
278+
Assert.IsTrue(headerResult, "File should be detected as a valid SerializedFile");
279+
280+
bool result = SerializedFileDetector.TryParseMetadata(testFile, headerInfo, out var metadata, out var errorMessage);
281+
Assert.IsTrue(result, $"Metadata parsing should succeed. Error: {errorMessage}");
282+
Assert.IsNotNull(metadata);
283+
284+
// --- Initial metadata fields ---
285+
Assert.IsTrue(metadata.EnableTypeTree, "EnableTypeTree should be true");
286+
287+
// --- Type counts ---
288+
Assert.That(metadata.TypeTreeCount, Is.EqualTo(4), "Should have 4 regular type entries");
289+
Assert.That(metadata.SerializedReferenceTypeTreeCount, Is.EqualTo(1), "Should have 1 SerializeReference type entry");
290+
Assert.IsNotNull(metadata.TypeTrees, "TypeTrees array should be populated");
291+
Assert.IsNotNull(metadata.SerializedReferenceTypeTrees, "SerializedReferenceTypeTrees array should be populated");
292+
293+
// --- Regular type entries: persistentTypeIDs in order ---
294+
int[] expectedTypeIDs = { 142, 4, 1, 114 };
295+
Assert.That(metadata.TypeTrees.Length, Is.EqualTo(expectedTypeIDs.Length));
296+
for (int i = 0; i < expectedTypeIDs.Length; i++)
297+
Assert.That(metadata.TypeTrees[i].PersistentTypeID, Is.EqualTo(expectedTypeIDs[i]),
298+
$"TypeTrees[{i}].PersistentTypeID");
299+
300+
// --- v22 files do not store TypeTreeContentHash (it is all-zeros) ---
301+
foreach (var entry in metadata.TypeTrees)
302+
Assert.IsTrue(entry.TypeTreeContentHash.IsZero,
303+
$"TypeTreeContentHash should be zero for v22 (persistentTypeID={entry.PersistentTypeID})");
304+
foreach (var entry in metadata.SerializedReferenceTypeTrees)
305+
Assert.IsTrue(entry.TypeTreeContentHash.IsZero,
306+
"SerializedReferenceTypeTrees TypeTreeContentHash should be zero for v22");
307+
308+
// --- All type trees are inline (non-zero size, InlineTypeTree=true) ---
309+
foreach (var entry in metadata.TypeTrees)
310+
{
311+
Assert.IsTrue(entry.InlineTypeTree,
312+
$"InlineTypeTree should be true (persistentTypeID={entry.PersistentTypeID})");
313+
Assert.Greater(entry.TypeTreeSerializedSize, 0u,
314+
$"TypeTreeSerializedSize should be non-zero (persistentTypeID={entry.PersistentTypeID})");
315+
}
316+
foreach (var entry in metadata.SerializedReferenceTypeTrees)
317+
{
318+
Assert.IsTrue(entry.InlineTypeTree, "SerializedReferenceTypeTrees[0].InlineTypeTree should be true");
319+
Assert.Greater(entry.TypeTreeSerializedSize, 0u,
320+
"SerializedReferenceTypeTrees[0].TypeTreeSerializedSize should be non-zero");
321+
}
322+
323+
// --- MonoBehaviour (114) has special entries because it refers to a specific C# class ---
324+
// Note: if multiple C# MonoBehaviour-derived types were used in this serialized files then we would have multiple entries.
325+
var monoBehaviour = metadata.TypeTrees.First(t => t.PersistentTypeID == 114);
326+
Assert.IsFalse(monoBehaviour.ScriptID.IsZero,
327+
"MonoBehaviour type entry should carry a non-zero scriptID");
328+
329+
Assert.That(monoBehaviour.ScriptTypeIndex, Is.EqualTo(0),
330+
"MonoBehaviour type entry should have a valid ScriptTypeIndex"); // -1 is used for non-script types, so 0 is the first valid index
331+
332+
Assert.That(monoBehaviour.TypeDependencies.Length, Is.EqualTo(1),
333+
"MonoBehaviour should have TypeDependencies array because to record SerializedReference dependencies");
334+
335+
Assert.That(monoBehaviour.TypeDependencies[0], Is.EqualTo(0),
336+
"MonoBehaviour should record dependency on SerializedReference");
337+
338+
// --- SerializedReference type entry ---
339+
Assert.That(metadata.SerializedReferenceTypeTrees.Length, Is.EqualTo(1));
340+
var refType = metadata.SerializedReferenceTypeTrees[0];
341+
Assert.That(refType.PersistentTypeID, Is.EqualTo(-1));
342+
Assert.That(refType.ClassName, Is.EqualTo("Data"));
343+
Assert.That(refType.Namespace, Is.EqualTo("MyScripts"));
344+
Assert.That(refType.AssemblyName, Is.EqualTo("Assembly-CSharp"));
213345
}
214346

215347
#endregion
@@ -287,7 +419,7 @@ public void IsUnityArchive_ValidAssetBundle_ReturnsTrue()
287419
[Test]
288420
public void IsUnityArchive_OldFormatArchive_ReturnsTrue()
289421
{
290-
var testFile = Path.Combine(m_TestDataPath, "LegacyFormats", "alienprefab");
422+
var testFile = Path.Combine(m_TestDataPath, "LegacyFormats", "AssetBundles", "alienprefab");
291423

292424
bool result = ArchiveDetector.IsUnityArchive(testFile);
293425

0 commit comments

Comments
 (0)