]> git.proxmox.com Git - libgit2.git/blob - tests/filter/stream.c
226092c3f32343e504cdd18a844f6e1b6455b03a
[libgit2.git] / tests / filter / stream.c
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
9 static git_repository *g_repo = NULL;
10
11 static git_filter *create_compress_filter(void);
12 static git_filter *compress_filter;
13
14 void test_filter_stream__initialize(void)
15 {
16 compress_filter = create_compress_filter();
17
18 cl_git_pass(git_filter_register("compress", compress_filter, 50));
19 g_repo = cl_git_sandbox_init("empty_standard_repo");
20 }
21
22 void test_filter_stream__cleanup(void)
23 {
24 cl_git_sandbox_cleanup();
25 g_repo = NULL;
26
27 git_filter_unregister("compress");
28 git__free(compress_filter);
29 }
30
31 #define CHUNKSIZE 10240
32
33 struct compress_stream {
34 git_writestream parent;
35 git_writestream *next;
36 git_filter_mode_t mode;
37 char current;
38 size_t current_chunk;
39 };
40
41 static int compress_stream_write__deflated(struct compress_stream *stream, const char *buffer, size_t len)
42 {
43 size_t idx = 0;
44
45 while (len > 0) {
46 size_t chunkremain, chunksize;
47
48 if (stream->current_chunk == 0)
49 stream->current = buffer[idx];
50
51 chunkremain = CHUNKSIZE - stream->current_chunk;
52 chunksize = min(chunkremain, len);
53
54 stream->current_chunk += chunksize;
55 len -= chunksize;
56 idx += chunksize;
57
58 if (stream->current_chunk == CHUNKSIZE) {
59 cl_git_pass(stream->next->write(stream->next, &stream->current, 1));
60 stream->current_chunk = 0;
61 }
62 }
63
64 return 0;
65 }
66
67 static int compress_stream_write__inflated(struct compress_stream *stream, const char *buffer, size_t len)
68 {
69 char inflated[CHUNKSIZE];
70 size_t i, j;
71
72 for (i = 0; i < len; i++) {
73 for (j = 0; j < CHUNKSIZE; j++)
74 inflated[j] = buffer[i];
75
76 cl_git_pass(stream->next->write(stream->next, inflated, CHUNKSIZE));
77 }
78
79 return 0;
80 }
81
82 static int compress_stream_write(git_writestream *s, const char *buffer, size_t len)
83 {
84 struct compress_stream *stream = (struct compress_stream *)s;
85
86 return (stream->mode == GIT_FILTER_TO_ODB) ?
87 compress_stream_write__deflated(stream, buffer, len) :
88 compress_stream_write__inflated(stream, buffer, len);
89 }
90
91 static int compress_stream_close(git_writestream *s)
92 {
93 struct compress_stream *stream = (struct compress_stream *)s;
94 cl_assert_equal_i(0, stream->current_chunk);
95 stream->next->close(stream->next);
96 return 0;
97 }
98
99 static void compress_stream_free(git_writestream *stream)
100 {
101 git__free(stream);
102 }
103
104 static int compress_filter_stream_init(
105 git_writestream **out,
106 git_filter *self,
107 void **payload,
108 const git_filter_source *src,
109 git_writestream *next)
110 {
111 struct compress_stream *stream = git__calloc(1, sizeof(struct compress_stream));
112 cl_assert(stream);
113
114 GIT_UNUSED(self);
115 GIT_UNUSED(payload);
116
117 stream->parent.write = compress_stream_write;
118 stream->parent.close = compress_stream_close;
119 stream->parent.free = compress_stream_free;
120 stream->next = next;
121 stream->mode = git_filter_source_mode(src);
122
123 *out = (git_writestream *)stream;
124 return 0;
125 }
126
127 git_filter *create_compress_filter(void)
128 {
129 git_filter *filter = git__calloc(1, sizeof(git_filter));
130 cl_assert(filter);
131
132 filter->version = GIT_FILTER_VERSION;
133 filter->attributes = "+compress";
134 filter->stream = compress_filter_stream_init;
135
136 return filter;
137 }
138
139 static void writefile(const char *filename, size_t numchunks)
140 {
141 git_buf path = GIT_BUF_INIT;
142 char buf[CHUNKSIZE];
143 size_t i = 0, j = 0;
144 int fd;
145
146 cl_git_pass(git_buf_joinpath(&path, "empty_standard_repo", filename));
147
148 fd = p_open(path.ptr, O_RDWR|O_CREAT, 0666);
149 cl_assert(fd >= 0);
150
151 for (i = 0; i < numchunks; i++) {
152 for (j = 0; j < CHUNKSIZE; j++) {
153 buf[j] = i % 256;
154 }
155
156 cl_git_pass(p_write(fd, buf, CHUNKSIZE));
157 }
158 p_close(fd);
159
160 git_buf_dispose(&path);
161 }
162
163 static void test_stream(size_t numchunks)
164 {
165 git_index *index;
166 const git_index_entry *entry;
167 git_blob *blob;
168 struct stat st;
169 git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
170
171 checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
172
173 cl_git_mkfile(
174 "empty_standard_repo/.gitattributes",
175 "* compress\n");
176
177 /* write a file to disk */
178 writefile("streamed_file", numchunks);
179
180 /* place it in the index */
181 cl_git_pass(git_repository_index(&index, g_repo));
182 cl_git_pass(git_index_add_bypath(index, "streamed_file"));
183 cl_git_pass(git_index_write(index));
184
185 /* ensure it was appropriately compressed */
186 cl_assert(entry = git_index_get_bypath(index, "streamed_file", 0));
187
188 cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
189 cl_assert_equal_i(numchunks, git_blob_rawsize(blob));
190
191 /* check the file back out */
192 cl_must_pass(p_unlink("empty_standard_repo/streamed_file"));
193 cl_git_pass(git_checkout_index(g_repo, index, &checkout_opts));
194
195 /* ensure it was decompressed */
196 cl_must_pass(p_stat("empty_standard_repo/streamed_file", &st));
197 cl_assert_equal_sz((numchunks * CHUNKSIZE), st.st_size);
198
199 git_index_free(index);
200 git_blob_free(blob);
201 }
202
203 /* write a 50KB file through the "compression" stream */
204 void test_filter_stream__smallfile(void)
205 {
206 test_stream(5);
207 }
208
209 /* optionally write a 500 MB file through the compression stream */
210 void test_filter_stream__bigfile(void)
211 {
212 if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE"))
213 cl_skip();
214
215 test_stream(51200);
216 }