Skip to content

Commit eb442fc

Browse files
ammeepJ Wyman
authored andcommitted
Added custom filter support
1 parent 8008e2d commit eb442fc

14 files changed

+1112
-1
lines changed

LibGit2Sharp.Tests/FilterFixture.cs

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
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+
private FileInfo CheckoutFileForSmudge(string repoPath, string branchName, string content)
182+
{
183+
FileInfo expectedPath;
184+
using (var repo = CreateTestRepository(repoPath))
185+
{
186+
StageNewFile(repo, content);
187+
188+
repo.Commit("Initial commit");
189+
190+
expectedPath = CommitFileOnBranch(repo, branchName, content);
191+
192+
repo.Checkout("master");
193+
194+
repo.Checkout(branchName);
195+
}
196+
return expectedPath;
197+
}
198+
199+
private static FileInfo CommitFileOnBranch(Repository repo, string branchName, String content)
200+
{
201+
var branch = repo.CreateBranch(branchName);
202+
repo.Checkout(branch.Name);
203+
204+
FileInfo expectedPath = StageNewFile(repo, content);
205+
repo.Commit("Commit");
206+
return expectedPath;
207+
}
208+
209+
private static FileInfo StageNewFile(IRepository repo, string contents = "null")
210+
{
211+
string newFilePath = Touch(repo.Info.WorkingDirectory, Guid.NewGuid() + ".txt", contents);
212+
var stageNewFile = new FileInfo(newFilePath);
213+
repo.Stage(newFilePath);
214+
return stageNewFile;
215+
}
216+
217+
private Repository CreateTestRepository(string path)
218+
{
219+
string configPath = CreateConfigurationWithDummyUser(Constants.Signature);
220+
var repositoryOptions = new RepositoryOptions { GlobalConfigurationLocation = configPath };
221+
var repository = new Repository(path, repositoryOptions);
222+
CreateAttributesFile(repository, "* filter=test");
223+
return repository;
224+
}
225+
226+
private static void CreateAttributesFile(IRepository repo, string attributeEntry)
227+
{
228+
Touch(repo.Info.WorkingDirectory, ".gitattributes", attributeEntry);
229+
}
230+
231+
class EmptyFilter : Filter
232+
{
233+
public EmptyFilter(string name, IEnumerable<FilterAttributeEntry> attributes)
234+
: base(name, attributes)
235+
{ }
236+
}
237+
238+
class FakeFilter : Filter
239+
{
240+
private readonly Func<Stream, Stream, int> cleanCallback;
241+
private readonly Func<Stream, Stream, int> smudgeCallback;
242+
private readonly Func<int> initCallback;
243+
244+
public FakeFilter(string name, IEnumerable<FilterAttributeEntry> attributes,
245+
Func<Stream, Stream, int> cleanCallback = null,
246+
Func<Stream, Stream, int> smudgeCallback = null,
247+
Func<int> initCallback = null)
248+
: base(name, attributes)
249+
{
250+
this.cleanCallback = cleanCallback;
251+
this.smudgeCallback = smudgeCallback;
252+
this.initCallback = initCallback;
253+
}
254+
255+
protected override int Clean(string path, Stream input, Stream output)
256+
{
257+
return cleanCallback != null ? cleanCallback(input, output) : base.Clean(path, input, output);
258+
}
259+
260+
protected override int Smudge(string path, Stream input, Stream output)
261+
{
262+
return smudgeCallback != null ? smudgeCallback(input, output) : base.Smudge(path, input, output);
263+
}
264+
265+
protected override int Initialize()
266+
{
267+
return initCallback != null ? initCallback() : base.Initialize();
268+
}
269+
}
270+
}
271+
}

0 commit comments

Comments
 (0)