1 #include "clar_libgit2.h"
3 #include "git2/clone.h"
4 #include "git2/cred_helpers.h"
9 #define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
10 #define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
11 #define BB_REPO_URL "https://libgit3@bitbucket.org/libgit2/testgitrepository.git"
12 #define BB_REPO_URL_WITH_PASS "https://libgit3:libgit3@bitbucket.org/libgit2/testgitrepository.git"
13 #define BB_REPO_URL_WITH_WRONG_PASS "https://libgit3:wrong@bitbucket.org/libgit2/testgitrepository.git"
15 #define SSH_REPO_URL "ssh://github.com/libgit2/TestGitRepository"
17 static git_repository
*g_repo
;
18 static git_clone_options g_options
;
20 void test_online_clone__initialize(void)
22 git_checkout_options dummy_opts
= GIT_CHECKOUT_OPTIONS_INIT
;
23 git_fetch_options dummy_fetch
= GIT_FETCH_OPTIONS_INIT
;
27 memset(&g_options
, 0, sizeof(git_clone_options
));
28 g_options
.version
= GIT_CLONE_OPTIONS_VERSION
;
29 g_options
.checkout_opts
= dummy_opts
;
30 g_options
.checkout_opts
.checkout_strategy
= GIT_CHECKOUT_SAFE
;
31 g_options
.fetch_opts
= dummy_fetch
;
34 void test_online_clone__cleanup(void)
37 git_repository_free(g_repo
);
40 cl_fixture_cleanup("./foo");
43 void test_online_clone__network_full(void)
47 cl_git_pass(git_clone(&g_repo
, LIVE_REPO_URL
, "./foo", &g_options
));
48 cl_assert(!git_repository_is_bare(g_repo
));
49 cl_git_pass(git_remote_lookup(&origin
, g_repo
, "origin"));
51 cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO
, origin
->download_tags
);
53 git_remote_free(origin
);
56 void test_online_clone__network_bare(void)
60 g_options
.bare
= true;
62 cl_git_pass(git_clone(&g_repo
, LIVE_REPO_URL
, "./foo", &g_options
));
63 cl_assert(git_repository_is_bare(g_repo
));
64 cl_git_pass(git_remote_lookup(&origin
, g_repo
, "origin"));
66 git_remote_free(origin
);
69 void test_online_clone__empty_repository(void)
73 cl_git_pass(git_clone(&g_repo
, LIVE_EMPTYREPO_URL
, "./foo", &g_options
));
75 cl_assert_equal_i(true, git_repository_is_empty(g_repo
));
76 cl_assert_equal_i(true, git_repository_head_unborn(g_repo
));
78 cl_git_pass(git_reference_lookup(&head
, g_repo
, GIT_HEAD_FILE
));
79 cl_assert_equal_i(GIT_REF_SYMBOLIC
, git_reference_type(head
));
80 cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head
));
82 git_reference_free(head
);
85 static void checkout_progress(const char *path
, size_t cur
, size_t tot
, void *payload
)
87 bool *was_called
= (bool*)payload
;
88 GIT_UNUSED(path
); GIT_UNUSED(cur
); GIT_UNUSED(tot
);
92 static int fetch_progress(const git_transfer_progress
*stats
, void *payload
)
94 bool *was_called
= (bool*)payload
;
100 void test_online_clone__can_checkout_a_cloned_repo(void)
102 git_buf path
= GIT_BUF_INIT
;
104 bool checkout_progress_cb_was_called
= false,
105 fetch_progress_cb_was_called
= false;
107 g_options
.checkout_opts
.checkout_strategy
= GIT_CHECKOUT_SAFE
;
108 g_options
.checkout_opts
.progress_cb
= &checkout_progress
;
109 g_options
.checkout_opts
.progress_payload
= &checkout_progress_cb_was_called
;
110 g_options
.fetch_opts
.callbacks
.transfer_progress
= &fetch_progress
;
111 g_options
.fetch_opts
.callbacks
.payload
= &fetch_progress_cb_was_called
;
113 cl_git_pass(git_clone(&g_repo
, LIVE_REPO_URL
, "./foo", &g_options
));
115 cl_git_pass(git_buf_joinpath(&path
, git_repository_workdir(g_repo
), "master.txt"));
116 cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path
)));
118 cl_git_pass(git_reference_lookup(&head
, g_repo
, "HEAD"));
119 cl_assert_equal_i(GIT_REF_SYMBOLIC
, git_reference_type(head
));
120 cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head
));
122 cl_assert_equal_i(true, checkout_progress_cb_was_called
);
123 cl_assert_equal_i(true, fetch_progress_cb_was_called
);
125 git_reference_free(head
);
129 static int remote_mirror_cb(git_remote
**out
, git_repository
*repo
,
130 const char *name
, const char *url
, void *payload
)
137 if ((error
= git_remote_create_with_fetchspec(&remote
, repo
, name
, url
, "+refs/*:refs/*")) < 0)
144 void test_online_clone__clone_mirror(void)
146 git_clone_options opts
= GIT_CLONE_OPTIONS_INIT
;
149 bool fetch_progress_cb_was_called
= false;
151 opts
.fetch_opts
.callbacks
.transfer_progress
= &fetch_progress
;
152 opts
.fetch_opts
.callbacks
.payload
= &fetch_progress_cb_was_called
;
155 opts
.remote_cb
= remote_mirror_cb
;
157 cl_git_pass(git_clone(&g_repo
, LIVE_REPO_URL
, "./foo.git", &opts
));
159 cl_git_pass(git_reference_lookup(&head
, g_repo
, "HEAD"));
160 cl_assert_equal_i(GIT_REF_SYMBOLIC
, git_reference_type(head
));
161 cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head
));
163 cl_assert_equal_i(true, fetch_progress_cb_was_called
);
165 git_reference_free(head
);
166 git_repository_free(g_repo
);
169 cl_fixture_cleanup("./foo.git");
172 static int update_tips(const char *refname
, const git_oid
*a
, const git_oid
*b
, void *payload
)
174 int *callcount
= (int*)payload
;
175 GIT_UNUSED(refname
); GIT_UNUSED(a
); GIT_UNUSED(b
);
176 *callcount
= *callcount
+ 1;
180 void test_online_clone__custom_remote_callbacks(void)
184 g_options
.fetch_opts
.callbacks
.update_tips
= update_tips
;
185 g_options
.fetch_opts
.callbacks
.payload
= &callcount
;
187 cl_git_pass(git_clone(&g_repo
, LIVE_REPO_URL
, "./foo", &g_options
));
188 cl_assert(callcount
> 0);
191 static int cred_failure_cb(
194 const char *username_from_url
,
195 unsigned int allowed_types
,
198 GIT_UNUSED(cred
); GIT_UNUSED(url
); GIT_UNUSED(username_from_url
);
199 GIT_UNUSED(allowed_types
); GIT_UNUSED(data
);
203 void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
205 const char *remote_url
= cl_getenv("GITTEST_REMOTE_URL");
206 const char *remote_user
= cl_getenv("GITTEST_REMOTE_USER");
208 if (!remote_url
|| !remote_user
)
211 g_options
.fetch_opts
.callbacks
.credentials
= cred_failure_cb
;
213 cl_git_fail_with(-172, git_clone(&g_repo
, remote_url
, "./foo", &g_options
));
216 static int cred_count_calls_cb(git_cred
**cred
, const char *url
, const char *user
,
217 unsigned int allowed_types
, void *data
)
219 size_t *counter
= (size_t *) data
;
221 GIT_UNUSED(url
); GIT_UNUSED(user
); GIT_UNUSED(allowed_types
);
223 if (allowed_types
== GIT_CREDTYPE_USERNAME
)
224 return git_cred_username_new(cred
, "foo");
231 return git_cred_userpass_plaintext_new(cred
, "foo", "bar");
234 void test_online_clone__cred_callback_called_again_on_auth_failure(void)
236 const char *remote_url
= cl_getenv("GITTEST_REMOTE_URL");
237 const char *remote_user
= cl_getenv("GITTEST_REMOTE_USER");
240 if (!remote_url
|| !remote_user
)
243 g_options
.fetch_opts
.callbacks
.credentials
= cred_count_calls_cb
;
244 g_options
.fetch_opts
.callbacks
.payload
= &counter
;
246 cl_git_fail_with(GIT_EUSER
, git_clone(&g_repo
, remote_url
, "./foo", &g_options
));
247 cl_assert_equal_i(3, counter
);
253 const char *user_from_url
,
254 unsigned int allowed_types
,
258 GIT_UNUSED(user_from_url
);
261 if (!(allowed_types
& GIT_CREDTYPE_DEFAULT
))
264 return git_cred_default_new(cred
);
267 void test_online_clone__credentials(void)
269 /* Remote URL environment variable must be set.
270 * User and password are optional.
272 const char *remote_url
= cl_getenv("GITTEST_REMOTE_URL");
273 git_cred_userpass_payload user_pass
= {
274 cl_getenv("GITTEST_REMOTE_USER"),
275 cl_getenv("GITTEST_REMOTE_PASS")
278 if (!remote_url
) return;
280 if (cl_getenv("GITTEST_REMOTE_DEFAULT")) {
281 g_options
.fetch_opts
.callbacks
.credentials
= cred_default
;
283 g_options
.fetch_opts
.callbacks
.credentials
= git_cred_userpass
;
284 g_options
.fetch_opts
.callbacks
.payload
= &user_pass
;
287 cl_git_pass(git_clone(&g_repo
, remote_url
, "./foo", &g_options
));
288 git_repository_free(g_repo
); g_repo
= NULL
;
289 cl_fixture_cleanup("./foo");
292 void test_online_clone__bitbucket_style(void)
294 git_cred_userpass_payload user_pass
= {
298 g_options
.fetch_opts
.callbacks
.credentials
= git_cred_userpass
;
299 g_options
.fetch_opts
.callbacks
.payload
= &user_pass
;
301 cl_git_pass(git_clone(&g_repo
, BB_REPO_URL
, "./foo", &g_options
));
302 git_repository_free(g_repo
); g_repo
= NULL
;
303 cl_fixture_cleanup("./foo");
305 /* User and pass from URL */
306 user_pass
.password
= "wrong";
307 cl_git_pass(git_clone(&g_repo
, BB_REPO_URL_WITH_PASS
, "./foo", &g_options
));
308 git_repository_free(g_repo
); g_repo
= NULL
;
309 cl_fixture_cleanup("./foo");
311 /* Wrong password in URL, fall back to user_pass */
312 user_pass
.password
= "libgit2";
313 cl_git_pass(git_clone(&g_repo
, BB_REPO_URL_WITH_WRONG_PASS
, "./foo", &g_options
));
314 git_repository_free(g_repo
); g_repo
= NULL
;
315 cl_fixture_cleanup("./foo");
318 static int cancel_at_half(const git_transfer_progress
*stats
, void *payload
)
322 if (stats
->received_objects
> (stats
->total_objects
/2))
327 void test_online_clone__can_cancel(void)
329 g_options
.fetch_opts
.callbacks
.transfer_progress
= cancel_at_half
;
332 git_clone(&g_repo
, LIVE_REPO_URL
, "./foo", &g_options
), 4321);
335 static int cred_cb(git_cred
**cred
, const char *url
, const char *user_from_url
,
336 unsigned int allowed_types
, void *payload
)
338 const char *remote_user
= cl_getenv("GITTEST_REMOTE_USER");
339 const char *pubkey
= cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
340 const char *privkey
= cl_getenv("GITTEST_REMOTE_SSH_KEY");
341 const char *passphrase
= cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
343 GIT_UNUSED(url
); GIT_UNUSED(user_from_url
); GIT_UNUSED(payload
);
345 if (allowed_types
& GIT_CREDTYPE_USERNAME
)
346 return git_cred_username_new(cred
, remote_user
);
348 if (allowed_types
& GIT_CREDTYPE_SSH_KEY
)
349 return git_cred_ssh_key_new(cred
, remote_user
, pubkey
, privkey
, passphrase
);
351 giterr_set(GITERR_NET
, "unexpected cred type");
355 static int check_ssh_auth_methods(git_cred
**cred
, const char *url
, const char *username_from_url
,
356 unsigned int allowed_types
, void *data
)
358 int *with_user
= (int *) data
;
359 GIT_UNUSED(cred
); GIT_UNUSED(url
); GIT_UNUSED(username_from_url
); GIT_UNUSED(data
);
362 cl_assert_equal_i(GIT_CREDTYPE_USERNAME
, allowed_types
);
364 cl_assert(!(allowed_types
& GIT_CREDTYPE_USERNAME
));
369 void test_online_clone__ssh_auth_methods(void)
376 g_options
.fetch_opts
.callbacks
.credentials
= check_ssh_auth_methods
;
377 g_options
.fetch_opts
.callbacks
.payload
= &with_user
;
380 cl_git_fail_with(GIT_EUSER
,
381 git_clone(&g_repo
, SSH_REPO_URL
, "./foo", &g_options
));
384 cl_git_fail_with(GIT_EUSER
,
385 git_clone(&g_repo
, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options
));
388 static int custom_remote_ssh_with_paths(
390 git_repository
*repo
,
399 if ((error
= git_remote_create(out
, repo
, name
, url
)) < 0)
405 void test_online_clone__ssh_with_paths(void)
407 char *bad_paths
[] = {
411 char *good_paths
[] = {
412 "/usr/bin/git-upload-pack",
413 "/usr/bin/git-receive-pack",
420 const char *remote_url
= cl_getenv("GITTEST_REMOTE_URL");
421 const char *remote_user
= cl_getenv("GITTEST_REMOTE_USER");
426 if (!remote_url
|| !remote_user
|| strncmp(remote_url
, "ssh://", 5) != 0)
429 g_options
.remote_cb
= custom_remote_ssh_with_paths
;
430 g_options
.fetch_opts
.callbacks
.transport
= git_transport_ssh_with_paths
;
431 g_options
.fetch_opts
.callbacks
.payload
= &arr
;
433 cl_git_fail(git_clone(&g_repo
, remote_url
, "./foo", &g_options
));
435 arr
.strings
= good_paths
;
436 cl_git_pass(git_clone(&g_repo
, remote_url
, "./foo", &g_options
));
439 static int cred_foo_bar(git_cred
**cred
, const char *url
, const char *username_from_url
,
440 unsigned int allowed_types
, void *data
)
443 GIT_UNUSED(url
); GIT_UNUSED(username_from_url
); GIT_UNUSED(allowed_types
); GIT_UNUSED(data
);
445 return git_cred_userpass_plaintext_new(cred
, "foo", "bar");
448 void test_online_clone__ssh_cannot_change_username(void)
453 g_options
.fetch_opts
.callbacks
.credentials
= cred_foo_bar
;
455 cl_git_fail(git_clone(&g_repo
, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options
));
458 int ssh_certificate_check(git_cert
*cert
, int valid
, const char *host
, void *payload
)
460 git_cert_hostkey
*key
;
461 git_oid expected
= {{0}}, actual
= {{0}};
462 const char *expected_str
;
467 expected_str
= cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT");
468 cl_assert(expected_str
);
470 cl_git_pass(git_oid_fromstrp(&expected
, expected_str
));
471 cl_assert_equal_i(GIT_CERT_HOSTKEY_LIBSSH2
, cert
->cert_type
);
472 key
= (git_cert_hostkey
*) cert
;
475 * We need to figure out how long our input was to check for
476 * the type. Here we abuse the fact that both hashes fit into
479 if (strlen(expected_str
) == 32 && key
->type
& GIT_CERT_SSH_MD5
) {
480 memcpy(&actual
.id
, key
->hash_md5
, 16);
481 } else if (strlen(expected_str
) == 40 && key
->type
& GIT_CERT_SSH_SHA1
) {
482 memcpy(&actual
, key
->hash_sha1
, 20);
484 cl_fail("Cannot find a usable SSH hash");
487 cl_assert(!memcmp(&expected
, &actual
, 20));
489 cl_assert_equal_s("localhost", host
);
494 void test_online_clone__ssh_cert(void)
496 g_options
.fetch_opts
.callbacks
.certificate_check
= ssh_certificate_check
;
498 if (!cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT"))
501 cl_git_fail_with(GIT_EUSER
, git_clone(&g_repo
, "ssh://localhost/foo", "./foo", &g_options
));
504 static char *read_key_file(const char *path
)
513 cl_assert((f
= fopen(path
, "r")) != NULL
);
514 cl_assert(fseek(f
, 0, SEEK_END
) != -1);
515 cl_assert((key_length
= ftell(f
)) != -1);
516 cl_assert(fseek(f
, 0, SEEK_SET
) != -1);
517 cl_assert((buf
= malloc(key_length
)) != NULL
);
518 cl_assert(fread(buf
, key_length
, 1, f
) == 1);
524 static int ssh_memory_cred_cb(git_cred
**cred
, const char *url
, const char *user_from_url
,
525 unsigned int allowed_types
, void *payload
)
527 const char *remote_user
= cl_getenv("GITTEST_REMOTE_USER");
528 const char *pubkey_path
= cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
529 const char *privkey_path
= cl_getenv("GITTEST_REMOTE_SSH_KEY");
530 const char *passphrase
= cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
532 GIT_UNUSED(url
); GIT_UNUSED(user_from_url
); GIT_UNUSED(payload
);
534 if (allowed_types
& GIT_CREDTYPE_USERNAME
)
535 return git_cred_username_new(cred
, remote_user
);
537 if (allowed_types
& GIT_CREDTYPE_SSH_KEY
)
539 char *pubkey
= read_key_file(pubkey_path
);
540 char *privkey
= read_key_file(privkey_path
);
542 int ret
= git_cred_ssh_key_memory_new(cred
, remote_user
, pubkey
, privkey
, passphrase
);
551 giterr_set(GITERR_NET
, "unexpected cred type");
555 void test_online_clone__ssh_memory_auth(void)
557 const char *remote_url
= cl_getenv("GITTEST_REMOTE_URL");
558 const char *remote_user
= cl_getenv("GITTEST_REMOTE_USER");
559 const char *privkey
= cl_getenv("GITTEST_REMOTE_SSH_KEY");
561 #ifndef GIT_SSH_MEMORY_CREDENTIALS
564 if (!remote_url
|| !remote_user
|| !privkey
|| strncmp(remote_url
, "ssh://", 5) != 0)
567 g_options
.fetch_opts
.callbacks
.credentials
= ssh_memory_cred_cb
;
569 cl_git_pass(git_clone(&g_repo
, remote_url
, "./foo", &g_options
));
572 void test_online_clone__url_with_no_path_returns_EINVALIDSPEC(void)
574 cl_git_fail_with(git_clone(&g_repo
, "http://github.com", "./foo", &g_options
),
578 static int fail_certificate_check(git_cert
*cert
, int valid
, const char *host
, void *payload
)
585 return GIT_ECERTIFICATE
;
588 void test_online_clone__certificate_invalid(void)
590 g_options
.fetch_opts
.callbacks
.certificate_check
= fail_certificate_check
;
592 cl_git_fail_with(git_clone(&g_repo
, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options
),
596 cl_git_fail_with(git_clone(&g_repo
, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options
),
601 static int succeed_certificate_check(git_cert
*cert
, int valid
, const char *host
, void *payload
)
607 cl_assert_equal_s("github.com", host
);
612 void test_online_clone__certificate_valid(void)
614 g_options
.fetch_opts
.callbacks
.certificate_check
= succeed_certificate_check
;
616 cl_git_pass(git_clone(&g_repo
, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options
));
619 void test_online_clone__start_with_http(void)
621 g_options
.fetch_opts
.callbacks
.certificate_check
= succeed_certificate_check
;
623 cl_git_pass(git_clone(&g_repo
, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options
));