Update asset database
This commit is contained in:
@@ -22,7 +22,7 @@ public class AssetDatabaseIntegrationTest
|
||||
// Create temporary test project structure
|
||||
_testProjectDir = Path.Combine(Path.GetTempPath(), "GhostAssetDBIntegration_" + Guid.NewGuid().ToString());
|
||||
_testAssetsDir = Path.Combine(_testProjectDir, ProjectService.ASSETS_FOLDER);
|
||||
|
||||
|
||||
Directory.CreateDirectory(_testProjectDir);
|
||||
Directory.CreateDirectory(_testAssetsDir);
|
||||
Directory.CreateDirectory(Path.Combine(_testProjectDir, ProjectService.CACHE_FOLDER));
|
||||
@@ -33,22 +33,22 @@ public class AssetDatabaseIntegrationTest
|
||||
|
||||
// Create a minimal project file with required metadata
|
||||
var projectPath = Path.Combine(_testProjectDir, "TestProject.gproj");
|
||||
|
||||
|
||||
// Create a proper ProjectMetadata instance
|
||||
var metadata = new Ghost.Data.Models.ProjectMetadata("TestProject", new Version(1, 0, 0));
|
||||
|
||||
|
||||
await using var fileStream = File.Create(projectPath);
|
||||
await System.Text.Json.JsonSerializer.SerializeAsync(fileStream, metadata, Ghost.Data.JsonContext.Default.ProjectMetadata, TestContext.CancellationToken);
|
||||
await fileStream.FlushAsync(TestContext.CancellationToken);
|
||||
fileStream.Close();
|
||||
|
||||
// Set CurrentProject directly
|
||||
var projectMetadataInfo = new Ghost.Data.Models.ProjectMetadataInfo(projectPath, metadata);
|
||||
var projectMetadataInfo = new Data.Models.ProjectMetadataInfo(projectPath, metadata);
|
||||
ProjectService.CurrentProject = projectMetadataInfo;
|
||||
|
||||
|
||||
// Initialize AssetDatabase
|
||||
AssetDatabase.Initialize();
|
||||
|
||||
AssetDatabase.Initialize(TestContext.CancellationToken);
|
||||
|
||||
// Give the file system watcher time to start
|
||||
await Task.Delay(100, TestContext.CancellationToken);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class AssetDatabaseIntegrationTest
|
||||
try
|
||||
{
|
||||
// Add delay to allow file handles to be released
|
||||
System.Threading.Thread.Sleep(100);
|
||||
Thread.Sleep(100);
|
||||
Directory.Delete(_testProjectDir, true);
|
||||
}
|
||||
catch
|
||||
@@ -82,6 +82,18 @@ public class AssetDatabaseIntegrationTest
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to wait for file system events to be processed.
|
||||
/// </summary>
|
||||
private async Task WaitForFileSystemEvents(int delayMs = 300)
|
||||
{
|
||||
await Task.Delay(delayMs, TestContext.CancellationToken);
|
||||
AssetDatabase.FlushPendingCommands();
|
||||
|
||||
// Give a bit more time after flush for any final processing
|
||||
await Task.Delay(50, TestContext.CancellationToken);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestAutoMetaGeneration_WhenFileCreated()
|
||||
{
|
||||
@@ -89,8 +101,8 @@ public class AssetDatabaseIntegrationTest
|
||||
var testFile = Path.Combine(_testAssetsDir, "test.txt");
|
||||
await File.WriteAllTextAsync(testFile, "Hello World", TestContext.CancellationToken);
|
||||
|
||||
// Wait a bit for file system watcher to react
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
// Wait for file system watcher to react and process commands
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
// Check if meta file was auto-generated
|
||||
var metaFile = testFile + ".gmeta";
|
||||
@@ -111,18 +123,18 @@ public class AssetDatabaseIntegrationTest
|
||||
await File.WriteAllTextAsync(Path.Combine(_testAssetsDir, "enemy.txt"), "data", TestContext.CancellationToken);
|
||||
|
||||
// Wait for database to update
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
// Test wildcard search: player*
|
||||
var results = await AssetDatabase.FindAssetsByNameAsync("player*");
|
||||
var results = await AssetDatabase.FindAssetsByNameAsync("player*", TestContext.CancellationToken);
|
||||
Assert.HasCount(3, results, "Should find 3 files matching 'player*'");
|
||||
|
||||
// Test single character wildcard: player?
|
||||
results = await AssetDatabase.FindAssetsByNameAsync("player?.txt");
|
||||
results = await AssetDatabase.FindAssetsByNameAsync("player?.txt", TestContext.CancellationToken);
|
||||
Assert.HasCount(2, results, "Should find 2 files matching 'player?.txt'");
|
||||
|
||||
// Test exact match
|
||||
results = await AssetDatabase.FindAssetsByNameAsync("enemy.txt");
|
||||
results = await AssetDatabase.FindAssetsByNameAsync("enemy.txt", TestContext.CancellationToken);
|
||||
Assert.HasCount(1, results, "Should find 1 file matching 'enemy.txt'");
|
||||
}
|
||||
|
||||
@@ -132,7 +144,7 @@ public class AssetDatabaseIntegrationTest
|
||||
// Create a file
|
||||
var originalPath = Path.Combine(_testAssetsDir, "original.txt");
|
||||
await File.WriteAllTextAsync(originalPath, "data", TestContext.CancellationToken);
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
// Get the GUID before rename
|
||||
var guidResult = AssetDatabase.PathToGuid(originalPath);
|
||||
@@ -142,7 +154,7 @@ public class AssetDatabaseIntegrationTest
|
||||
// Rename via file system
|
||||
var newPath = Path.Combine(_testAssetsDir, "renamed.txt");
|
||||
File.Move(originalPath, newPath);
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
// Check if meta file was also moved
|
||||
var newMetaPath = newPath + ".gmeta";
|
||||
@@ -160,7 +172,7 @@ public class AssetDatabaseIntegrationTest
|
||||
// Create a file
|
||||
var filePath = Path.Combine(_testAssetsDir, "todelete.txt");
|
||||
await File.WriteAllTextAsync(filePath, "data", TestContext.CancellationToken);
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
var guidResult = AssetDatabase.PathToGuid(filePath);
|
||||
Assert.IsTrue(guidResult.IsSuccess);
|
||||
@@ -168,7 +180,7 @@ public class AssetDatabaseIntegrationTest
|
||||
|
||||
// Delete via file system
|
||||
File.Delete(filePath);
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
// Meta file should also be deleted
|
||||
var metaPath = filePath + ".gmeta";
|
||||
@@ -183,9 +195,9 @@ public class AssetDatabaseIntegrationTest
|
||||
public async Task TestFileCreate_ViaAPI()
|
||||
{
|
||||
var filePath = Path.Combine(_testAssetsDir, "apiCreated.txt");
|
||||
|
||||
|
||||
// Create via API
|
||||
var result = await AssetDatabase.CreateAssetAsync(filePath);
|
||||
var result = await AssetDatabase.CreateAssetAsync(filePath, TestContext.CancellationToken);
|
||||
Assert.IsTrue(result.IsSuccess, "Should create asset successfully");
|
||||
|
||||
// File and meta should exist
|
||||
@@ -203,7 +215,7 @@ public class AssetDatabaseIntegrationTest
|
||||
// Create initial file
|
||||
var sourcePath = Path.Combine(_testAssetsDir, "source.txt");
|
||||
await File.WriteAllTextAsync(sourcePath, "data", TestContext.CancellationToken);
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
var guid = AssetDatabase.PathToGuid(sourcePath).Value;
|
||||
|
||||
@@ -214,7 +226,7 @@ public class AssetDatabaseIntegrationTest
|
||||
var destPath = Path.Combine(subDir, "source.txt");
|
||||
|
||||
// Move via API
|
||||
var result = await AssetDatabase.MoveAssetAsync(sourcePath, destPath);
|
||||
var result = await AssetDatabase.MoveAssetAsync(sourcePath, destPath, TestContext.CancellationToken);
|
||||
Assert.IsTrue(result.IsSuccess, $"Should move asset successfully. Error: {result.Message}");
|
||||
|
||||
// Old file should not exist
|
||||
@@ -236,13 +248,13 @@ public class AssetDatabaseIntegrationTest
|
||||
// Create initial file
|
||||
var sourcePath = Path.Combine(_testAssetsDir, "tocopy.txt");
|
||||
await File.WriteAllTextAsync(sourcePath, "data", TestContext.CancellationToken);
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
var sourceGuid = AssetDatabase.PathToGuid(sourcePath).Value;
|
||||
var destPath = Path.Combine(_testAssetsDir, "copied.txt");
|
||||
|
||||
// Copy via API
|
||||
var result = await AssetDatabase.CopyAssetAsync(sourcePath, destPath);
|
||||
var result = await AssetDatabase.CopyAssetAsync(sourcePath, destPath, TestContext.CancellationToken);
|
||||
Assert.IsTrue(result.IsSuccess, "Should copy asset successfully");
|
||||
|
||||
// Both files should exist
|
||||
@@ -260,12 +272,12 @@ public class AssetDatabaseIntegrationTest
|
||||
// Create initial file
|
||||
var filePath = Path.Combine(_testAssetsDir, "todelete2.txt");
|
||||
await File.WriteAllTextAsync(filePath, "data", TestContext.CancellationToken);
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
var guid = AssetDatabase.PathToGuid(filePath).Value;
|
||||
|
||||
// Delete via API
|
||||
var result = await AssetDatabase.DeleteAssetAsync(filePath);
|
||||
var result = await AssetDatabase.DeleteAssetAsync(filePath, TestContext.CancellationToken);
|
||||
Assert.IsTrue(result.IsSuccess, "Should delete asset successfully");
|
||||
|
||||
// File and meta should not exist
|
||||
@@ -289,7 +301,7 @@ public class AssetDatabaseIntegrationTest
|
||||
var fileName = $"race{i}.txt";
|
||||
fileNames.Add(fileName);
|
||||
var filePath = Path.Combine(_testAssetsDir, fileName);
|
||||
|
||||
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
await File.WriteAllTextAsync(filePath, $"data{i}", TestContext.CancellationToken);
|
||||
@@ -297,16 +309,16 @@ public class AssetDatabaseIntegrationTest
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
await Task.Delay(500, TestContext.CancellationToken); // Wait for all file system events
|
||||
await WaitForFileSystemEvents(500); // Wait for all file system events
|
||||
|
||||
// All files should have exactly one meta file
|
||||
foreach (var fileName in fileNames)
|
||||
{
|
||||
var filePath = Path.Combine(_testAssetsDir, fileName);
|
||||
var metaPath = filePath + ".gmeta";
|
||||
|
||||
|
||||
Assert.IsTrue(File.Exists(metaPath), $"Meta file should exist for {fileName}");
|
||||
|
||||
|
||||
// Read meta and verify it's valid JSON
|
||||
var metaContent = await File.ReadAllTextAsync(metaPath, TestContext.CancellationToken);
|
||||
Assert.Contains("Guid", metaContent, $"Meta file should be valid for {fileName}");
|
||||
@@ -324,20 +336,20 @@ public class AssetDatabaseIntegrationTest
|
||||
await File.WriteAllTextAsync(file1, "data", TestContext.CancellationToken);
|
||||
await File.WriteAllTextAsync(file2, "data", TestContext.CancellationToken);
|
||||
await File.WriteAllTextAsync(file3, "data", TestContext.CancellationToken);
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
var guid1 = AssetDatabase.PathToGuid(file1).Value;
|
||||
var guid2 = AssetDatabase.PathToGuid(file2).Value;
|
||||
|
||||
// Add tags
|
||||
await AssetDatabase.SetAssetTagsAsync(guid1, new List<string> { "Test", "Player" });
|
||||
await AssetDatabase.SetAssetTagsAsync(guid2, new List<string> { "Test", "Enemy" });
|
||||
await AssetDatabase.SetAssetTagsAsync(guid1, new List<string> { "Test", "Player" }, TestContext.CancellationToken);
|
||||
await AssetDatabase.SetAssetTagsAsync(guid2, new List<string> { "Test", "Enemy" }, TestContext.CancellationToken);
|
||||
|
||||
// Search by tag
|
||||
var testAssets = await AssetDatabase.FindAssetsByTagAsync("Test");
|
||||
var testAssets = await AssetDatabase.FindAssetsByTagAsync("Test", TestContext.CancellationToken);
|
||||
Assert.HasCount(2, testAssets, "Should find 2 assets with 'Test' tag");
|
||||
|
||||
var playerAssets = await AssetDatabase.FindAssetsByTagAsync("Player");
|
||||
var playerAssets = await AssetDatabase.FindAssetsByTagAsync("Player", TestContext.CancellationToken);
|
||||
Assert.HasCount(1, playerAssets, "Should find 1 asset with 'Player' tag");
|
||||
}
|
||||
|
||||
@@ -347,14 +359,14 @@ public class AssetDatabaseIntegrationTest
|
||||
// Create a file
|
||||
var filePath = Path.Combine(_testAssetsDir, "refresh.txt");
|
||||
await File.WriteAllTextAsync(filePath, "data", TestContext.CancellationToken);
|
||||
await Task.Delay(200, TestContext.CancellationToken);
|
||||
await WaitForFileSystemEvents();
|
||||
|
||||
var guid1 = AssetDatabase.PathToGuid(filePath).Value;
|
||||
|
||||
// Call RefreshAsync multiple times
|
||||
await AssetDatabase.RefreshAsync();
|
||||
await AssetDatabase.RefreshAsync();
|
||||
await AssetDatabase.RefreshAsync();
|
||||
await AssetDatabase.RefreshAsync(TestContext.CancellationToken);
|
||||
await AssetDatabase.RefreshAsync(TestContext.CancellationToken);
|
||||
await AssetDatabase.RefreshAsync(TestContext.CancellationToken);
|
||||
|
||||
// GUID should remain the same
|
||||
var guid2 = AssetDatabase.PathToGuid(filePath).Value;
|
||||
@@ -364,4 +376,12 @@ public class AssetDatabaseIntegrationTest
|
||||
var metaFiles = Directory.GetFiles(_testAssetsDir, "refresh.txt.gmeta");
|
||||
Assert.HasCount(1, metaFiles, "Should have exactly one meta file");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ThreadSafetyTest()
|
||||
{
|
||||
var testFile = Path.Combine(_testAssetsDir, "test.txt");
|
||||
await File.WriteAllTextAsync(testFile, "Hello World", TestContext.CancellationToken);
|
||||
await AssetDatabase.RefreshAsync(TestContext.CancellationToken); // This will cause race conditions if not handle properly because both AssetDatabase and FileSystemWatcher are involved
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user