Commit 42e6cf78 by Russell Belfer

Add diff drivers tests (and fix bugs)

This adds real tests for user-configured diff drivers and in the
process found a bunch of bugs.
parent 5dc98298
...@@ -38,6 +38,7 @@ struct git_diff_driver { ...@@ -38,6 +38,7 @@ struct git_diff_driver {
uint32_t other_flags; uint32_t other_flags;
git_array_t(regex_t) fn_patterns; git_array_t(regex_t) fn_patterns;
regex_t word_pattern; regex_t word_pattern;
char name[GIT_FLEX_ARRAY];
}; };
struct git_diff_driver_registry { struct git_diff_driver_registry {
...@@ -132,15 +133,18 @@ static int git_diff_driver_load( ...@@ -132,15 +133,18 @@ static int git_diff_driver_load(
int error = 0, bval; int error = 0, bval;
git_diff_driver_registry *reg; git_diff_driver_registry *reg;
git_diff_driver *drv; git_diff_driver *drv;
size_t namelen = strlen(driver_name);
khiter_t pos;
git_config *cfg; git_config *cfg;
git_buf name = GIT_BUF_INIT; git_buf name = GIT_BUF_INIT;
const char *val; const char *val;
bool found_driver = false;
reg = git_repository_driver_registry(repo); reg = git_repository_driver_registry(repo);
if (!reg) if (!reg)
return -1; return -1;
else { else {
khiter_t pos = git_strmap_lookup_index(reg->drivers, driver_name); pos = git_strmap_lookup_index(reg->drivers, driver_name);
if (git_strmap_valid_index(reg->drivers, pos)) { if (git_strmap_valid_index(reg->drivers, pos)) {
*out = git_strmap_value_at(reg->drivers, pos); *out = git_strmap_value_at(reg->drivers, pos);
return 0; return 0;
...@@ -153,9 +157,10 @@ static int git_diff_driver_load( ...@@ -153,9 +157,10 @@ static int git_diff_driver_load(
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
} }
drv = git__calloc(1, sizeof(git_diff_driver)); drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1);
GITERR_CHECK_ALLOC(drv); GITERR_CHECK_ALLOC(drv);
drv->type = DIFF_DRIVER_AUTO; drv->type = DIFF_DRIVER_AUTO;
memcpy(drv->name, driver_name, namelen);
if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0)
goto fail; goto fail;
...@@ -176,51 +181,62 @@ static int git_diff_driver_load( ...@@ -176,51 +181,62 @@ static int git_diff_driver_load(
/* if diff.<driver>.binary is false, force binary checks off */ /* if diff.<driver>.binary is false, force binary checks off */
/* but still may have custom function context patterns, etc. */ /* but still may have custom function context patterns, etc. */
drv->binary_flags = GIT_DIFF_FORCE_TEXT; drv->binary_flags = GIT_DIFF_FORCE_TEXT;
found_driver = true;
} }
/* TODO: warn if diff.<name>.command or diff.<name>.textconv are set */ /* TODO: warn if diff.<name>.command or diff.<name>.textconv are set */
if ((error = git_buf_printf(&name, "diff.%s.xfuncname", driver_name)) < 0) git_buf_truncate(&name, namelen + strlen("diff.."));
goto fail; git_buf_put(&name, "xfuncname", strlen("xfuncname"));
if ((error = git_config_get_multivar( if ((error = git_config_get_multivar(
cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) { cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) {
if (error != GIT_ENOTFOUND) if (error != GIT_ENOTFOUND)
goto fail; goto fail;
/* no diff.<driver>.xfuncname values, so just continue */ giterr_clear(); /* no diff.<driver>.xfuncname, so just continue */
giterr_clear();
} }
if ((error = git_buf_printf(&name, "diff.%s.funcname", driver_name)) < 0) git_buf_truncate(&name, namelen + strlen("diff.."));
goto fail; git_buf_put(&name, "funcname", strlen("funcname"));
if ((error = git_config_get_multivar( if ((error = git_config_get_multivar(
cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) { cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) {
if (error != GIT_ENOTFOUND) if (error != GIT_ENOTFOUND)
goto fail; goto fail;
/* no diff.<driver>.funcname values, so just continue */ giterr_clear(); /* no diff.<driver>.funcname, so just continue */
giterr_clear();
} }
/* if we found any patterns, set driver type to use correct callback */ /* if we found any patterns, set driver type to use correct callback */
if (git_array_size(drv->fn_patterns) > 0) if (git_array_size(drv->fn_patterns) > 0) {
drv->type = DIFF_DRIVER_PATTERNLIST; drv->type = DIFF_DRIVER_PATTERNLIST;
found_driver = true;
}
if ((error = git_buf_printf(&name, "diff.%s.wordregex", driver_name)) < 0) git_buf_truncate(&name, namelen + strlen("diff.."));
goto fail; git_buf_put(&name, "wordregex", strlen("wordregex"));
if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) {
if (error != GIT_ENOTFOUND) if (error != GIT_ENOTFOUND)
goto fail; goto fail;
/* no diff.<driver>.wordregex, so just continue */ giterr_clear(); /* no diff.<driver>.wordregex, so just continue */
giterr_clear();
} else if ((error = regcomp(&drv->word_pattern, val, REG_EXTENDED)) != 0) { } else if ((error = regcomp(&drv->word_pattern, val, REG_EXTENDED)) != 0) {
/* TODO: warning about bad regex instead of failure */ /* TODO: warning about bad regex instead of failure */
error = giterr_set_regex(&drv->word_pattern, error); error = giterr_set_regex(&drv->word_pattern, error);
goto fail; goto fail;
} else {
found_driver = true;
} }
/* TODO: look up diff.<driver>.algorithm to turn on minimal / patience /* TODO: look up diff.<driver>.algorithm to turn on minimal / patience
* diff in drv->other_flags * diff in drv->other_flags
*/ */
/* if no driver config found, fall back on AUTO driver */
if (!found_driver)
goto fail;
/* store driver in registry */
git_strmap_insert(reg->drivers, drv->name, drv, error);
if (error < 0)
goto fail;
*out = drv; *out = drv;
return 0; return 0;
...@@ -244,18 +260,15 @@ int git_diff_driver_lookup( ...@@ -244,18 +260,15 @@ int git_diff_driver_lookup(
if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0) if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0)
return error; return error;
if (GIT_ATTR_FALSE(value)) { if (GIT_ATTR_UNSPECIFIED(value))
/* just use the auto value */;
else if (GIT_ATTR_FALSE(value))
*out = &global_drivers[DIFF_DRIVER_BINARY]; *out = &global_drivers[DIFF_DRIVER_BINARY];
return 0; else if (GIT_ATTR_TRUE(value))
}
else if (GIT_ATTR_TRUE(value)) {
*out = &global_drivers[DIFF_DRIVER_TEXT]; *out = &global_drivers[DIFF_DRIVER_TEXT];
return 0;
}
/* otherwise look for driver information in config and build driver */ /* otherwise look for driver information in config and build driver */
if ((error = git_diff_driver_load(out, repo, value)) < 0) { else if ((error = git_diff_driver_load(out, repo, value)) < 0) {
if (error != GIT_ENOTFOUND) if (error != GIT_ENOTFOUND)
return error; return error;
else else
...@@ -263,7 +276,9 @@ int git_diff_driver_lookup( ...@@ -263,7 +276,9 @@ int git_diff_driver_lookup(
} }
use_auto: use_auto:
*out = &global_drivers[DIFF_DRIVER_AUTO]; if (!*out)
*out = &global_drivers[DIFF_DRIVER_AUTO];
return 0; return 0;
} }
...@@ -326,7 +341,7 @@ static int diff_context_line__pattern_match( ...@@ -326,7 +341,7 @@ static int diff_context_line__pattern_match(
GIT_UNUSED(line_len); GIT_UNUSED(line_len);
for (i = 0; i > git_array_size(driver->fn_patterns); ++i) { for (i = 0; i < git_array_size(driver->fn_patterns); ++i) {
if (!regexec(git_array_get(driver->fn_patterns, i), line, 0, NULL, 0)) if (!regexec(git_array_get(driver->fn_patterns, i), line, 0, NULL, 0))
return true; return true;
} }
......
#include "clar_libgit2.h"
#include "diff_helpers.h"
#include "repository.h"
#include "diff_driver.h"
static git_repository *g_repo = NULL;
static git_config *g_cfg = NULL;
void test_diff_drivers__initialize(void)
{
}
void test_diff_drivers__cleanup(void)
{
git_config_free(g_cfg);
g_cfg = NULL;
cl_git_sandbox_cleanup();
g_repo = NULL;
}
void test_diff_drivers__patterns(void)
{
const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
git_tree *one;
git_diff_list *diff;
git_diff_patch *patch;
char *text;
const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n";
const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
g_repo = cl_git_sandbox_init("renames");
one = resolve_commit_oid_to_tree(g_repo, one_sha);
/* no diff */
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(0, (int)git_diff_num_deltas(diff));
git_diff_list_free(diff);
/* default diff */
cl_git_append2file("renames/untimely.txt", "\r\nSome new stuff\r\n");
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
cl_git_pass(git_diff_patch_to_str(&text, patch));
cl_assert_equal_s(expected0, text);
git__free(text);
git_diff_patch_free(patch);
git_diff_list_free(diff);
/* attribute diff set to false */
cl_git_rewritefile("renames/.gitattributes", "untimely.txt -diff\n");
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
cl_git_pass(git_diff_patch_to_str(&text, patch));
cl_assert_equal_s(expected1, text);
git__free(text);
git_diff_patch_free(patch);
git_diff_list_free(diff);
/* attribute diff set to unconfigured value (should use default) */
cl_git_rewritefile("renames/.gitattributes", "untimely.txt diff=kipling0\n");
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
cl_git_pass(git_diff_patch_to_str(&text, patch));
cl_assert_equal_s(expected0, text);
git__free(text);
git_diff_patch_free(patch);
git_diff_list_free(diff);
/* let's define that driver */
cl_git_pass(git_repository_config(&g_cfg, g_repo));
cl_git_pass(git_config_set_bool(g_cfg, "diff.kipling0.binary", 1));
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
cl_git_pass(git_diff_patch_to_str(&text, patch));
cl_assert_equal_s(expected1, text);
git__free(text);
git_diff_patch_free(patch);
git_diff_list_free(diff);
/* let's use a real driver with some regular expressions */
git_diff_driver_registry_free(g_repo->diff_drivers);
g_repo->diff_drivers = NULL;
cl_git_pass(git_repository_config(&g_cfg, g_repo));
cl_git_pass(git_config_set_bool(g_cfg, "diff.kipling0.binary", 0));
cl_git_pass(git_config_set_string(g_cfg, "diff.kipling0.xfuncname", "^H"));
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
cl_git_pass(git_diff_patch_to_str(&text, patch));
cl_assert_equal_s(expected2, text);
git__free(text);
git_diff_patch_free(patch);
git_diff_list_free(diff);
git_tree_free(one);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment