]>
Commit | Line | Data |
---|---|---|
b47349b8 RB |
1 | #include "clar_libgit2.h" |
2 | #include "posix.h" | |
3 | #include "blob.h" | |
4 | #include "filter.h" | |
5 | #include "buf_text.h" | |
6 | #include "git2/sys/filter.h" | |
7 | #include "git2/sys/repository.h" | |
8 | ||
eefc32d5 RB |
9 | /* going TO_WORKDIR, filters are executed low to high |
10 | * going TO_ODB, filters are executed high to low | |
11 | */ | |
12 | #define BITFLIP_FILTER_PRIORITY -1 | |
13 | #define REVERSE_FILTER_PRIORITY -2 | |
b47349b8 RB |
14 | |
15 | #define VERY_SECURE_ENCRYPTION(b) ((b) ^ 0xff) | |
16 | ||
17 | #ifdef GIT_WIN32 | |
18 | # define NEWLINE "\r\n" | |
19 | #else | |
20 | # define NEWLINE "\n" | |
21 | #endif | |
22 | ||
23 | static char workdir_data[] = | |
24 | "some simple" NEWLINE | |
25 | "data" NEWLINE | |
26 | "that will be" NEWLINE | |
27 | "trivially" NEWLINE | |
28 | "scrambled." NEWLINE; | |
29 | ||
30 | /* Represents the data above scrambled (bits flipped) after \r\n -> \n | |
31 | * conversion, then bytewise reversed | |
32 | */ | |
33 | static unsigned char bitflipped_and_reversed_data[] = | |
34 | { 0xf5, 0xd1, 0x9b, 0x9a, 0x93, 0x9d, 0x92, 0x9e, 0x8d, 0x9c, 0x8c, | |
35 | 0xf5, 0x86, 0x93, 0x93, 0x9e, 0x96, 0x89, 0x96, 0x8d, 0x8b, 0xf5, | |
36 | 0x9a, 0x9d, 0xdf, 0x93, 0x93, 0x96, 0x88, 0xdf, 0x8b, 0x9e, 0x97, | |
37 | 0x8b, 0xf5, 0x9e, 0x8b, 0x9e, 0x9b, 0xf5, 0x9a, 0x93, 0x8f, 0x92, | |
38 | 0x96, 0x8c, 0xdf, 0x9a, 0x92, 0x90, 0x8c }; | |
39 | ||
40 | #define BITFLIPPED_AND_REVERSED_DATA_LEN 51 | |
41 | ||
42 | static git_repository *g_repo = NULL; | |
43 | ||
44 | static void register_custom_filters(void); | |
45 | ||
46 | void test_filter_custom__initialize(void) | |
47 | { | |
48 | register_custom_filters(); | |
49 | ||
50 | g_repo = cl_git_sandbox_init("empty_standard_repo"); | |
51 | ||
52 | cl_git_mkfile( | |
53 | "empty_standard_repo/.gitattributes", | |
54 | "hero* bitflip reverse\n" | |
55 | "herofile text\n" | |
ad7417d7 RB |
56 | "heroflip -reverse binary\n" |
57 | "*.bin binary\n"); | |
b47349b8 RB |
58 | } |
59 | ||
60 | void test_filter_custom__cleanup(void) | |
61 | { | |
62 | cl_git_sandbox_cleanup(); | |
63 | g_repo = NULL; | |
64 | } | |
65 | ||
66 | static int bitflip_filter_apply( | |
67 | git_filter *self, | |
68 | void **payload, | |
69 | git_buf *to, | |
70 | const git_buf *from, | |
71 | const git_filter_source *source) | |
72 | { | |
73 | const unsigned char *src = (const unsigned char *)from->ptr; | |
74 | unsigned char *dst; | |
75 | size_t i; | |
76 | ||
77 | GIT_UNUSED(self); GIT_UNUSED(payload); | |
78 | ||
79 | /* verify that attribute path match worked as expected */ | |
80 | cl_assert_equal_i( | |
81 | 0, git__strncmp("hero", git_filter_source_path(source), 4)); | |
82 | ||
83 | if (!from->size) | |
84 | return 0; | |
85 | ||
86 | cl_git_pass(git_buf_grow(to, from->size)); | |
87 | ||
88 | dst = (unsigned char *)to->ptr; | |
89 | ||
90 | for (i = 0; i < from->size; i++) | |
91 | dst[i] = VERY_SECURE_ENCRYPTION(src[i]); | |
92 | ||
93 | to->size = from->size; | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | static void bitflip_filter_free(git_filter *f) | |
99 | { | |
100 | git__free(f); | |
101 | } | |
102 | ||
103 | static git_filter *create_bitflip_filter(void) | |
104 | { | |
105 | git_filter *filter = git__calloc(1, sizeof(git_filter)); | |
106 | cl_assert(filter); | |
107 | ||
108 | filter->version = GIT_FILTER_VERSION; | |
109 | filter->attributes = "+bitflip"; | |
110 | filter->shutdown = bitflip_filter_free; | |
111 | filter->apply = bitflip_filter_apply; | |
112 | ||
113 | return filter; | |
114 | } | |
115 | ||
116 | ||
117 | static int reverse_filter_apply( | |
118 | git_filter *self, | |
119 | void **payload, | |
120 | git_buf *to, | |
121 | const git_buf *from, | |
122 | const git_filter_source *source) | |
123 | { | |
124 | const unsigned char *src = (const unsigned char *)from->ptr; | |
125 | const unsigned char *end = src + from->size; | |
126 | unsigned char *dst; | |
127 | ||
128 | GIT_UNUSED(self); GIT_UNUSED(payload); GIT_UNUSED(source); | |
129 | ||
130 | /* verify that attribute path match worked as expected */ | |
131 | cl_assert_equal_i( | |
132 | 0, git__strncmp("hero", git_filter_source_path(source), 4)); | |
133 | ||
134 | if (!from->size) | |
135 | return 0; | |
136 | ||
137 | cl_git_pass(git_buf_grow(to, from->size)); | |
138 | ||
139 | dst = (unsigned char *)to->ptr + from->size - 1; | |
140 | ||
141 | while (src < end) | |
142 | *dst-- = *src++; | |
143 | ||
144 | to->size = from->size; | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | static void reverse_filter_free(git_filter *f) | |
150 | { | |
151 | git__free(f); | |
152 | } | |
153 | ||
eefc32d5 | 154 | static git_filter *create_reverse_filter(const char *attrs) |
b47349b8 RB |
155 | { |
156 | git_filter *filter = git__calloc(1, sizeof(git_filter)); | |
157 | cl_assert(filter); | |
158 | ||
159 | filter->version = GIT_FILTER_VERSION; | |
eefc32d5 | 160 | filter->attributes = attrs; |
b47349b8 RB |
161 | filter->shutdown = reverse_filter_free; |
162 | filter->apply = reverse_filter_apply; | |
163 | ||
164 | return filter; | |
165 | } | |
166 | ||
167 | static void register_custom_filters(void) | |
168 | { | |
169 | static int filters_registered = 0; | |
170 | ||
171 | if (!filters_registered) { | |
172 | cl_git_pass(git_filter_register( | |
173 | "bitflip", create_bitflip_filter(), BITFLIP_FILTER_PRIORITY)); | |
174 | ||
175 | cl_git_pass(git_filter_register( | |
eefc32d5 RB |
176 | "reverse", create_reverse_filter("+reverse"), |
177 | REVERSE_FILTER_PRIORITY)); | |
178 | ||
179 | /* re-register reverse filter with standard filter=xyz priority */ | |
180 | cl_git_pass(git_filter_register( | |
181 | "pre-reverse", | |
182 | create_reverse_filter("+prereverse"), | |
183 | GIT_FILTER_DRIVER_PRIORITY)); | |
b47349b8 RB |
184 | |
185 | filters_registered = 1; | |
186 | } | |
187 | } | |
188 | ||
189 | ||
190 | void test_filter_custom__to_odb(void) | |
191 | { | |
192 | git_filter_list *fl; | |
193 | git_buf out = { 0 }; | |
194 | git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data)); | |
195 | ||
196 | cl_git_pass(git_filter_list_load( | |
197 | &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB)); | |
198 | ||
199 | cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); | |
200 | ||
201 | cl_assert_equal_i(BITFLIPPED_AND_REVERSED_DATA_LEN, out.size); | |
202 | ||
203 | cl_assert_equal_i( | |
204 | 0, memcmp(bitflipped_and_reversed_data, out.ptr, out.size)); | |
205 | ||
206 | git_filter_list_free(fl); | |
207 | git_buf_free(&out); | |
208 | } | |
209 | ||
210 | void test_filter_custom__to_workdir(void) | |
211 | { | |
212 | git_filter_list *fl; | |
213 | git_buf out = { 0 }; | |
214 | git_buf in = GIT_BUF_INIT_CONST( | |
215 | bitflipped_and_reversed_data, BITFLIPPED_AND_REVERSED_DATA_LEN); | |
216 | ||
217 | cl_git_pass(git_filter_list_load( | |
218 | &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE)); | |
219 | ||
220 | cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); | |
221 | ||
222 | cl_assert_equal_i(strlen(workdir_data), out.size); | |
223 | ||
224 | cl_assert_equal_i( | |
225 | 0, memcmp(workdir_data, out.ptr, out.size)); | |
226 | ||
227 | git_filter_list_free(fl); | |
228 | git_buf_free(&out); | |
229 | } | |
230 | ||
231 | void test_filter_custom__can_register_a_custom_filter_in_the_repository(void) | |
232 | { | |
233 | git_filter_list *fl; | |
234 | ||
235 | cl_git_pass(git_filter_list_load( | |
236 | &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE)); | |
237 | /* expect: bitflip, reverse, crlf */ | |
238 | cl_assert_equal_sz(3, git_filter_list_length(fl)); | |
239 | git_filter_list_free(fl); | |
240 | ||
241 | cl_git_pass(git_filter_list_load( | |
242 | &fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE)); | |
ad7417d7 RB |
243 | /* expect: bitflip, reverse - possibly crlf depending on global config */ |
244 | { | |
245 | size_t flen = git_filter_list_length(fl); | |
246 | cl_assert(flen == 2 || flen == 3); | |
247 | } | |
248 | git_filter_list_free(fl); | |
249 | ||
250 | cl_git_pass(git_filter_list_load( | |
251 | &fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE)); | |
b47349b8 RB |
252 | /* expect: bitflip, reverse */ |
253 | cl_assert_equal_sz(2, git_filter_list_length(fl)); | |
254 | git_filter_list_free(fl); | |
255 | ||
256 | cl_git_pass(git_filter_list_load( | |
257 | &fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE)); | |
258 | /* expect: bitflip (because of -reverse) */ | |
259 | cl_assert_equal_sz(1, git_filter_list_length(fl)); | |
260 | git_filter_list_free(fl); | |
261 | ||
262 | cl_git_pass(git_filter_list_load( | |
ad7417d7 | 263 | &fl, g_repo, NULL, "doesntapplytome.bin", GIT_FILTER_TO_WORKTREE)); |
b47349b8 RB |
264 | /* expect: none */ |
265 | cl_assert_equal_sz(0, git_filter_list_length(fl)); | |
266 | git_filter_list_free(fl); | |
267 | } | |
eab3746b RB |
268 | |
269 | void test_filter_custom__order_dependency(void) | |
270 | { | |
271 | git_index *index; | |
272 | git_blob *blob; | |
273 | git_buf buf = { 0 }; | |
274 | ||
275 | /* so if ident and reverse are used together, an interesting thing | |
276 | * happens - a reversed "$Id$" string is no longer going to trigger | |
277 | * ident correctly. When checking out, the filters should be applied | |
278 | * in order CLRF, then ident, then reverse, so ident expansion should | |
279 | * work correctly. On check in, the content should be reversed, then | |
280 | * ident, then CRLF filtered. Let's make sure that works... | |
281 | */ | |
282 | ||
283 | cl_git_mkfile( | |
284 | "empty_standard_repo/.gitattributes", | |
eefc32d5 | 285 | "hero.*.rev-ident text ident prereverse eol=lf\n"); |
eab3746b RB |
286 | |
287 | cl_git_mkfile( | |
288 | "empty_standard_repo/hero.1.rev-ident", | |
289 | "This is a test\n$Id$\nHave fun!\n"); | |
290 | ||
291 | cl_git_mkfile( | |
292 | "empty_standard_repo/hero.2.rev-ident", | |
293 | "Another test\n$dI$\nCrazy!\n"); | |
294 | ||
295 | cl_git_pass(git_repository_index(&index, g_repo)); | |
296 | cl_git_pass(git_index_add_bypath(index, "hero.1.rev-ident")); | |
297 | cl_git_pass(git_index_add_bypath(index, "hero.2.rev-ident")); | |
298 | cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Filter chains\n"); | |
299 | git_index_free(index); | |
300 | ||
301 | cl_git_pass(git_blob_lookup(&blob, g_repo, | |
d541170c | 302 | & git_index_get_bypath(index, "hero.1.rev-ident", 0)->id)); |
eab3746b RB |
303 | cl_assert_equal_s( |
304 | "\n!nuf evaH\n$dI$\ntset a si sihT", git_blob_rawcontent(blob)); | |
305 | cl_git_pass(git_blob_filtered_content(&buf, blob, "hero.1.rev-ident", 0)); | |
306 | /* no expansion because id was reversed at checkin and now at ident | |
307 | * time, reverse is not applied yet */ | |
308 | cl_assert_equal_s( | |
309 | "This is a test\n$Id$\nHave fun!\n", buf.ptr); | |
310 | git_blob_free(blob); | |
311 | ||
312 | cl_git_pass(git_blob_lookup(&blob, g_repo, | |
d541170c | 313 | & git_index_get_bypath(index, "hero.2.rev-ident", 0)->id)); |
eab3746b RB |
314 | cl_assert_equal_s( |
315 | "\n!yzarC\n$Id$\ntset rehtonA", git_blob_rawcontent(blob)); | |
316 | cl_git_pass(git_blob_filtered_content(&buf, blob, "hero.2.rev-ident", 0)); | |
317 | /* expansion because reverse was applied at checkin and at ident time, | |
318 | * reverse is not applied yet */ | |
319 | cl_assert_equal_s( | |
320 | "Another test\n$59001fe193103b1016b27027c0c827d036fd0ac8 :dI$\nCrazy!\n", buf.ptr); | |
321 | cl_assert_equal_i(0, git_oid_strcmp( | |
322 | git_blob_id(blob), "8ca0df630d728c0c72072b6101b301391ef10095")); | |
323 | git_blob_free(blob); | |
324 | ||
325 | git_buf_free(&buf); | |
326 | } | |
eefc32d5 RB |
327 | |
328 | void test_filter_custom__filter_registry_failure_cases(void) | |
329 | { | |
330 | git_filter fake = { GIT_FILTER_VERSION, 0 }; | |
331 | ||
332 | cl_assert_equal_i(GIT_EEXISTS, git_filter_register("bitflip", &fake, 0)); | |
333 | ||
334 | cl_git_fail(git_filter_unregister(GIT_FILTER_CRLF)); | |
335 | cl_git_fail(git_filter_unregister(GIT_FILTER_IDENT)); | |
336 | cl_assert_equal_i(GIT_ENOTFOUND, git_filter_unregister("not-a-filter")); | |
337 | } |