Message ID | 20240528125837.31090-1-shyamthakkar001@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [GSoC] t/: migrate helper/test-example-decorate to the unit testing framework | expand |
Ghanshyam Thakkar <shyamthakkar001@gmail.com> writes: > +struct test_vars { > + struct object *one, *two, *three; > + struct decoration n; > + int decoration_a, decoration_b; > +}; > + > +static void t_add(struct test_vars *vars) > +{ > + void *ret = add_decoration(&vars->n, vars->one, &vars->decoration_a); > + > + if (!check(ret == NULL)) > + test_msg("when adding a brand-new object, NULL should be returned"); > + ret = add_decoration(&vars->n, vars->two, NULL); > + if (!check(ret == NULL)) > + test_msg("when adding a brand-new object, NULL should be returned"); > +} > + > +static void t_readd(struct test_vars *vars) > +{ > + void *ret = add_decoration(&vars->n, vars->one, NULL); > + > + if (!check(ret == &vars->decoration_a)) > + test_msg("when readding an already existing object, existing decoration should be returned"); > + ret = add_decoration(&vars->n, vars->two, &vars->decoration_b); > + if (!check(ret == NULL)) > + test_msg("when readding an already existing object, existing decoration should be returned"); > +} > + > +static void t_lookup(struct test_vars *vars) > +{ > + void *ret = lookup_decoration(&vars->n, vars->one); > + > + if (!check(ret == NULL)) > + test_msg("lookup should return added declaration"); > + ret = lookup_decoration(&vars->n, vars->two); > + if (!check(ret == &vars->decoration_b)) > + test_msg("lookup should return added declaration"); > + ret = lookup_decoration(&vars->n, vars->three); > + if (!check(ret == NULL)) > + test_msg("lookup for unknown object should return NULL"); > +} > + > +static void t_loop(struct test_vars *vars) > +{ > + int i, objects_noticed = 0; > + > + for (i = 0; i < vars->n.size; i++) { > + if (vars->n.entries[i].base) > + objects_noticed++; > + } > + if (!check_int(objects_noticed, ==, 2)) > + test_msg("should have 2 objects"); > +} > + > +int cmd_main(int argc UNUSED, const char **argv UNUSED) > +{ > + struct object_id one_oid = { { 1 } }, two_oid = { { 2 } }, three_oid = { { 3 } }; > + struct test_vars vars = { 0 }; > + > + vars.one = lookup_unknown_object(the_repository, &one_oid); > + vars.two = lookup_unknown_object(the_repository, &two_oid); > + vars.three = lookup_unknown_object(the_repository, &three_oid); > + > + TEST(t_add(&vars), > + "Add 2 objects, one with a non-NULL decoration and one with a NULL decoration."); > + TEST(t_readd(&vars), > + "When re-adding an already existing object, the old decoration is returned."); > + TEST(t_lookup(&vars), > + "Lookup returns the added declarations, or NULL if the object was never added."); > + TEST(t_loop(&vars), "The user can also loop through all entries."); These tests as a whole look like a faithful copy of the original done by cmd__example_decorate(). I do not understand the criteria used to split them into the four separate helper functions. It is not like they can be reused or reordered---for example, t_readd() must be done after t_add() has been done. What benefit are you trying to get out of these split? IOW, what are we gaining by having four separate helper functions, instead of testing all of these same things in a single helper function t_all with something like TEST(t_all(&vars), "Do all decorate tests."); in cmd_main()? If there is a concrete benefit of having larger number of smaller tests, would it make the result even better if we split t_add() further into t_add_one() that adds one with deco_a and t_add_two() that adds two with NULL? The other helpers can of course be further split into individual pieces the same way. What ere the criteria used to decide where to stop and use these four? Thanks.
On Wed, May 29, 2024 at 11:41 PM Junio C Hamano <gitster@pobox.com> wrote: > Ghanshyam Thakkar <shyamthakkar001@gmail.com> writes: > > + TEST(t_add(&vars), > > + "Add 2 objects, one with a non-NULL decoration and one with a NULL decoration."); > > + TEST(t_readd(&vars), > > + "When re-adding an already existing object, the old decoration is returned."); > > + TEST(t_lookup(&vars), > > + "Lookup returns the added declarations, or NULL if the object was never added."); > > + TEST(t_loop(&vars), "The user can also loop through all entries."); > > These tests as a whole look like a faithful copy of the original > done by cmd__example_decorate(). > > I do not understand the criteria used to split them into the four > separate helper functions. It is not like they can be reused or > reordered---for example, t_readd() must be done after t_add() has > been done. > > What benefit are you trying to get out of these split? IOW, what > are we gaining by having four separate helper functions, instead of > testing all of these same things in a single helper function t_all > with something like > > TEST(t_all(&vars), "Do all decorate tests."); > > in cmd_main()? If there is a concrete benefit of having larger > number of smaller tests, would it make the result even better if we > split t_add() further into t_add_one() that adds one with deco_a and > t_add_two() that adds two with NULL? The other helpers can of > course be further split into individual pieces the same way. What > ere the criteria used to decide where to stop and use these four? The original code has some kind of "sections" (or paragraphs) separated using comments like: /* * Add 2 objects, one with a non-NULL decoration and one with a NULL * decoration. */ or: /* * When re-adding an already existing object, the old decoration is * returned. */ I think it makes sense to separate the code using functions matching these "sections" and to reuse each comment in the TEST() macro that calls the corresponding function. If this patch is rerolled for some reason, I think it would be a good idea to mention this in the commit message though.
On Thu, 30 May 2024, Christian Couder <christian.couder@gmail.com> wrote: > On Wed, May 29, 2024 at 11:41 PM Junio C Hamano <gitster@pobox.com> wrote: > > Ghanshyam Thakkar <shyamthakkar001@gmail.com> writes: > > > > + TEST(t_add(&vars), > > > + "Add 2 objects, one with a non-NULL decoration and one with a NULL decoration."); > > > + TEST(t_readd(&vars), > > > + "When re-adding an already existing object, the old decoration is returned."); > > > + TEST(t_lookup(&vars), > > > + "Lookup returns the added declarations, or NULL if the object was never added."); > > > + TEST(t_loop(&vars), "The user can also loop through all entries."); > > > > These tests as a whole look like a faithful copy of the original > > done by cmd__example_decorate(). > > > > I do not understand the criteria used to split them into the four > > separate helper functions. It is not like they can be reused or > > reordered---for example, t_readd() must be done after t_add() has > > been done. > > > > What benefit are you trying to get out of these split? IOW, what > > are we gaining by having four separate helper functions, instead of > > testing all of these same things in a single helper function t_all > > with something like > > > > TEST(t_all(&vars), "Do all decorate tests."); > > In addition to what Christian said, doing it all in one function would provide no context as is. i.e. when we do it in a single function, *** unit-tests/bin/t-example-decorate *** # check "objects_noticed == 1" failed at t/unit-tests/t-example-decorate.c:46 # left: 2 # right: 1 # should have 2 objects not ok 1 - All decorate tests 1..1 make[1]: *** [Makefile:78: unit-tests/bin/t-example-decorate] Error 1 vs separated *** unit-tests/bin/t-example-decorate *** ok 1 - Add 2 objects, one with a non-NULL decoration and one with a NULL decoration. ok 2 - When re-adding an already existing object, the old decoration is returned. ok 3 - Lookup returns the added declarations, or NULL if the object was never added. # check "objects_noticed == 1" failed at t/unit-tests/t-example-decorate.c:56 # left: 2 # right: 1 # should have 2 objects not ok 4 - The user can also loop through all entries. 1..4 make[1]: *** [Makefile:78: unit-tests/bin/t-example-decorate] Error 1 The latter provides much more context (we almost don't have to open t-example-decorate.c file itself in some cases to know what failed) than the former. Now, of course we can add more test_msg()s to the former to improve, but I feel that this approach of splitting them provides and improves the information provided on stdout _without_ adding any of my own test_msg()s. And I think that this is a good middleground between cluttering the stdout vs providing very little context while also remaining a faithful copy of the original. > > in cmd_main()? If there is a concrete benefit of having larger > > number of smaller tests, would it make the result even better if we > > split t_add() further into t_add_one() that adds one with deco_a and > > t_add_two() that adds two with NULL? The other helpers can of > > course be further split into individual pieces the same way. What > > ere the criteria used to decide where to stop and use these four? > > The original code has some kind of "sections" (or paragraphs) > separated using comments like: > > /* > * Add 2 objects, one with a non-NULL decoration and one with a NULL > * decoration. > */ > > or: > > /* > * When re-adding an already existing object, the old decoration is > * returned. > */ > > I think it makes sense to separate the code using functions matching > these "sections" and to reuse each comment in the TEST() macro that > calls the corresponding function. If this patch is rerolled for some > reason, I think it would be a good idea to mention this in the commit > message though. I agree about the commit message. Thanks.
Ghanshyam Thakkar <shyamthakkar001@gmail.com> writes: > The latter provides much more context (we almost don't have to open > t-example-decorate.c file itself in some cases to know what failed) > than the former. Now, of course we can add more test_msg()s to the > former to improve, but I feel that this approach of splitting them > provides and improves the information provided on stdout _without_ > adding any of my own test_msg()s. And I think that this is a good > middleground between cluttering the stdout vs providing very little > context while also remaining a faithful copy of the original. If so, why stop at having four, each of which has more than one step that could further be split? What's the downside? Note: Here in this review, I am not necessarily suggesting the tests in this patch to be further split into greater number of smaller helper functions. I am primarily interested in finding out what the unit test framework can further do to help unit tests written using it (i.e., like this patch). If using finer-grained tests gives you better diagnosis, but if it is too cumbersome to separate the tests out further, is it because the framework is inadequate in some way? How can we improve it? Thanks.
On Thu, 30 May 2024, Junio C Hamano <gitster@pobox.com> wrote: > Ghanshyam Thakkar <shyamthakkar001@gmail.com> writes: > > > The latter provides much more context (we almost don't have to open > > t-example-decorate.c file itself in some cases to know what failed) > > than the former. Now, of course we can add more test_msg()s to the > > former to improve, but I feel that this approach of splitting them > > provides and improves the information provided on stdout _without_ > > adding any of my own test_msg()s. And I think that this is a good > > middleground between cluttering the stdout vs providing very little > > context while also remaining a faithful copy of the original. > > If so, why stop at having four, each of which has more than one step > that could further be split? What's the downside? > > Note: Here in this review, I am not necessarily suggesting the > tests in this patch to be further split into greater number of > smaller helper functions. I am primarily interested in finding > out what the unit test framework can further do to help unit > tests written using it (i.e., like this patch). If using > finer-grained tests gives you better diagnosis, but if it is too > cumbersome to separate the tests out further, is it because the > framework is inadequate in some way? How can we improve it? It's not that the framework is inadequate in its current state (for this test). As Christian said, in the original test-example-decorate.c, the tests were divided into four sections by a space and comments like: /* * Add 2 objects, one with a non-NULL decoration and one with a NULL * decoration. */ So, I also made those four sections in the form of those functions and the comments became the test description. I definitely don't see any downside in further dividing where it makes sense. For example, the first test can be split into two, one which adds an object with non-NULL decoration and one with NULL (I think you mentioned this). And the third test can split to test lookup for a known object vs an unknown object. Besides these I don't see where we can split. Thanks.
On 2024.05.30 08:54, Junio C Hamano wrote: > Ghanshyam Thakkar <shyamthakkar001@gmail.com> writes: > > > The latter provides much more context (we almost don't have to open > > t-example-decorate.c file itself in some cases to know what failed) > > than the former. Now, of course we can add more test_msg()s to the > > former to improve, but I feel that this approach of splitting them > > provides and improves the information provided on stdout _without_ > > adding any of my own test_msg()s. And I think that this is a good > > middleground between cluttering the stdout vs providing very little > > context while also remaining a faithful copy of the original. > > If so, why stop at having four, each of which has more than one step > that could further be split? What's the downside? > > Note: Here in this review, I am not necessarily suggesting the > tests in this patch to be further split into greater number of > smaller helper functions. I am primarily interested in finding > out what the unit test framework can further do to help unit > tests written using it (i.e., like this patch). If using > finer-grained tests gives you better diagnosis, but if it is too > cumbersome to separate the tests out further, is it because the > framework is inadequate in some way? How can we improve it? I'll try not to speak for anyone else here, but I think the test framework isn't causing much friction here in the decision of how to split the tests. [However, neither is it providing much guidance. At some point we should review the unit tests and see if we can extract a helpful style guide or best practices doc.] The setup for the cases is minimal and done through the main function. I think the current split is reasonable as a first patch, as it mirrors the organization of the original test and makes it easier for reviewers to verify that it tests the same behaviors. If further simplification or reorganization is needed, I would like to see that as a separate patch on top of the more straightforward conversion. The only part that bothers me a bit (and this is really more of a complaint about the framework than the patch itself) is the carryover of state between the different TEST() cases. We can't skip t_add and expect the other test cases to still pass, unfortunately. However, I don't think this patch needs to worry about that, since the framework doesn't restrict persistent state. [And we certainly don't restrict persistent state in the shell tests either.] > Thanks. >
On Mon, 03 Jun 2024, Josh Steadmon <steadmon@google.com> wrote: > On 2024.05.30 08:54, Junio C Hamano wrote: > > Ghanshyam Thakkar <shyamthakkar001@gmail.com> writes: > > > > > The latter provides much more context (we almost don't have to open > > > t-example-decorate.c file itself in some cases to know what failed) > > > than the former. Now, of course we can add more test_msg()s to the > > > former to improve, but I feel that this approach of splitting them > > > provides and improves the information provided on stdout _without_ > > > adding any of my own test_msg()s. And I think that this is a good > > > middleground between cluttering the stdout vs providing very little > > > context while also remaining a faithful copy of the original. > > > > If so, why stop at having four, each of which has more than one step > > that could further be split? What's the downside? > > > > Note: Here in this review, I am not necessarily suggesting the > > tests in this patch to be further split into greater number of > > smaller helper functions. I am primarily interested in finding > > out what the unit test framework can further do to help unit > > tests written using it (i.e., like this patch). If using > > finer-grained tests gives you better diagnosis, but if it is too > > cumbersome to separate the tests out further, is it because the > > framework is inadequate in some way? How can we improve it? > > I'll try not to speak for anyone else here, but I think the test > framework isn't causing much friction here in the decision of how to > split the tests. [However, neither is it providing much guidance. At > some point we should review the unit tests and see if we can extract a > helpful style guide or best practices doc.] The setup for the cases is > minimal and done through the main function. Agreed about style guide/best practices doc. > I think the current split is reasonable as a first patch, as it mirrors > the organization of the original test and makes it easier for reviewers > to verify that it tests the same behaviors. If further simplification or > reorganization is needed, I would like to see that as a separate patch > on top of the more straightforward conversion. > > The only part that bothers me a bit (and this is really more of a > complaint about the framework than the patch itself) is the carryover of > state between the different TEST() cases. We can't skip t_add and expect > the other test cases to still pass, unfortunately. However, I don't > think this patch needs to worry about that, since the framework doesn't > restrict persistent state. [And we certainly don't restrict persistent > state in the shell tests either.] I talked about this in private with Christian, and we came to the same conclusion that having independent state would better. But seeing the original test-example-decorate, it would be a bit more boiler plate to produce the exact same checks, without relying on previous state. And seeing the lack of convention (written guideline) about independent state vs dependent, I decided to stick to having the tests rely on previous state, similar to the original, and see the mailing list response about what should be done. Thanks.
diff --git a/Makefile b/Makefile index 8f4432ae57..43663fe528 100644 --- a/Makefile +++ b/Makefile @@ -793,7 +793,6 @@ TEST_BUILTINS_OBJS += test-dump-fsmonitor.o TEST_BUILTINS_OBJS += test-dump-split-index.o TEST_BUILTINS_OBJS += test-dump-untracked-cache.o TEST_BUILTINS_OBJS += test-env-helper.o -TEST_BUILTINS_OBJS += test-example-decorate.o TEST_BUILTINS_OBJS += test-example-tap.o TEST_BUILTINS_OBJS += test-find-pack.o TEST_BUILTINS_OBJS += test-fsmonitor-client.o @@ -1335,6 +1334,7 @@ THIRD_PARTY_SOURCES += sha1collisiondetection/% THIRD_PARTY_SOURCES += sha1dc/% UNIT_TEST_PROGRAMS += t-ctype +UNIT_TEST_PROGRAMS += t-example-decorate UNIT_TEST_PROGRAMS += t-mem-pool UNIT_TEST_PROGRAMS += t-prio-queue UNIT_TEST_PROGRAMS += t-strbuf diff --git a/decorate.h b/decorate.h index cdeb17c9df..08af658d34 100644 --- a/decorate.h +++ b/decorate.h @@ -3,7 +3,7 @@ /* * A data structure that associates Git objects to void pointers. See - * t/helper/test-example-decorate.c for a demonstration of how to use these + * t/unit-tests/t-example-decorate.c for a demonstration of how to use these * functions. */ diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c deleted file mode 100644 index 8f59f6be4c..0000000000 --- a/t/helper/test-example-decorate.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "test-tool.h" -#include "git-compat-util.h" -#include "object.h" -#include "decorate.h" -#include "repository.h" - -int cmd__example_decorate(int argc UNUSED, const char **argv UNUSED) -{ - struct decoration n; - struct object_id one_oid = { {1} }; - struct object_id two_oid = { {2} }; - struct object_id three_oid = { {3} }; - struct object *one, *two, *three; - - int decoration_a, decoration_b; - - void *ret; - - int i, objects_noticed = 0; - - /* - * The struct must be zero-initialized. - */ - memset(&n, 0, sizeof(n)); - - /* - * Add 2 objects, one with a non-NULL decoration and one with a NULL - * decoration. - */ - one = lookup_unknown_object(the_repository, &one_oid); - two = lookup_unknown_object(the_repository, &two_oid); - ret = add_decoration(&n, one, &decoration_a); - if (ret) - BUG("when adding a brand-new object, NULL should be returned"); - ret = add_decoration(&n, two, NULL); - if (ret) - BUG("when adding a brand-new object, NULL should be returned"); - - /* - * When re-adding an already existing object, the old decoration is - * returned. - */ - ret = add_decoration(&n, one, NULL); - if (ret != &decoration_a) - BUG("when readding an already existing object, existing decoration should be returned"); - ret = add_decoration(&n, two, &decoration_b); - if (ret) - BUG("when readding an already existing object, existing decoration should be returned"); - - /* - * Lookup returns the added declarations, or NULL if the object was - * never added. - */ - ret = lookup_decoration(&n, one); - if (ret) - BUG("lookup should return added declaration"); - ret = lookup_decoration(&n, two); - if (ret != &decoration_b) - BUG("lookup should return added declaration"); - three = lookup_unknown_object(the_repository, &three_oid); - ret = lookup_decoration(&n, three); - if (ret) - BUG("lookup for unknown object should return NULL"); - - /* - * The user can also loop through all entries. - */ - for (i = 0; i < n.size; i++) { - if (n.entries[i].base) - objects_noticed++; - } - if (objects_noticed != 2) - BUG("should have 2 objects"); - - clear_decoration(&n, NULL); - - return 0; -} diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index f6fd0fe491..2d82515f56 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -29,7 +29,6 @@ static struct test_cmd cmds[] = { { "dump-split-index", cmd__dump_split_index }, { "dump-untracked-cache", cmd__dump_untracked_cache }, { "env-helper", cmd__env_helper }, - { "example-decorate", cmd__example_decorate }, { "example-tap", cmd__example_tap }, { "find-pack", cmd__find_pack }, { "fsmonitor-client", cmd__fsmonitor_client }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 868f33453c..bc334183c3 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -23,7 +23,6 @@ int cmd__dump_split_index(int argc, const char **argv); int cmd__dump_untracked_cache(int argc, const char **argv); int cmd__dump_reftable(int argc, const char **argv); int cmd__env_helper(int argc, const char **argv); -int cmd__example_decorate(int argc, const char **argv); int cmd__example_tap(int argc, const char **argv); int cmd__find_pack(int argc, const char **argv); int cmd__fsmonitor_client(int argc, const char **argv); diff --git a/t/t9004-example.sh b/t/t9004-example.sh deleted file mode 100755 index 590aab0304..0000000000 --- a/t/t9004-example.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -test_description='check that example code compiles and runs' - -TEST_PASSES_SANITIZE_LEAK=true -. ./test-lib.sh - -test_expect_success 'decorate' ' - test-tool example-decorate -' - -test_done diff --git a/t/unit-tests/t-example-decorate.c b/t/unit-tests/t-example-decorate.c new file mode 100644 index 0000000000..3c856a8cf2 --- /dev/null +++ b/t/unit-tests/t-example-decorate.c @@ -0,0 +1,80 @@ +#include "test-lib.h" +#include "object.h" +#include "decorate.h" +#include "repository.h" + +struct test_vars { + struct object *one, *two, *three; + struct decoration n; + int decoration_a, decoration_b; +}; + +static void t_add(struct test_vars *vars) +{ + void *ret = add_decoration(&vars->n, vars->one, &vars->decoration_a); + + if (!check(ret == NULL)) + test_msg("when adding a brand-new object, NULL should be returned"); + ret = add_decoration(&vars->n, vars->two, NULL); + if (!check(ret == NULL)) + test_msg("when adding a brand-new object, NULL should be returned"); +} + +static void t_readd(struct test_vars *vars) +{ + void *ret = add_decoration(&vars->n, vars->one, NULL); + + if (!check(ret == &vars->decoration_a)) + test_msg("when readding an already existing object, existing decoration should be returned"); + ret = add_decoration(&vars->n, vars->two, &vars->decoration_b); + if (!check(ret == NULL)) + test_msg("when readding an already existing object, existing decoration should be returned"); +} + +static void t_lookup(struct test_vars *vars) +{ + void *ret = lookup_decoration(&vars->n, vars->one); + + if (!check(ret == NULL)) + test_msg("lookup should return added declaration"); + ret = lookup_decoration(&vars->n, vars->two); + if (!check(ret == &vars->decoration_b)) + test_msg("lookup should return added declaration"); + ret = lookup_decoration(&vars->n, vars->three); + if (!check(ret == NULL)) + test_msg("lookup for unknown object should return NULL"); +} + +static void t_loop(struct test_vars *vars) +{ + int i, objects_noticed = 0; + + for (i = 0; i < vars->n.size; i++) { + if (vars->n.entries[i].base) + objects_noticed++; + } + if (!check_int(objects_noticed, ==, 2)) + test_msg("should have 2 objects"); +} + +int cmd_main(int argc UNUSED, const char **argv UNUSED) +{ + struct object_id one_oid = { { 1 } }, two_oid = { { 2 } }, three_oid = { { 3 } }; + struct test_vars vars = { 0 }; + + vars.one = lookup_unknown_object(the_repository, &one_oid); + vars.two = lookup_unknown_object(the_repository, &two_oid); + vars.three = lookup_unknown_object(the_repository, &three_oid); + + TEST(t_add(&vars), + "Add 2 objects, one with a non-NULL decoration and one with a NULL decoration."); + TEST(t_readd(&vars), + "When re-adding an already existing object, the old decoration is returned."); + TEST(t_lookup(&vars), + "Lookup returns the added declarations, or NULL if the object was never added."); + TEST(t_loop(&vars), "The user can also loop through all entries."); + + clear_decoration(&vars.n, NULL); + + return test_done(); +}
helper/test-example-decorate.c along with t9004-example.sh provide an example of how to use the functions in decorate.h (which provides a data structure that associates Git objects to void pointers) and also test their output. Migrate them to the new unit testing framework for better debugging and runtime performance. Mentored-by: Christian Couder <chriscool@tuxfamily.org> Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com> Signed-off-by: Ghanshyam Thakkar <shyamthakkar001@gmail.com> --- Makefile | 2 +- decorate.h | 2 +- t/helper/test-example-decorate.c | 78 ------------------------------ t/helper/test-tool.c | 1 - t/helper/test-tool.h | 1 - t/t9004-example.sh | 12 ----- t/unit-tests/t-example-decorate.c | 80 +++++++++++++++++++++++++++++++ 7 files changed, 82 insertions(+), 94 deletions(-) delete mode 100644 t/helper/test-example-decorate.c delete mode 100755 t/t9004-example.sh create mode 100644 t/unit-tests/t-example-decorate.c