Skip to content

Commit 1d6c55b

Browse files
committed
test: add unit tests for Python tools asset and sync services
1 parent 366936a commit 1d6c55b

File tree

8 files changed

+501
-0
lines changed

8 files changed

+501
-0
lines changed

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Data.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
using System;
2+
using System.Linq;
3+
using NUnit.Framework;
4+
using UnityEngine;
5+
using MCPForUnity.Editor.Data;
6+
7+
namespace MCPForUnityTests.Editor.Data
8+
{
9+
public class PythonToolsAssetTests
10+
{
11+
private PythonToolsAsset _asset;
12+
13+
[SetUp]
14+
public void SetUp()
15+
{
16+
_asset = ScriptableObject.CreateInstance<PythonToolsAsset>();
17+
}
18+
19+
[TearDown]
20+
public void TearDown()
21+
{
22+
if (_asset != null)
23+
{
24+
UnityEngine.Object.DestroyImmediate(_asset, true);
25+
}
26+
}
27+
28+
[Test]
29+
public void GetValidFiles_ReturnsEmptyList_WhenNoFilesAdded()
30+
{
31+
var validFiles = _asset.GetValidFiles().ToList();
32+
33+
Assert.IsEmpty(validFiles, "Should return empty list when no files added");
34+
}
35+
36+
[Test]
37+
public void GetValidFiles_FiltersOutNullReferences()
38+
{
39+
_asset.pythonFiles.Add(null);
40+
_asset.pythonFiles.Add(new TextAsset("print('test')"));
41+
_asset.pythonFiles.Add(null);
42+
43+
var validFiles = _asset.GetValidFiles().ToList();
44+
45+
Assert.AreEqual(1, validFiles.Count, "Should filter out null references");
46+
}
47+
48+
[Test]
49+
public void GetValidFiles_ReturnsAllNonNullFiles()
50+
{
51+
var file1 = new TextAsset("print('test1')");
52+
var file2 = new TextAsset("print('test2')");
53+
54+
_asset.pythonFiles.Add(file1);
55+
_asset.pythonFiles.Add(file2);
56+
57+
var validFiles = _asset.GetValidFiles().ToList();
58+
59+
Assert.AreEqual(2, validFiles.Count, "Should return all non-null files");
60+
CollectionAssert.Contains(validFiles, file1);
61+
CollectionAssert.Contains(validFiles, file2);
62+
}
63+
64+
[Test]
65+
public void NeedsSync_ReturnsTrue_WhenHashingDisabled()
66+
{
67+
_asset.useContentHashing = false;
68+
var textAsset = new TextAsset("print('test')");
69+
70+
bool needsSync = _asset.NeedsSync(textAsset, "any_hash");
71+
72+
Assert.IsTrue(needsSync, "Should always need sync when hashing disabled");
73+
}
74+
75+
[Test]
76+
public void NeedsSync_ReturnsTrue_WhenFileNotInStates()
77+
{
78+
_asset.useContentHashing = true;
79+
var textAsset = new TextAsset("print('test')");
80+
81+
bool needsSync = _asset.NeedsSync(textAsset, "new_hash");
82+
83+
Assert.IsTrue(needsSync, "Should need sync for new file");
84+
}
85+
86+
[Test]
87+
public void NeedsSync_ReturnsFalse_WhenHashMatches()
88+
{
89+
_asset.useContentHashing = true;
90+
var textAsset = new TextAsset("print('test')");
91+
string hash = "test_hash_123";
92+
93+
// Record the file with a hash
94+
_asset.RecordSync(textAsset, hash);
95+
96+
// Check if needs sync with same hash
97+
bool needsSync = _asset.NeedsSync(textAsset, hash);
98+
99+
Assert.IsFalse(needsSync, "Should not need sync when hash matches");
100+
}
101+
102+
[Test]
103+
public void NeedsSync_ReturnsTrue_WhenHashDiffers()
104+
{
105+
_asset.useContentHashing = true;
106+
var textAsset = new TextAsset("print('test')");
107+
108+
// Record with one hash
109+
_asset.RecordSync(textAsset, "old_hash");
110+
111+
// Check with different hash
112+
bool needsSync = _asset.NeedsSync(textAsset, "new_hash");
113+
114+
Assert.IsTrue(needsSync, "Should need sync when hash differs");
115+
}
116+
117+
[Test]
118+
public void RecordSync_AddsNewFileState()
119+
{
120+
var textAsset = new TextAsset("print('test')");
121+
string hash = "test_hash";
122+
123+
_asset.RecordSync(textAsset, hash);
124+
125+
Assert.AreEqual(1, _asset.fileStates.Count, "Should add one file state");
126+
Assert.AreEqual(hash, _asset.fileStates[0].contentHash, "Should store the hash");
127+
Assert.IsNotNull(_asset.fileStates[0].assetGuid, "Should store the GUID");
128+
}
129+
130+
[Test]
131+
public void RecordSync_UpdatesExistingFileState()
132+
{
133+
var textAsset = new TextAsset("print('test')");
134+
135+
// Record first time
136+
_asset.RecordSync(textAsset, "hash1");
137+
var firstTime = _asset.fileStates[0].lastSyncTime;
138+
139+
// Wait a tiny bit to ensure time difference
140+
System.Threading.Thread.Sleep(10);
141+
142+
// Record second time with different hash
143+
_asset.RecordSync(textAsset, "hash2");
144+
145+
Assert.AreEqual(1, _asset.fileStates.Count, "Should still have only one state");
146+
Assert.AreEqual("hash2", _asset.fileStates[0].contentHash, "Should update the hash");
147+
Assert.Greater(_asset.fileStates[0].lastSyncTime, firstTime, "Should update sync time");
148+
}
149+
150+
[Test]
151+
public void CleanupStaleStates_RemovesStatesForRemovedFiles()
152+
{
153+
var file1 = new TextAsset("print('test1')");
154+
var file2 = new TextAsset("print('test2')");
155+
156+
// Add both files
157+
_asset.pythonFiles.Add(file1);
158+
_asset.pythonFiles.Add(file2);
159+
160+
// Record sync for both
161+
_asset.RecordSync(file1, "hash1");
162+
_asset.RecordSync(file2, "hash2");
163+
164+
Assert.AreEqual(2, _asset.fileStates.Count, "Should have two states");
165+
166+
// Remove one file
167+
_asset.pythonFiles.Remove(file1);
168+
169+
// Cleanup
170+
_asset.CleanupStaleStates();
171+
172+
Assert.AreEqual(1, _asset.fileStates.Count, "Should have one state after cleanup");
173+
}
174+
175+
[Test]
176+
public void CleanupStaleStates_KeepsStatesForCurrentFiles()
177+
{
178+
var file1 = new TextAsset("print('test1')");
179+
180+
_asset.pythonFiles.Add(file1);
181+
_asset.RecordSync(file1, "hash1");
182+
183+
_asset.CleanupStaleStates();
184+
185+
Assert.AreEqual(1, _asset.fileStates.Count, "Should keep state for current file");
186+
}
187+
188+
[Test]
189+
public void CleanupStaleStates_HandlesEmptyFilesList()
190+
{
191+
// Add some states without corresponding files
192+
_asset.fileStates.Add(new PythonFileState
193+
{
194+
assetGuid = "fake_guid_1",
195+
contentHash = "hash1",
196+
fileName = "test1.py",
197+
lastSyncTime = DateTime.UtcNow
198+
});
199+
200+
_asset.CleanupStaleStates();
201+
202+
Assert.IsEmpty(_asset.fileStates, "Should remove all states when no files exist");
203+
}
204+
}
205+
}

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Data/PythonToolsAssetTests.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using NUnit.Framework;
4+
using UnityEngine;
5+
using MCPForUnity.Editor.Data;
6+
using MCPForUnity.Editor.Services;
7+
8+
namespace MCPForUnityTests.Editor.Services
9+
{
10+
public class PythonToolRegistryServiceTests
11+
{
12+
private PythonToolRegistryService _service;
13+
14+
[SetUp]
15+
public void SetUp()
16+
{
17+
_service = new PythonToolRegistryService();
18+
}
19+
20+
[Test]
21+
public void GetAllRegistries_ReturnsEmptyList_WhenNoPythonToolsAssetsExist()
22+
{
23+
var registries = _service.GetAllRegistries().ToList();
24+
25+
// Note: This might find assets in the test project, so we just verify it doesn't throw
26+
Assert.IsNotNull(registries, "Should return a non-null list");
27+
}
28+
29+
[Test]
30+
public void NeedsSync_ReturnsTrue_WhenHashingDisabled()
31+
{
32+
var asset = ScriptableObject.CreateInstance<PythonToolsAsset>();
33+
asset.useContentHashing = false;
34+
35+
var textAsset = new TextAsset("print('test')");
36+
37+
bool needsSync = _service.NeedsSync(asset, textAsset);
38+
39+
Assert.IsTrue(needsSync, "Should always need sync when hashing is disabled");
40+
41+
Object.DestroyImmediate(asset);
42+
}
43+
44+
[Test]
45+
public void NeedsSync_ReturnsTrue_WhenFileNotPreviouslySynced()
46+
{
47+
var asset = ScriptableObject.CreateInstance<PythonToolsAsset>();
48+
asset.useContentHashing = true;
49+
50+
var textAsset = new TextAsset("print('test')");
51+
52+
bool needsSync = _service.NeedsSync(asset, textAsset);
53+
54+
Assert.IsTrue(needsSync, "Should need sync for new file");
55+
56+
Object.DestroyImmediate(asset);
57+
}
58+
59+
[Test]
60+
public void NeedsSync_ReturnsFalse_WhenHashMatches()
61+
{
62+
var asset = ScriptableObject.CreateInstance<PythonToolsAsset>();
63+
asset.useContentHashing = true;
64+
65+
var textAsset = new TextAsset("print('test')");
66+
67+
// First sync
68+
_service.RecordSync(asset, textAsset);
69+
70+
// Check if needs sync again
71+
bool needsSync = _service.NeedsSync(asset, textAsset);
72+
73+
Assert.IsFalse(needsSync, "Should not need sync when hash matches");
74+
75+
Object.DestroyImmediate(asset);
76+
}
77+
78+
[Test]
79+
public void RecordSync_StoresFileState()
80+
{
81+
var asset = ScriptableObject.CreateInstance<PythonToolsAsset>();
82+
var textAsset = new TextAsset("print('test')");
83+
84+
_service.RecordSync(asset, textAsset);
85+
86+
Assert.AreEqual(1, asset.fileStates.Count, "Should have one file state recorded");
87+
Assert.IsNotNull(asset.fileStates[0].contentHash, "Hash should be stored");
88+
Assert.IsNotNull(asset.fileStates[0].assetGuid, "GUID should be stored");
89+
90+
Object.DestroyImmediate(asset);
91+
}
92+
93+
[Test]
94+
public void RecordSync_UpdatesExistingState_WhenFileAlreadyRecorded()
95+
{
96+
var asset = ScriptableObject.CreateInstance<PythonToolsAsset>();
97+
var textAsset = new TextAsset("print('test')");
98+
99+
// Record twice
100+
_service.RecordSync(asset, textAsset);
101+
var firstHash = asset.fileStates[0].contentHash;
102+
103+
_service.RecordSync(asset, textAsset);
104+
105+
Assert.AreEqual(1, asset.fileStates.Count, "Should still have only one state");
106+
Assert.AreEqual(firstHash, asset.fileStates[0].contentHash, "Hash should remain the same");
107+
108+
Object.DestroyImmediate(asset);
109+
}
110+
111+
[Test]
112+
public void ComputeHash_ReturnsSameHash_ForSameContent()
113+
{
114+
var textAsset1 = new TextAsset("print('hello')");
115+
var textAsset2 = new TextAsset("print('hello')");
116+
117+
string hash1 = _service.ComputeHash(textAsset1);
118+
string hash2 = _service.ComputeHash(textAsset2);
119+
120+
Assert.AreEqual(hash1, hash2, "Same content should produce same hash");
121+
}
122+
123+
[Test]
124+
public void ComputeHash_ReturnsDifferentHash_ForDifferentContent()
125+
{
126+
var textAsset1 = new TextAsset("print('hello')");
127+
var textAsset2 = new TextAsset("print('world')");
128+
129+
string hash1 = _service.ComputeHash(textAsset1);
130+
string hash2 = _service.ComputeHash(textAsset2);
131+
132+
Assert.AreNotEqual(hash1, hash2, "Different content should produce different hash");
133+
}
134+
}
135+
}

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/PythonToolRegistryServiceTests.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)