diff --git a/AGENTS.md b/AGENTS.md index 91166bc..7cc600d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -132,6 +132,7 @@ CLI entry point is `UnityDataTool/Program.cs` using System.CommandLine. Per-comm ### Extending Analyze * New Unity types can be added by following the same pattern as the existing types, for example MonoScripts. +* Any database schema change (new or changed tables, views, or columns) must bump `PRAGMA user_version` in `Analyzer/Resources/Init.sql` and extend the version-history comment above it. * Analysis of additional file formats could be added, for example AssetBundle manifest files by following the pattern of Addressables build layout files are handled. ### Other Extensions diff --git a/Analyzer/Resources/Init.sql b/Analyzer/Resources/Init.sql index 43f6652..666ab77 100644 --- a/Analyzer/Resources/Init.sql +++ b/Analyzer/Resources/Init.sql @@ -203,8 +203,10 @@ INSERT INTO types (id, name) VALUES (-1, 'Scene'); -- assetbundle_assets/preload_dependencies (issue #82); 3 = renamed asset_bundles table to archives -- and the asset_bundle column/alias to archive (issue #68); 4 = build_report_packed_asset_contents_view -- type column changed from numeric id to type name (issue #55); 5 = added dangling_refs table/view --- (issue #85); 6 = archives.name is unique (issue #51); databases produced before versioning report 0. -PRAGMA user_version = 6; +-- (issue #85); 6 = archives.name is unique (issue #51); 7 = Unity 6.6 build_reports columns and +-- build_report_content_* tables (issue #107), asset_name/asset_extension columns on +-- build_report_source_assets (issue #110); databases produced before versioning report 0. +PRAGMA user_version = 7; PRAGMA synchronous = OFF; PRAGMA journal_mode = MEMORY; diff --git a/Analyzer/Resources/PackedAssets.sql b/Analyzer/Resources/PackedAssets.sql index e717b0e..58c14c4 100644 --- a/Analyzer/Resources/PackedAssets.sql +++ b/Analyzer/Resources/PackedAssets.sql @@ -9,6 +9,10 @@ CREATE TABLE IF NOT EXISTS build_report_source_assets( id INTEGER PRIMARY KEY AUTOINCREMENT, source_asset_guid TEXT NOT NULL, build_time_asset_path TEXT NOT NULL, + -- Derived from build_time_asset_path for convenient grouping: the filename without its + -- extension, and the lower-cased extension without the dot (empty when there is none). + asset_name TEXT NOT NULL, + asset_extension TEXT NOT NULL, UNIQUE(source_asset_guid, build_time_asset_path) ); @@ -52,6 +56,8 @@ SELECT pac.offset, sa.source_asset_guid, sa.build_time_asset_path, + sa.asset_name, + sa.asset_extension, br_obj.id as build_report_id FROM build_report_packed_asset_info pac LEFT JOIN build_report_packed_assets pa ON pac.packed_assets_id = pa.id diff --git a/Analyzer/SQLite/Handlers/PackedAssetsHandler.cs b/Analyzer/SQLite/Handlers/PackedAssetsHandler.cs index 72d7f34..de3a21e 100644 --- a/Analyzer/SQLite/Handlers/PackedAssetsHandler.cs +++ b/Analyzer/SQLite/Handlers/PackedAssetsHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using Microsoft.Data.Sqlite; using UnityDataTools.Analyzer.SerializedObjects; using UnityDataTools.FileSystem; @@ -43,12 +44,14 @@ public void Init(SqliteConnection db) m_InsertSourceAssetCommand = db.CreateCommand(); m_InsertSourceAssetCommand.CommandText = @"INSERT OR IGNORE INTO build_report_source_assets( - source_asset_guid, build_time_asset_path + source_asset_guid, build_time_asset_path, asset_name, asset_extension ) VALUES( - @source_asset_guid, @build_time_asset_path + @source_asset_guid, @build_time_asset_path, @asset_name, @asset_extension )"; m_InsertSourceAssetCommand.Parameters.Add("@source_asset_guid", SqliteType.Text); m_InsertSourceAssetCommand.Parameters.Add("@build_time_asset_path", SqliteType.Text); + m_InsertSourceAssetCommand.Parameters.Add("@asset_name", SqliteType.Text); + m_InsertSourceAssetCommand.Parameters.Add("@asset_extension", SqliteType.Text); m_GetSourceAssetIdCommand = db.CreateCommand(); m_GetSourceAssetIdCommand.CommandText = @"SELECT id FROM build_report_source_assets @@ -117,10 +120,14 @@ public void Process(Context ctx, long objectId, RandomAccessReader reader, out s var cacheKey = (content.SourceAssetGUID, content.BuildTimeAssetPath); if (!m_SourceAssetCache.TryGetValue(cacheKey, out long sourceAssetId)) { - // Insert the source asset (will be ignored if it already exists) + // Insert the source asset (will be ignored if it already exists). The name and + // extension are precomputed from the path because extracting them in SQLite + // queries is awkward (no reverse string search). m_InsertSourceAssetCommand.Transaction = ctx.Transaction; m_InsertSourceAssetCommand.Parameters["@source_asset_guid"].Value = content.SourceAssetGUID; m_InsertSourceAssetCommand.Parameters["@build_time_asset_path"].Value = content.BuildTimeAssetPath; + m_InsertSourceAssetCommand.Parameters["@asset_name"].Value = Path.GetFileNameWithoutExtension(content.BuildTimeAssetPath); + m_InsertSourceAssetCommand.Parameters["@asset_extension"].Value = Path.GetExtension(content.BuildTimeAssetPath).TrimStart('.').ToLowerInvariant(); m_InsertSourceAssetCommand.ExecuteNonQuery(); // Get the ID (whether just inserted or already existing) diff --git a/Documentation/ProjectAuditor-ObjectsBySourceAsset.png b/Documentation/ProjectAuditor-ObjectsBySourceAsset.png new file mode 100644 index 0000000..78dbe92 Binary files /dev/null and b/Documentation/ProjectAuditor-ObjectsBySourceAsset.png differ diff --git a/Documentation/analyze-examples-buildreport.md b/Documentation/analyze-examples-buildreport.md new file mode 100644 index 0000000..4dafa1d --- /dev/null +++ b/Documentation/analyze-examples-buildreport.md @@ -0,0 +1,254 @@ +# Example queries for Player build reports + +This page shows how to answer common questions about a Player build using the build report data in an [analyze](command-analyze.md) database. The examples reproduce the data views of the **Build** section of the [Project Auditor](https://docs.unity3d.com/6000.6/Documentation/Manual/project-auditor/build-view-reference.html) package. + +Each view maps to a short SQL query. Because the queries run against an SQLite database, this approach handles much larger builds than the Project Auditor UI, and the queries can be incorporated into scripts and custom tools. Project Auditor only shows the most recent clean build, while these queries work against any build report in your build history (see [Analyzing multiple build reports](buildreport.md#analyzing-multiple-build-reports)). + +See [BuildReport Support](buildreport.md) for the full description of the imported data and the database schema, and [Example usage of Analyze](analyze-examples.md) for general tips about running queries (including from the command line). + +The example output on this page comes from a Windows Player build of the [Happy Harvest](https://assetstore.unity.com/packages/essentials/tutorial-projects/happy-harvest-2d-sample-project-259218) sample project, made with Unity 6.6. That build report is included in this repository as a test file, so you can reproduce the output: + +``` +UnityDataTool analyze TestCommon/Data/BuildReports/happyHarvest.buildreport -o Analysis.db +sqlite3 Analysis.db ".mode column" "" +``` + +## General build information + +The build summary lands in the `build_reports` table, one row per report. The `.mode line` output format of sqlite3 suits this one-row result: + +``` +sqlite3 Analysis.db ".mode line" "SELECT build_name, platform_name, build_result, start_time, end_time, total_time_seconds, printf('%.1f MB', total_size / 1024.0 / 1024.0) AS total_size, output_path FROM build_reports;" +``` + +``` + build_name = perf.u6.happy-harvest + platform_name = Win64 + build_result = Succeeded + start_time = 2026-07-22T18:50:13.1761397Z + end_time = 2026-07-22T18:51:41.7167432Z +total_time_seconds = 89 + total_size = 280.0 MB + output_path = D:/UnitySrc/perf.u6.happy-harvest/Build/perf.u6.happy-harvest.exe +``` + +`SELECT * FROM build_reports` shows the full set of recorded columns (error and warning counts, build GUID, build options, etc.). In Unity 6.6+ the [Build Analysis window](https://docs.unity3d.com/6000.7/Documentation/Manual/build-analysis-window-reference.html) shows similar summary information directly in the Editor. + +## Size by runtime type + +A breakdown of the content size by Unity object type, ordered by total size. For Unity 6.6+ reports this is precalculated in the ContentSummary: + +```sql +SELECT type_name, printf('%.1f MB', size / 1024.0 / 1024.0) AS pretty_size, object_count +FROM build_report_content_type_stats_view +ORDER BY size DESC LIMIT 10; +``` + +``` +type_name pretty_size object_count +-------------- ----------- ------------ +Texture2D 101.7 MB 589 +AudioClip 9.2 MB 74 +Shader 2.5 MB 96 +Font 1.5 MB 3 +ComputeShader 1.4 MB 104 +MonoBehaviour 0.7 MB 1768 +ParticleSystem 0.7 MB 91 +Sprite 0.6 MB 688 +TextAsset 0.4 MB 5 +Tilemap 0.4 MB 15 +``` + +Reports from Unity versions before 6.6 have no ContentSummary, but the same breakdown can be computed from the per-object PackedAssets data: + +```sql +SELECT type, COUNT(*) AS objects, printf('%.1f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size +FROM build_report_packed_asset_contents_view +GROUP BY type ORDER BY SUM(size) DESC LIMIT 10; +``` + +This produces the same numbers for this build (the sizes include the resource data that types like Texture2D and AudioClip store in `.resS` and `.resource` files). + +## Objects grouped by source asset + +The rest of the examples use `build_report_packed_asset_contents_view`, which has one row per object (or resource blob) in the build output, with its type, size, containing build file, and originating source asset. This is the data behind the object views of Project Auditor, such as this grouping by source asset: + +![](ProjectAuditor-ObjectsBySourceAsset.png) + +The collapsed group rows correspond to a `GROUP BY` query. The `asset_name` column holds the source asset's filename without extension, matching how the UI names groups. This example also computes each group's share of the total content size, like the UI's "Size % (of Data)" column: + +```sql +SELECT asset_name, COUNT(*) AS objects, printf('%.1f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size, + printf('%.1f%%', 100.0 * SUM(size) / (SELECT SUM(size) FROM build_report_packed_asset_info)) AS data_pct +FROM build_report_packed_asset_contents_view +GROUP BY asset_name ORDER BY SUM(size) DESC LIMIT 5; +``` + +``` +asset_name objects pretty_size data_pct +----------------------------------- ------- ----------- -------- +SpriteAtlas_Tiles 7 48.2 MB 40.0% +Sprite_Pinetree_normal 2 5.1 MB 4.2% +Sprite_Pinetree 3 3.8 MB 3.2% +Sprite_Pinetree_mask 2 3.8 MB 3.2% +Background ambience outside - Night 2 3.8 MB 3.1% +``` + +Expanding a group in the UI corresponds to a filtered query listing the individual objects. Filtering on the full asset path avoids mixing up assets that share a name: + +```sql +SELECT type, size, path AS build_file +FROM build_report_packed_asset_contents_view +WHERE build_time_asset_path = 'Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 8.prefab' +ORDER BY size; +``` + +``` +type size build_file +----------------- ---- -------------------- +GameObject 35 sharedassets2.assets +GameObject 67 sharedassets2.assets +Transform 68 sharedassets2.assets +Transform 80 sharedassets2.assets +PolygonCollider2D 180 sharedassets2.assets +SpriteRenderer 212 sharedassets2.assets +``` + +> **Note:** The build report records which source asset each object came from, but not the object's name. The `find-refs` command and the `objects` table can help identify specific objects when the build output itself is also analyzed — see [Cross-referencing with build output](buildreport.md#cross-referencing-with-build-output). + +## Objects grouped by source file extension + +The `asset_extension` column holds the source asset's file extension (lower-cased, without the dot). Grouping by it shows which kinds of source files contribute the most content. This is also the closest equivalent to Project Auditor's "Importer Type" grouping, because Unity selects the importer based on the file extension: + +```sql +SELECT asset_extension, COUNT(*) AS objects, printf('%.1f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size, + printf('%.1f%%', 100.0 * SUM(size) / (SELECT SUM(size) FROM build_report_packed_asset_info)) AS data_pct +FROM build_report_packed_asset_contents_view +GROUP BY asset_extension ORDER BY SUM(size) DESC LIMIT 8; +``` + +``` +asset_extension objects pretty_size data_pct +--------------- ------- ----------- -------- +spriteatlasv2 7 48.2 MB 40.0% +png 1039 37.1 MB 30.8% +psd 154 13.0 MB 10.8% +wav 74 9.2 MB 7.6% + 17 3.1 MB 2.6% +vfx 185 2.9 MB 2.4% +unity 7600 2.0 MB 1.7% +ttf 9 1.5 MB 1.3% +``` + +The row with the empty extension collects content with no normal source asset path, such as built-in resources (e.g. the splash screen logo) and objects with no recorded source asset. The `unity` row is the objects from the built scenes (Unity 6.6+ reports; older reports do not cover scene files). + +Drill into one extension the same way as any other group: + +```sql +SELECT asset_name, type, printf('%.1f KB', size / 1024.0) AS pretty_size, path AS build_file +FROM build_report_packed_asset_contents_view +WHERE asset_extension = 'wav' ORDER BY size DESC LIMIT 5; +``` + +``` +asset_name type pretty_size build_file +----------------------------------- --------- ----------- ---------------------- +Background ambience outside - Night AudioClip 3864.0 KB sharedassets2.resource +Background ambience outside - Day AudioClip 3829.3 KB sharedassets2.resource +Rain AudioClip 1242.2 KB sharedassets2.resource +Thunder AudioClip 197.5 KB sharedassets2.resource +Watering crop-001 AudioClip 26.3 KB resources.resource +``` + +## Objects grouped by build file + +The `path` column is the file in the build output that contains the object: a SerializedFile (`level2`, `sharedassets0.assets`, ...) or a resource file (`.resS` for textures and meshes, `.resource` for audio and video): + +```sql +SELECT path AS build_file, COUNT(*) AS objects, printf('%.2f MB', SUM(size) / 1024.0 / 1024.0) AS pretty_size +FROM build_report_packed_asset_contents_view +GROUP BY path ORDER BY path; +``` + +``` +build_file objects pretty_size +------------------------------ ------- ----------- +globalgamemanagers.assets 1738 1.16 MB +globalgamemanagers.assets.resS 38 2.95 MB +level0 16 0.00 MB +level1 17 0.00 MB +level2 6521 1.82 MB +level3 1046 0.16 MB +resources.assets 1227 2.23 MB +resources.assets.resS 85 59.32 MB +resources.resource 23 0.17 MB +sharedassets0.assets 72 1.73 MB +... +``` + +Listing the content of one build file (`ORDER BY offset` shows the objects in their order within the file): + +```sql +SELECT asset_name, type, printf('%.1f KB', size / 1024.0) AS pretty_size +FROM build_report_packed_asset_contents_view +WHERE path = 'sharedassets2.resource' ORDER BY asset_name; +``` + +``` +asset_name type pretty_size +----------------------------------- --------- ----------- +Background ambience outside - Day AudioClip 3829.3 KB +Background ambience outside - Night AudioClip 3864.0 KB +Chicken-001 AudioClip 8.1 KB +Chicken-002 AudioClip 8.3 KB +Close Window AudioClip 2.2 KB +... +``` + +## Objects grouped by runtime type + +The [Size by runtime type](#size-by-runtime-type) section above shows the aggregate query. To see the individual objects of one type, and where they come from: + +```sql +SELECT asset_name, build_time_asset_path, size +FROM build_report_packed_asset_contents_view +WHERE type = 'AudioMixerGroup'; +``` + +``` +asset_name build_time_asset_path size +---------- ------------------------------------------------ ---- +MainMixer Assets/HappyHarvest/Common/Audio/MainMixer.mixer 40 +MainMixer Assets/HappyHarvest/Common/Audio/MainMixer.mixer 68 +MainMixer Assets/HappyHarvest/Common/Audio/MainMixer.mixer 40 +``` + +## Objects grouped by source asset path + +Grouping by the full `build_time_asset_path` distinguishes same-named assets in different folders, and a `LIKE` filter on the path restricts the report to one part of the project — something a fixed UI grouping cannot do: + +```sql +SELECT build_time_asset_path, COUNT(*) AS objects, printf('%.1f KB', SUM(size) / 1024.0) AS pretty_size +FROM build_report_packed_asset_contents_view +WHERE build_time_asset_path LIKE 'Assets/HappyHarvest/Art/Tiles/Fence/%' +GROUP BY build_time_asset_path ORDER BY build_time_asset_path; +``` + +``` +build_time_asset_path objects pretty_size +----------------------------------------------------------- ------- ----------- +Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 1.prefab 6 0.6 KB +Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 10.prefab 6 0.6 KB +Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 11.prefab 6 0.6 KB +Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 12.prefab 6 0.6 KB +Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 13.prefab 6 0.6 KB +... +``` + +Paths outside `Assets/` also appear: package assets (`Packages/...`), built-in resources (`Resources/unity_builtin_extra`), and generated content. + +## Beyond a single Player build report + +The queries work the same for AssetBundle builds made with `BuildPipeline.BuildAssetBundles`, because those reports populate the same data. For those builds, the `archive` column of `build_report_packed_asset_contents_view` holds the name of the AssetBundle containing each object (for Player builds it is NULL), so you can add it to a query's columns or `GROUP BY` to break the results down by AssetBundle. + +The examples above assume a single build report in the database. If you analyze several reports together, add a filter such as `WHERE build_report_filename = '...'` (or `build_report_id`) to the queries — the views expose both columns. diff --git a/Documentation/analyze-examples.md b/Documentation/analyze-examples.md index 428cd30..5fa38a5 100644 --- a/Documentation/analyze-examples.md +++ b/Documentation/analyze-examples.md @@ -70,6 +70,8 @@ Shader Graphs/CustomLightingBuildingsB 113.4 KB 1b2fdfe013c58ffd57d7663 See [buildreport.md](buildreport.md) for information about using analyze to look at BuildReport files. +Example queries against build report data — build summary, size by type, and objects grouped by source asset, file extension, or build file — are collected on a dedicated page: [Example queries for Player build reports](analyze-examples-buildreport.md). + ## Example: Using AI tools to help write queries diff --git a/Documentation/buildreport.md b/Documentation/buildreport.md index df23c7d..f090612 100644 --- a/Documentation/buildreport.md +++ b/Documentation/buildreport.md @@ -117,6 +117,8 @@ For more detail on the build history layout, see the Unity Manual's [Build histo ## Example queries +For a worked set of Player build examples — build summary, size by type, and objects grouped by source asset, file extension, or build file — see [Example queries for Player build reports](analyze-examples-buildreport.md). + Run these after analyzing a build report file. Show all successful builds recorded in the database: @@ -198,7 +200,7 @@ Views automatically identify which build report each row belongs to, simplifying - `build_report_files` and `build_report_archive_contents`: store the BuildReport object `id` for each row (as `build_report_id`). - `build_report_packed_asset_info`: stores the PackedAssets object `id` for each row (as `packed_assets_id`). -- `build_report_source_assets`: normalized table of distinct source asset GUIDs and paths, linked via `build_report_packed_asset_info.source_asset_id`. +- `build_report_source_assets`: normalized table of distinct source asset GUIDs and paths, linked via `build_report_packed_asset_info.source_asset_id`. The `asset_name` (filename without extension) and `asset_extension` (lower-cased, without the dot) columns are derived from the path for convenient grouping, and are also exposed by `build_report_packed_asset_contents_view`. - `build_report_content_type_stats` and `build_report_content_asset_stats`: store the ContentSummary object `id` for each row (as `content_summary_id`). > **Note:** BuildReport and PackedAssets objects are also linked in the `refs` table (BuildReport references PackedAssets in its appendices array), but this relationship is not used in the built-in views because `refs` population is optional. diff --git a/TestCommon/Data/BuildReports/README.md b/TestCommon/Data/BuildReports/README.md index 30714b8..ce300f2 100644 --- a/TestCommon/Data/BuildReports/README.md +++ b/TestCommon/Data/BuildReports/README.md @@ -6,4 +6,5 @@ They were output from the TestProject in the [BuildReportInspector](https://gith * **AssetBundle.buildreport** - Example report from an AssetBundle build (BuildPipeline.BuildAssetBundles). * **Player.buildreport** - BuildReport for a Windows Player build with detailed build reporting (generated with Unity 6000.0.65f1) +* **happyHarvest.buildreport** - BuildReport for a Windows Player build of the [Happy Harvest](https://assetstore.unity.com/packages/essentials/tutorial-projects/happy-harvest-2d-sample-project-259218) 2D sample project (generated with Unity 6000.6). A larger report with a ContentSummary object and a diverse range of content; the example output in `Documentation/analyze-examples-buildreport.md` is based on it. diff --git a/TestCommon/Data/BuildReports/happyHarvest.buildreport b/TestCommon/Data/BuildReports/happyHarvest.buildreport new file mode 100644 index 0000000..fc70600 Binary files /dev/null and b/TestCommon/Data/BuildReports/happyHarvest.buildreport differ diff --git a/UnityDataTool.Tests/BuildReportTests.cs b/UnityDataTool.Tests/BuildReportTests.cs index 4adc2d0..396ee0b 100644 --- a/UnityDataTool.Tests/BuildReportTests.cs +++ b/UnityDataTool.Tests/BuildReportTests.cs @@ -348,12 +348,20 @@ public async Task Analyze_BuildReport_AssetBundle_ContainsPackedAssetsData() "Unexpected source_asset_guid in build_report_packed_asset_contents_view"); SQLTestHelper.AssertQueryString(db, - $@"SELECT build_time_asset_path FROM build_report_packed_asset_contents_view - WHERE packed_assets_id = {packedAssetId} + $@"SELECT build_time_asset_path FROM build_report_packed_asset_contents_view + WHERE packed_assets_id = {packedAssetId} AND object_id = {objectId}", "Assets/Sprites/Snow.jpg", "Unexpected build_time_asset_path in build_report_packed_asset_contents_view"); + // The name and extension derived from the source asset path. + SQLTestHelper.AssertQueryString(db, + $@"SELECT asset_name || '|' || asset_extension FROM build_report_packed_asset_contents_view + WHERE packed_assets_id = {packedAssetId} + AND object_id = {objectId}", + "Snow|jpg", + "Unexpected asset_name/asset_extension in build_report_packed_asset_contents_view"); + SQLTestHelper.AssertQueryString(db, $"SELECT path FROM build_report_packed_assets_view WHERE id = {packedAssetId}", "CAB-6b49068aebcf9d3b05692c8efd933167", @@ -522,6 +530,80 @@ public async Task Analyze_BuildReports_BothReports_ContainsBuildReportFilesData( "Expected all PackedAssets from Player.buildreport have NULL archive"); } + // A Unity 6.6 Player build of the Happy Harvest sample project: a larger report with a + // ContentSummary and diverse content. The expected values pin down the example query results + // shown in Documentation/analyze-examples-buildreport.md (issue #110). + [Test] + public async Task Analyze_BuildReport_HappyHarvest_MatchesDocumentedExamples() + { + var databasePath = SQLTestHelper.GetDatabasePath(m_TestOutputFolder); + + Assert.AreEqual(0, await Program.Main(new[] + { "analyze", Path.Combine(m_TestDataFolder, "happyHarvest.buildreport") })); + using var db = SQLTestHelper.OpenDatabase(databasePath); + + // General build information. + SQLTestHelper.AssertQueryString(db, "SELECT build_name FROM build_reports", "perf.u6.happy-harvest", + "Unexpected build_name"); + SQLTestHelper.AssertQueryString(db, "SELECT platform_name FROM build_reports", "Win64", + "Unexpected platform_name"); + SQLTestHelper.AssertQueryString(db, "SELECT build_result FROM build_reports", "Succeeded", + "Unexpected build_result"); + SQLTestHelper.AssertQueryInt(db, "SELECT total_time_seconds FROM build_reports", 89, + "Unexpected total_time_seconds"); + + // Size by runtime type: the largest type from the ContentSummary. + SQLTestHelper.AssertQueryString(db, + @"SELECT type_name || '|' || size || '|' || object_count + FROM build_report_content_type_stats_view ORDER BY size DESC LIMIT 1", + "Texture2D|106623032|589", + "Unexpected largest type in ContentSummary type stats"); + + // The same breakdown computed from PackedAssets matches the ContentSummary. + SQLTestHelper.AssertQueryString(db, + @"SELECT COUNT(*) || '|' || SUM(size) FROM build_report_packed_asset_contents_view + WHERE type = 'Texture2D'", + "589|106623032", + "PackedAssets Texture2D breakdown should match the ContentSummary"); + SQLTestHelper.AssertQueryInt(db, + @"SELECT (SELECT SUM(size) FROM build_report_content_type_stats) + - (SELECT SUM(size) FROM build_report_packed_asset_info)", 0, + "ContentSummary and PackedAssets should report the same total content size"); + + // Objects grouped by source asset. + SQLTestHelper.AssertQueryString(db, + @"SELECT asset_name || '|' || COUNT(*) || '|' || SUM(size) + FROM build_report_packed_asset_contents_view + GROUP BY asset_name ORDER BY SUM(size) DESC LIMIT 1", + "SpriteAtlas_Tiles|7|50562096", + "Unexpected largest source asset group"); + SQLTestHelper.AssertQueryInt(db, + @"SELECT COUNT(*) FROM build_report_packed_asset_contents_view + WHERE build_time_asset_path = 'Assets/HappyHarvest/Art/Tiles/Fence/Prefabs/Fence 8.prefab'", + 6, "Unexpected object count for the Fence 8 prefab"); + + // Objects grouped by source file extension. + SQLTestHelper.AssertQueryString(db, + @"SELECT COUNT(*) || '|' || SUM(size) FROM build_report_packed_asset_contents_view + WHERE asset_extension = 'png'", + "1039|38913212", + "Unexpected png extension group"); + + // Content without a normal source asset path has an empty name and extension. + SQLTestHelper.AssertQueryString(db, + @"SELECT asset_name || '|' || asset_extension FROM build_report_source_assets + WHERE build_time_asset_path = 'Built-in Texture2D: Splash Screen Unity Logo'", + "Built-in Texture2D: Splash Screen Unity Logo|", + "Expected no extension for a built-in resource"); + + // Objects grouped by build file: scene content is covered from Unity 6.6. + SQLTestHelper.AssertQueryInt(db, "SELECT COUNT(*) FROM build_report_packed_assets", 18, + "Unexpected number of PackedAssets files"); + SQLTestHelper.AssertQueryInt(db, + "SELECT COUNT(*) FROM build_report_packed_asset_contents_view WHERE path = 'level2'", + 6521, "Unexpected object count in level2"); + } + // The Unity 6.6 Summary adds several fields (issue #107 Part 1). Verify they land in the new // build_reports columns for a ContentDirectory report, which populates all of them. [Test]