Skip to content

Commit 88984ab

Browse files
[#72] Add find-refs test coverage and fix its database/null-ref bugs (#79)
* Introduce second UnityProject Rename UnityFileSystemTestData and introduce a LeadingEdge project for testing Content Directory builds and other newer features that require Unity >=6.6. Add AGENTS.md explaining the relationship between these projects and the referenced files checked in to TestCommon * Dump command tested with core numeric field types and other serialization primitives * [#72] Fix find-refs database open and null-ref crashes, add test coverage find-refs had no automated tests and two bugs that broke it against any current analyze database: - Issue #72: the database was opened with a hand-written connection string using the legacy System.Data.SQLite "Version=3" keyword, which Microsoft.Data.Sqlite rejects. Build the connection string with SqliteConnectionStringBuilder instead (matching SQLiteWriter). Fixed the same broken string in ExpectedDataGenerator. - A ScriptableObject is a MonoBehaviour whose m_GameObject PPtr is 0, so its game_object column is a non-null id matching no row; the game_object/script subqueries then return NULL and GetString threw. Null-check those reads. Also add a --stdout option to find-refs (mutually exclusive with -o), mirroring dump, and update command-find-refs.md. Add FindRefsTests covering both bugs, name/type lookup and disambiguation, object-id lookup, --find-all, missing objects, empty refs table, the --stdout/-o validator, and direct refs-table queries. Tests run against the LeadingEdge AssetBundle reference build.
1 parent f55ea6f commit 88984ab

133 files changed

Lines changed: 2820 additions & 47 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@
3232

3333
.claude
3434

35-
UnityFileSystemTestData/AssetBundles/
36-
UnityFileSystemTestData/**/*.csproj
37-
UnityFileSystemTestData/**/*.sln
38-
UnityFileSystemTestData/ProjectSettings/
39-
UnityFileSystemTestData/UserSettings/
40-
UnityFileSystemTestData/Packages/
35+
UnityProjects/**/*.csproj
36+
UnityProjects/**/*.sln
37+
UnityProjects/**/AssetBundles/
38+
UnityProjects/**/ProjectSettings/
39+
UnityProjects/**/UserSettings/
40+
UnityProjects/**/Packages/
41+
UnityProjects/**/Builds/
4142
*.db
4243
*.csv
4344

AGENTS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,6 @@ To use a specific Unity version's library:
174174

175175
* TestCommon/Data contains small reference files extracted from Unity builds (player and AssetBundles). These are used by the automated tests and also useful for manual testing.
176176

177-
* UnityFileSystemTestData is a Unity project that generates test data for the test suites.
177+
* UnityProjects contains two Unity projects that generate test data for the test suites:
178+
* `Baseline` - tracks stable, broadly-used Unity versions and is upgraded only when necessary. Most UnityDataTools users inspect output from these versions. Its `TypeIdRegistryGenerator` regenerates `UnityFileSystem/TypeIdRegistry.cs`.
179+
* `LeadingEdge` - tracks the newest Unity version (currently the 6.6 beta) and is updated proactively so newer build features (Content Directory builds, serialized dictionaries, etc.) can be tested.

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
AGENTS.md
1+
@AGENTS.md

Documentation/command-find-refs.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# find-refs Command
22

3-
> ⚠️ **Experimental:** This command may not work as expected in all cases.
4-
53
The `find-refs` command traces reference chains leading to specific objects. Use it to understand why an asset was included (and potentially duplicated) in a build.
64

5+
It walks *up* the reference graph from the target object and **stops at the first asset it reaches**. The reported chains therefore end at the immediate containing asset, not at the ultimate root that transitively depends on the target. For example, if `RootAsset` references `LeafAsset` which references a texture, searching for the texture reports the chain ending at `LeafAsset`; to see that `RootAsset` pulls it in, search for `LeafAsset` instead.
6+
77
## Quick Reference
88

99
```
@@ -16,10 +16,13 @@ UnityDataTool find-refs <database> [options]
1616
| `-i, --object-id <id>` | ID of object to analyze (from `id` column) ||
1717
| `-n, --object-name <name>` | Name of objects to analyze ||
1818
| `-t, --object-type <type>` | Type filter when using `-n` ||
19-
| `-o, --output-file <file>` | Output filename ||
19+
| `-o, --output-file <file>` | Output filename | `references.txt` |
20+
| `--stdout` | Write the reference chains to stdout instead of a file | `false` |
2021
| `-a, --find-all` | Find all chains instead of stopping at first | `false` |
2122

2223
> **Note:** Either `--object-id` or `--object-name` must be provided.
24+
>
25+
> `--stdout` and `-o/--output-file` are mutually exclusive.
2326
2427
## Prerequisites
2528

@@ -29,14 +32,19 @@ This command requires a database created by the [`analyze`](command-analyze.md)
2932

3033
## Examples
3134

32-
Find references to an object by name and type:
35+
Find references to an object by name and type, printing directly to the console:
36+
```bash
37+
UnityDataTool find-refs my_database.db -n "MyTexture" -t "Texture2D" --stdout
38+
```
39+
40+
Write the reference chains to a file instead:
3341
```bash
3442
UnityDataTool find-refs my_database.db -n "MyTexture" -t "Texture2D" -o refs.txt
3543
```
3644

3745
Find references to a specific object by ID:
3846
```bash
39-
UnityDataTool find-refs my_database.db -i 12345 -o refs.txt
47+
UnityDataTool find-refs my_database.db -i 12345 --stdout
4048
```
4149

4250
Find all duplicate references (useful for finding why an asset is duplicated):

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The repository contains the following items:
2525
* UnityFileSystem: source code and binaries of a .NET class library exposing the functionalities or the
2626
UnityFileSystemApi native library.
2727
* UnityFileSystem.Tests: test suite for the UnityFileSystem library.
28-
* UnityFileSystemTestData: the Unity project used to generate the test data.
28+
* UnityProjects: Unity projects used to generate some of the test data.
2929
* TestCommon: a helper library used by the test projects.
3030

3131
## Downloads

ReferenceFinder/ReferenceFinderTool.cs

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,14 @@ public class ReferenceFinderTool
2424
List<ReferenceTreeNode> m_Roots = new List<ReferenceTreeNode>();
2525
HashSet<(long, string)> m_ProcessedObjects = new HashSet<(long, string)>();
2626

27-
StreamWriter m_Writer;
27+
TextWriter m_Writer;
2828

29-
public int FindReferences(string objectName, string objectType, string databasePath, string outputFile, bool findAll)
29+
public int FindReferences(string objectName, string objectType, string databasePath, string outputFile, bool findAll, bool toStdout = false)
3030
{
3131
var objectIds = new List<long>();
32-
SqliteConnection db;
33-
34-
try
35-
{
36-
db = new SqliteConnection($"Data Source={databasePath};Version=3;Foreign Keys=False;");
37-
db.Open();
38-
}
39-
catch (Exception e)
32+
using var db = OpenDatabase(databasePath);
33+
if (db == null)
4034
{
41-
Console.WriteLine($"Error opening database: {e.Message}");
4235
return 1;
4336
}
4437

@@ -81,33 +74,50 @@ public int FindReferences(string objectName, string objectType, string databaseP
8174
return 1;
8275
}
8376

84-
return FindReferences(db, outputFile, objectIds, findAll);
77+
return FindReferences(db, outputFile, objectIds, findAll, toStdout);
8578
}
8679

87-
public int FindReferences(long objectId, string databasePath, string outputFile, bool findAll)
80+
public int FindReferences(long objectId, string databasePath, string outputFile, bool findAll, bool toStdout = false)
8881
{
8982
var objectIds = new List<long>();
90-
SqliteConnection db;
83+
using var db = OpenDatabase(databasePath);
84+
if (db == null)
85+
{
86+
return 1;
87+
}
9188

89+
objectIds.Add(objectId);
90+
91+
return FindReferences(db, outputFile, objectIds, findAll, toStdout);
92+
}
93+
94+
// Opens the analyze database for reading. Uses SqliteConnectionStringBuilder (matching SQLiteWriter) rather than a
95+
// hand-written connection string, which used a legacy System.Data.SQLite keyword that Microsoft.Data.Sqlite rejects.
96+
static SqliteConnection OpenDatabase(string databasePath)
97+
{
9298
try
9399
{
94-
db = new SqliteConnection($"Data Source={databasePath};Version=3;Foreign Keys=False;");
100+
var connectionString = new SqliteConnectionStringBuilder
101+
{
102+
DataSource = databasePath,
103+
Mode = SqliteOpenMode.ReadOnly,
104+
Pooling = false,
105+
ForeignKeys = false,
106+
}.ConnectionString;
107+
var db = new SqliteConnection(connectionString);
95108
db.Open();
109+
return db;
96110
}
97111
catch (Exception e)
98112
{
99113
Console.WriteLine($"Error opening database: {e.Message}");
100-
return 1;
114+
return null;
101115
}
102-
103-
objectIds.Add(objectId);
104-
105-
return FindReferences(db, outputFile, objectIds, findAll);
106116
}
107117

108-
int FindReferences(SqliteConnection db, string outputFile, IList<long> objectIds, bool findAll)
118+
int FindReferences(SqliteConnection db, string outputFile, IList<long> objectIds, bool findAll, bool toStdout)
109119
{
110-
m_Writer = new StreamWriter(outputFile);
120+
m_Writer = toStdout ? Console.Out : new StreamWriter(outputFile);
111121

112122
m_GetRefsCommand = db.CreateCommand();
113123
m_GetRefsCommand.CommandText = @"SELECT object, property_path, EXISTS (SELECT * FROM assets a WHERE a.object = r.object) FROM refs r WHERE referenced_object = @id";
@@ -181,7 +191,11 @@ FROM object_view o
181191
}
182192
}
183193

184-
m_Writer.Close();
194+
// Don't close Console.Out when writing to stdout; just flush it.
195+
if (toStdout)
196+
m_Writer.Flush();
197+
else
198+
m_Writer.Close();
185199

186200
return 0;
187201
}
@@ -196,10 +210,13 @@ void OutputReferenceNode(ReferenceTreeNode node, string propertyPath, int indent
196210
{
197211
reader.Read();
198212

213+
// game_object and script come from correlated subqueries that yield NULL when there is no matching row
214+
// (e.g. a ScriptableObject is a MonoBehaviour whose m_GameObject PPtr is 0, or a MonoBehaviour with no
215+
// m_Script reference), so both must be null-checked.
199216
var objectType = reader.GetString(0);
200217
var objectName = reader.GetString(1);
201-
var gameObject = reader.GetString(2);
202-
var script = reader.GetString(3);
218+
var gameObject = reader.IsDBNull(2) ? "" : reader.GetString(2);
219+
var script = reader.IsDBNull(3) ? "" : reader.GetString(3);
203220

204221
if (propertyPath != "")
205222
{
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# AssetBundle test data
2+
3+
AssetBundle build output used by the UnityDataTools test suites, kept in a subfolder per Unity version (`2019.4.0f1` through `2023.1.0a16`). Each holds the `assetbundle` and `scenes` bundles built from the project's assets; some versions also include additional fixtures used by specific tests.
4+
5+
Generated by the `UnityProjects/Baseline` project - run **Tools > Generate AssetBundles** there (built for StandaloneOSX and copied here under the Unity version). See that project's `AGENTS.md` for the assets that go into the bundles.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# LeadingEdge reference builds
2+
3+
The checked-in build output of the `UnityProjects/LeadingEdge` project, for use in automated and ad hoc tests. See that project's `AGENTS.md` for the test scenarios and how the assets are set up.
4+
5+
The LeadingEdge build scripts regenerate this folder directly, so to update it, rebuild in that project and check in the results.
6+
7+
## Layout
8+
9+
* `AssetBundles/` - the AssetBundle build: one bundle per asset (named after the asset) plus the `AssetBundles` manifest bundle.
10+
* `ContentDirectory/` - the Content Directory build: content (`.cf`) files, `.resource` files and the build manifest.
11+
* `BuildReport-AssetBundles/LastBuild.buildreport` - the AssetBundle build report.
12+
* `BuildReport-ContentDirectory/` - the Content Directory build report folder, including `ContentLayout.json`.
29.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)