Forge
csharpdeeb25a2
1using CascadeIDE.Models;
2using CascadeIDE.Services;
3using Xunit;
4
5namespace CascadeIDE.Tests;
6
7public sealed class WorkspaceIgnoreMatcherTests
8{
9 [Fact]
10 public void Folder_workspace_respects_gitignore_and_cascadeignore()
11 {
12 WorkspaceIgnoreMatcher.ClearCacheForTests();
13 var tmp = Directory.CreateTempSubdirectory("cascade_ws_ignore_");
14 try
15 {
16 File.WriteAllText(Path.Combine(tmp.FullName, ".gitignore"), "*.tmp\n");
17 File.WriteAllText(Path.Combine(tmp.FullName, ".cascadeignore"), "*.bak\n");
18 File.WriteAllText(Path.Combine(tmp.FullName, "keep.cs"), "//");
19 File.WriteAllText(Path.Combine(tmp.FullName, "x.tmp"), "");
20 File.WriteAllText(Path.Combine(tmp.FullName, "y.bak"), "");
21
22 var root = FolderWorkspaceTreeBuilder.TryBuild(tmp.FullName, out var err);
23 Assert.Null(err);
24 Assert.NotNull(root);
25 var names = CollectFileNames(root).ToHashSet(StringComparer.OrdinalIgnoreCase);
26 Assert.Contains("keep.cs", names);
27 Assert.DoesNotContain("x.tmp", names);
28 Assert.DoesNotContain("y.bak", names);
29 }
30 finally
31 {
32 try
33 {
34 Directory.Delete(tmp.FullName, recursive: true);
35 }
36 catch
37 {
38 // ignore
39 }
40
41 WorkspaceIgnoreMatcher.ClearCacheForTests();
42 }
43 }
44
45 [Fact]
46 public void GetOrCreate_applies_gitignore_to_paths()
47 {
48 WorkspaceIgnoreMatcher.ClearCacheForTests();
49 var tmp = Directory.CreateTempSubdirectory("cascade_matcher_");
50 try
51 {
52 File.WriteAllText(Path.Combine(tmp.FullName, ".gitignore"), "*.tmp\n");
53 var m = WorkspaceIgnoreMatcher.GetOrCreate(tmp.FullName);
54 Assert.True(m.IsIgnored(Path.Combine(tmp.FullName, "a.tmp")));
55 Assert.False(m.IsIgnored(Path.Combine(tmp.FullName, "a.cs")));
56 }
57 finally
58 {
59 try
60 {
61 Directory.Delete(tmp.FullName, recursive: true);
62 }
63 catch
64 {
65 // ignore
66 }
67
68 WorkspaceIgnoreMatcher.ClearCacheForTests();
69 }
70 }
71
72 [Fact]
73 public void GetOrCreate_negation_unignores_in_same_gitignore()
74 {
75 WorkspaceIgnoreMatcher.ClearCacheForTests();
76 var tmp = Directory.CreateTempSubdirectory("cascade_matcher_neg_");
77 try
78 {
79 File.WriteAllText(
80 Path.Combine(tmp.FullName, ".gitignore"),
81 """
82 *.tmp
83 !keep.tmp
84
85 """);
86 var m = WorkspaceIgnoreMatcher.GetOrCreate(tmp.FullName);
87 Assert.False(m.IsIgnored(Path.Combine(tmp.FullName, "keep.tmp")), "last match !keep.tmp");
88 Assert.True(m.IsIgnored(Path.Combine(tmp.FullName, "other.tmp")));
89 }
90 finally
91 {
92 try
93 {
94 Directory.Delete(tmp.FullName, recursive: true);
95 }
96 catch
97 {
98 // ignore
99 }
100
101 WorkspaceIgnoreMatcher.ClearCacheForTests();
102 }
103 }
104
105 [Fact]
106 public void GetOrCreate_nested_gitignore_negation_unignores_file()
107 {
108 WorkspaceIgnoreMatcher.ClearCacheForTests();
109 var tmp = Directory.CreateTempSubdirectory("cascade_matcher_nested_");
110 try
111 {
112 var sub = Path.Combine(tmp.FullName, "sub");
113 Directory.CreateDirectory(sub);
114 File.WriteAllText(Path.Combine(tmp.FullName, ".gitignore"), "*.tmp\n");
115 File.WriteAllText(Path.Combine(sub, ".gitignore"), "!keep.tmp\n");
116 File.WriteAllText(Path.Combine(sub, "keep.tmp"), "");
117 File.WriteAllText(Path.Combine(sub, "drop.tmp"), "");
118
119 var m = WorkspaceIgnoreMatcher.GetOrCreate(tmp.FullName);
120 Assert.False(m.IsIgnored(Path.Combine(sub, "keep.tmp")), "nested !keep.tmp after root *.tmp");
121 Assert.True(m.IsIgnored(Path.Combine(sub, "drop.tmp")));
122 }
123 finally
124 {
125 try
126 {
127 Directory.Delete(tmp.FullName, recursive: true);
128 }
129 catch
130 {
131 // ignore
132 }
133
134 WorkspaceIgnoreMatcher.ClearCacheForTests();
135 }
136 }
137
138 [Fact]
139 public void Folder_workspace_nested_gitignore_shows_unignored_tmp()
140 {
141 WorkspaceIgnoreMatcher.ClearCacheForTests();
142 var tmp = Directory.CreateTempSubdirectory("cascade_ws_nested_");
143 try
144 {
145 var sub = Path.Combine(tmp.FullName, "sub");
146 Directory.CreateDirectory(sub);
147 File.WriteAllText(Path.Combine(tmp.FullName, ".gitignore"), "*.tmp\n");
148 File.WriteAllText(Path.Combine(sub, ".gitignore"), "!keep.tmp\n");
149 File.WriteAllText(Path.Combine(tmp.FullName, "gone.tmp"), "");
150 File.WriteAllText(Path.Combine(sub, "keep.tmp"), "");
151 File.WriteAllText(Path.Combine(sub, "gone2.tmp"), "");
152 File.WriteAllText(Path.Combine(tmp.FullName, "ok.cs"), "//");
153
154 var root = FolderWorkspaceTreeBuilder.TryBuild(tmp.FullName, out var err);
155 Assert.Null(err);
156 Assert.NotNull(root);
157 var names = CollectFileNames(root).ToHashSet(StringComparer.OrdinalIgnoreCase);
158 Assert.Contains("ok.cs", names);
159 Assert.Contains("keep.tmp", names);
160 Assert.DoesNotContain("gone.tmp", names);
161 Assert.DoesNotContain("gone2.tmp", names);
162 }
163 finally
164 {
165 try
166 {
167 Directory.Delete(tmp.FullName, recursive: true);
168 }
169 catch
170 {
171 // ignore
172 }
173
174 WorkspaceIgnoreMatcher.ClearCacheForTests();
175 }
176 }
177
178 private static IEnumerable<string> CollectFileNames(SolutionItem node)
179 {
180 if (node.FullPath is { } p && File.Exists(p))
181 yield return Path.GetFileName(p);
182 foreach (var c in node.Children)
183 {
184 foreach (var n in CollectFileNames(c))
185 yield return n;
186 }
187 }
188}
189
View only · write via MCP/CIDE