Skip to content
This repository was archived by the owner on Feb 3, 2023. It is now read-only.

Commit 5036eea

Browse files
committed
Added custom filter support
1 parent 14759a5 commit 5036eea

16 files changed

+1304
-1
lines changed

LibGit2Sharp.Tests/FilterFixture.cs

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using LibGit2Sharp.Tests.TestHelpers;
5+
using Xunit;
6+
7+
namespace LibGit2Sharp.Tests
8+
{
9+
public class FilterFixture : BaseFixture
10+
{
11+
private const int GitPassThrough = -30;
12+
13+
readonly Func<Stream, Stream, int> successCallback = (reader, writer) => 0;
14+
15+
private const string FilterName = "the-filter";
16+
readonly List<FilterAttributeEntry> attributes = new List<FilterAttributeEntry> { new FilterAttributeEntry("test") };
17+
18+
[Fact]
19+
public void CanRegisterFilterWithSingleAttribute()
20+
{
21+
var filter = new EmptyFilter(FilterName, attributes);
22+
Assert.Equal( attributes , filter.Attributes);
23+
}
24+
25+
[Fact]
26+
public void CanRegisterAndUnregisterTheSameFilter()
27+
{
28+
var filter = new EmptyFilter(FilterName + 1, attributes);
29+
30+
var registration = GlobalSettings.RegisterFilter(filter);
31+
GlobalSettings.DeregisterFilter(registration);
32+
33+
var secondRegistration = GlobalSettings.RegisterFilter(filter);
34+
GlobalSettings.DeregisterFilter(secondRegistration);
35+
}
36+
37+
[Fact]
38+
public void CanRegisterAndDeregisterAfterGarbageCollection()
39+
{
40+
var filter = new EmptyFilter(FilterName + 2, attributes);
41+
var filterRegistration = GlobalSettings.RegisterFilter(filter);
42+
43+
GC.Collect();
44+
45+
GlobalSettings.DeregisterFilter(filterRegistration);
46+
}
47+
48+
[Fact]
49+
public void SameFilterIsEqual()
50+
{
51+
var filter = new EmptyFilter(FilterName + 3, attributes);
52+
Assert.Equal(filter, filter);
53+
}
54+
55+
[Fact]
56+
public void InitCallbackNotMadeWhenFilterNeverUsed()
57+
{
58+
bool called = false;
59+
Func<int> initializeCallback = () =>
60+
{
61+
called = true;
62+
return 0;
63+
};
64+
65+
var filter = new FakeFilter(FilterName + 11, attributes,
66+
successCallback,
67+
successCallback,
68+
initializeCallback);
69+
70+
var filterRegistration = GlobalSettings.RegisterFilter(filter);
71+
72+
Assert.False(called);
73+
74+
GlobalSettings.DeregisterFilter(filterRegistration);
75+
}
76+
77+
[Fact]
78+
public void InitCallbackMadeWhenUsingTheFilter()
79+
{
80+
bool called = false;
81+
Func<int> initializeCallback = () =>
82+
{
83+
called = true;
84+
return 0;
85+
};
86+
87+
var filter = new FakeFilter(FilterName + 12, attributes,
88+
successCallback,
89+
successCallback,
90+
initializeCallback);
91+
92+
var filterRegistration = GlobalSettings.RegisterFilter(filter);
93+
Assert.False(called);
94+
95+
string repoPath = InitNewRepository();
96+
using (var repo = CreateTestRepository(repoPath))
97+
{
98+
StageNewFile(repo);
99+
Assert.True(called);
100+
}
101+
102+
GlobalSettings.DeregisterFilter(filterRegistration);
103+
}
104+
105+
[Fact]
106+
public void WhenStagingFileApplyIsCalledWithCleanForCorrectPath()
107+
{
108+
string repoPath = InitNewRepository();
109+
bool called = false;
110+
111+
Func<Stream, Stream, int> clean = (reader, writer) =>
112+
{
113+
called = true;
114+
return GitPassThrough;
115+
};
116+
var filter = new FakeFilter(FilterName + 15, attributes, clean);
117+
118+
var filterRegistration = GlobalSettings.RegisterFilter(filter);
119+
120+
using (var repo = CreateTestRepository(repoPath))
121+
{
122+
StageNewFile(repo);
123+
Assert.True(called);
124+
}
125+
126+
GlobalSettings.DeregisterFilter(filterRegistration);
127+
}
128+
129+
[Fact]
130+
public void CleanFilterWritesOutputToObjectTree()
131+
{
132+
const string decodedInput = "This is a substitution cipher";
133+
const string encodedInput = "Guvf vf n fhofgvghgvba pvcure";
134+
135+
string repoPath = InitNewRepository();
136+
137+
Func<Stream, Stream, int> cleanCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;
138+
139+
var filter = new FakeFilter(FilterName + 16, attributes, cleanCallback);
140+
141+
var filterRegistration = GlobalSettings.RegisterFilter(filter);
142+
143+
using (var repo = CreateTestRepository(repoPath))
144+
{
145+
FileInfo expectedFile = StageNewFile(repo, decodedInput);
146+
var commit = repo.Commit("Clean that file");
147+
148+
var blob = (Blob)commit.Tree[expectedFile.Name].Target;
149+
150+
var textDetected = blob.GetContentText();
151+
Assert.Equal(encodedInput, textDetected);
152+
}
153+
154+
GlobalSettings.DeregisterFilter(filterRegistration);
155+
}
156+
157+
158+
[Fact]
159+
public void WhenCheckingOutAFileFileSmudgeWritesCorrectFileToWorkingDirectory()
160+
{
161+
const string decodedInput = "This is a substitution cipher";
162+
const string encodedInput = "Guvf vf n fhofgvghgvba pvcure";
163+
164+
const string branchName = "branch";
165+
string repoPath = InitNewRepository();
166+
167+
Func<Stream, Stream, int> smudgeCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;
168+
169+
var filter = new FakeFilter(FilterName + 17, attributes, null, smudgeCallback);
170+
var filterRegistration = GlobalSettings.RegisterFilter(filter);
171+
172+
FileInfo expectedFile = CheckoutFileForSmudge(repoPath, branchName, encodedInput);
173+
174+
string combine = Path.Combine(repoPath, "..", expectedFile.Name);
175+
string readAllText = File.ReadAllText(combine);
176+
Assert.Equal(decodedInput, readAllText);
177+
178+
GlobalSettings.DeregisterFilter(filterRegistration);
179+
}
180+
181+
[Fact]
182+
public void FilterStreamsAreCoherent()
183+
{
184+
string repoPath = InitNewRepository();
185+
186+
bool? inputCanWrite = null, inputCanRead = null, inputCanSeek = null;
187+
bool? outputCanWrite = null, outputCanRead = null, outputCanSeek = null;
188+
189+
Func<Stream, Stream, int> assertor = (input, output) =>
190+
{
191+
inputCanRead = input.CanRead;
192+
inputCanWrite = input.CanWrite;
193+
inputCanSeek = input.CanSeek;
194+
195+
outputCanRead = output.CanRead;
196+
outputCanWrite = output.CanWrite;
197+
outputCanSeek = output.CanSeek;
198+
199+
return GitPassThrough;
200+
};
201+
202+
var filter = new FakeFilter(FilterName + 18, attributes, assertor, assertor);
203+
204+
var filterRegistration = GlobalSettings.RegisterFilter(filter);
205+
206+
using (var repo = CreateTestRepository(repoPath))
207+
{
208+
StageNewFile(repo);
209+
}
210+
211+
GlobalSettings.DeregisterFilter(filterRegistration);
212+
213+
Assert.True(inputCanRead.HasValue);
214+
Assert.True(inputCanWrite.HasValue);
215+
Assert.True(inputCanSeek.HasValue);
216+
Assert.True(outputCanRead.HasValue);
217+
Assert.True(outputCanWrite.HasValue);
218+
Assert.True(outputCanSeek.HasValue);
219+
220+
Assert.True(inputCanRead.Value);
221+
Assert.False(inputCanWrite.Value);
222+
Assert.False(inputCanSeek.Value);
223+
224+
Assert.False(outputCanRead.Value);
225+
Assert.True(outputCanWrite.Value);
226+
Assert.False(outputCanSeek.Value);
227+
}
228+
229+
private FileInfo CheckoutFileForSmudge(string repoPath, string branchName, string content)
230+
{
231+
FileInfo expectedPath;
232+
using (var repo = CreateTestRepository(repoPath))
233+
{
234+
StageNewFile(repo, content);
235+
236+
repo.Commit("Initial commit");
237+
238+
expectedPath = CommitFileOnBranch(repo, branchName, content);
239+
240+
repo.Checkout("master");
241+
242+
repo.Checkout(branchName);
243+
}
244+
return expectedPath;
245+
}
246+
247+
private static FileInfo CommitFileOnBranch(Repository repo, string branchName, String content)
248+
{
249+
var branch = repo.CreateBranch(branchName);
250+
repo.Checkout(branch.Name);
251+
252+
FileInfo expectedPath = StageNewFile(repo, content);
253+
repo.Commit("Commit");
254+
return expectedPath;
255+
}
256+
257+
private static FileInfo StageNewFile(IRepository repo, string contents = "null")
258+
{
259+
string newFilePath = Touch(repo.Info.WorkingDirectory, Guid.NewGuid() + ".txt", contents);
260+
var stageNewFile = new FileInfo(newFilePath);
261+
repo.Stage(newFilePath);
262+
return stageNewFile;
263+
}
264+
265+
private Repository CreateTestRepository(string path)
266+
{
267+
string configPath = CreateConfigurationWithDummyUser(Constants.Signature);
268+
var repositoryOptions = new RepositoryOptions { GlobalConfigurationLocation = configPath };
269+
var repository = new Repository(path, repositoryOptions);
270+
CreateAttributesFile(repository, "* filter=test");
271+
return repository;
272+
}
273+
274+
private static void CreateAttributesFile(IRepository repo, string attributeEntry)
275+
{
276+
Touch(repo.Info.WorkingDirectory, ".gitattributes", attributeEntry);
277+
}
278+
279+
class EmptyFilter : Filter
280+
{
281+
public EmptyFilter(string name, IEnumerable<FilterAttributeEntry> attributes)
282+
: base(name, attributes)
283+
{ }
284+
}
285+
286+
class FakeFilter : Filter
287+
{
288+
private readonly Func<Stream, Stream, int> cleanCallback;
289+
private readonly Func<Stream, Stream, int> smudgeCallback;
290+
private readonly Func<int> initCallback;
291+
292+
public FakeFilter(string name, IEnumerable<FilterAttributeEntry> attributes,
293+
Func<Stream, Stream, int> cleanCallback = null,
294+
Func<Stream, Stream, int> smudgeCallback = null,
295+
Func<int> initCallback = null)
296+
: base(name, attributes)
297+
{
298+
this.cleanCallback = cleanCallback;
299+
this.smudgeCallback = smudgeCallback;
300+
this.initCallback = initCallback;
301+
}
302+
303+
protected override int Clean(string path, Stream input, Stream output)
304+
{
305+
return cleanCallback != null ? cleanCallback(input, output) : base.Clean(path, input, output);
306+
}
307+
308+
protected override int Smudge(string path, Stream input, Stream output)
309+
{
310+
return smudgeCallback != null ? smudgeCallback(input, output) : base.Smudge(path, input, output);
311+
}
312+
313+
protected override int Initialize()
314+
{
315+
return initCallback != null ? initCallback() : base.Initialize();
316+
}
317+
}
318+
}
319+
}

0 commit comments

Comments
 (0)