1+ using Microsoft . CodeAnalysis ;
2+ using Microsoft . CodeAnalysis . CSharp . Testing ;
3+ using Microsoft . CodeAnalysis . Testing ;
14using Microsoft . CodeAnalysis . Text ;
25using System ;
36using System . Collections . Generic ;
@@ -22,6 +25,29 @@ private static (string, SourceText) MakeAssemblyScriptTypesGeneratedSource(IColl
2225 ) ;
2326 }
2427
28+ /// <summary>
29+ /// Creates a verifier with a custom GodotProjectDir, for testing out-of-tree source paths.
30+ /// </summary>
31+ private static CSharpSourceGeneratorVerifier < ScriptPathAttributeGenerator > . Test MakeVerifierWithCustomProjectDir (
32+ string godotProjectDir , string assemblyName = "TestProject" )
33+ {
34+ var verifier = new CSharpSourceGeneratorVerifier < ScriptPathAttributeGenerator > . Test ( ) ;
35+
36+ verifier . TestState . AnalyzerConfigFiles . Add ( ( "/.globalconfig" , $ """
37+ is_global = true
38+ build_property.GodotProjectDir = { godotProjectDir }
39+ """ ) ) ;
40+
41+ verifier . SolutionTransforms . Add ( ( Solution solution , ProjectId projectId ) =>
42+ {
43+ Project project = solution . GetProject ( projectId ) !
44+ . WithAssemblyName ( assemblyName ) ;
45+ return project . Solution ;
46+ } ) ;
47+
48+ return verifier ;
49+ }
50+
2551 [ Fact ]
2652 public async Task ScriptBoilerplate ( )
2753 {
@@ -78,4 +104,45 @@ public async Task NamespaceMultipleClassesSameName()
78104 verifier . TestState . GeneratedSources . Add ( MakeAssemblyScriptTypesGeneratedSource ( new string [ ] { "global::NamespaceA.SameName" , "global::NamespaceB.SameName" } ) ) ;
79105 await verifier . RunAsync ( ) ;
80106 }
107+
108+ /// <summary>
109+ /// Tests that an in-tree external assembly script (source inside Godot project dir)
110+ /// gets a normal res:// path.
111+ /// </summary>
112+ [ Fact ]
113+ public async Task ExternalAssemblyInTree ( )
114+ {
115+ var verifier = CSharpSourceGeneratorVerifier < ScriptPathAttributeGenerator > . MakeVerifier (
116+ new string [ ] { "ExternalScript.cs" } ,
117+ new string [ ] { "ExternalModule.ExternalScript_ScriptPath.generated.cs" }
118+ ) ;
119+ verifier . TestState . GeneratedSources . Add (
120+ MakeAssemblyScriptTypesGeneratedSource ( new string [ ] { "global::ExternalModule.ExternalScript" } ) ) ;
121+ await verifier . RunAsync ( ) ;
122+ }
123+
124+ /// <summary>
125+ /// Tests that an out-of-tree external assembly script (source outside Godot project dir)
126+ /// gets a synthetic csharp:// path instead of an invalid res://../ path.
127+ /// </summary>
128+ [ Fact ]
129+ public async Task ExternalAssemblyOutOfTree ( )
130+ {
131+ // Set GodotProjectDir to a subdirectory that won't contain the source file.
132+ // The Roslyn test framework resolves source paths relative to CWD. By setting
133+ // GodotProjectDir to a non-existent subdirectory, the computed relative path
134+ // will start with "../", triggering the csharp:// synthetic path logic.
135+ string deepProjectDir = Path . Combine ( Constants . ExecutingAssemblyPath , "nonexistent" , "godot_project" ) ;
136+
137+ var verifier = MakeVerifierWithCustomProjectDir ( deepProjectDir , "ExternalLib" ) ;
138+
139+ string source = File . ReadAllText ( Path . Combine ( Constants . SourceFolderPath , "ExternalScript.cs" ) ) ;
140+ verifier . TestState . Sources . Add ( ( "ExternalScript.cs" , SourceText . From ( source ) ) ) ;
141+
142+ // Skip exact source check because SourceFile contains a machine-dependent absolute path.
143+ verifier . TestBehaviors |=
144+ Microsoft . CodeAnalysis . Testing . TestBehaviors . SkipGeneratedSourcesCheck ;
145+
146+ await verifier . RunAsync ( ) ;
147+ }
81148}
0 commit comments