diff --git a/test/s3comms.c b/test/s3comms.c index 8efbc584b39..fe7c8c6f8df 100644 --- a/test/s3comms.c +++ b/test/s3comms.c @@ -22,327 +22,23 @@ #ifdef H5_HAVE_ROS3_VFD -/***************************************************************************** - * - * FILE-LOCAL TESTING MACROS - * - * Purpose: - * - * 1) Upon test failure, goto-jump to single-location teardown in test - * function. E.g., `error:` (consistency with HDF corpus) or - * `failed:` (reflects purpose). - * >>> using "error", in part because `H5E_BEGIN_TRY` expects it. - * 2) Increase clarity and reduce overhead found with `TEST_ERROR`. - * e.g., "if(somefunction(arg, arg2) < 0) TEST_ERROR:" - * requires reading of entire line to know whether this if/call is - * part of the test setup, test operation, or a test unto itself. - * 3) Provide testing macros with optional user-supplied failure message; - * if not supplied (NULL), generate comparison output in the spirit of - * test-driven development. E.g., "expected 5 but was -3" - * User messages clarify test's purpose in code, encouraging description - * without relying on comments. - * 4) Configurable expected-actual order in generated comparison strings. - * Some prefer `VERIFY(expected, actual)`, others - * `VERIFY(actual, expected)`. Provide preprocessor ifdef switch - * to satisfy both parties, assuming one paradigm per test file. - * (One could #undef and redefine the flag through the file as desired, - * but _why_.) - * Provided as courtesy, per consideration for inclusion in the library - * proper. - * - * Macros: - * - * JSVERIFY_EXP_ACT - ifdef flag, configures comparison order - * FAIL_IF() - check condition - * FAIL_UNLESS() - check _not_ condition - * JSVERIFY() - long-int equality check; prints reason/comparison - * JSVERIFY_NOT() - long-int inequality check; prints - * JSVERIFY_STR() - string equality check; prints - * - *****************************************************************************/ - -/*---------------------------------------------------------------------------- - * - * ifdef flag: JSVERIFY_EXP_ACT - * - * JSVERIFY macros accept arguments as (EXPECTED, ACTUAL[, reason]) - * default, if this is undefined, is (ACTUAL, EXPECTED[, reason]) - * - *---------------------------------------------------------------------------- - */ -#define JSVERIFY_EXP_ACT 1L - -/*---------------------------------------------------------------------------- - * - * Macro: JSFAILED_AT() - * - * Purpose: - * - * Preface a test failure by printing "*FAILED*" and location to stdout - * Similar to `H5_FAILED(); AT();` from h5test.h - * - * *FAILED* at somefile.c:12 in function_name()... - * - *---------------------------------------------------------------------------- - */ -#define JSFAILED_AT() \ - { \ - printf("*FAILED* at %s:%d in %s()...\n", __FILE__, __LINE__, __func__); \ - } - -/*---------------------------------------------------------------------------- - * - * Macro: FAIL_IF() - * - * Purpose: - * - * Make tests more accessible and less cluttered than - * `if (thing == otherthing()) TEST_ERROR` - * paradigm. - * - * The following lines are roughly equivalent: - * - * `if (myfunc() < 0) TEST_ERROR;` (as seen elsewhere in HDF tests) - * `FAIL_IF(myfunc() < 0)` - * - * Prints a generic "FAILED AT" line to stdout and jumps to `error`, - * similar to `TEST_ERROR` in h5test.h - * - *---------------------------------------------------------------------------- - */ -#define FAIL_IF(condition) \ - if (condition) { \ - JSFAILED_AT() \ - goto error; \ - } - -/*---------------------------------------------------------------------------- - * - * Macro: FAIL_UNLESS() - * - * Purpose: - * - * TEST_ERROR wrapper to reduce cognitive overhead from "negative tests", - * e.g., "a != b". - * - * Opposite of FAIL_IF; fails if the given condition is _not_ true. - * - * `FAIL_IF( 5 != my_op() )` - * is equivalent to - * `FAIL_UNLESS( 5 == my_op() )` - * However, `JSVERIFY(5, my_op(), "bad return")` may be even clearer. - * (see JSVERIFY) - * - *---------------------------------------------------------------------------- - */ -#define FAIL_UNLESS(condition) \ - if (!(condition)) { \ - JSFAILED_AT() \ - goto error; \ - } - -/*---------------------------------------------------------------------------- - * - * Macro: JSERR_LONG() - * - * Purpose: - * - * Print an failure message for long-int arguments. - * ERROR-AT printed first. - * If `reason` is given, it is printed on own line and newlined after - * else, prints "expected/actual" aligned on own lines. - * - * *FAILED* at myfile.c:488 in somefunc()... - * forest must be made of trees. - * - * or - * - * *FAILED* at myfile.c:488 in somefunc()... - * ! Expected 425 - * ! Actual 3 - * - *---------------------------------------------------------------------------- - */ -static inline void -jserr_long(long expected, long actual, const char *reason) -{ - if (reason != NULL) { - printf("%s\n", reason); - } - else { - printf(" ! Expected %ld\n ! Actual %ld\n", expected, actual); - } -} - -#define JSERR_LONG(expected, actual, reason) \ - { \ - JSFAILED_AT() \ - jserr_long((long)(expected), (long)(actual), reason); \ - } - -/*---------------------------------------------------------------------------- - * - * Macro: JSERR_STR() - * - * Purpose: - * - * Print an failure message for string arguments. - * ERROR-AT printed first. - * If `reason` is given, it is printed on own line and newlined after - * else, prints "expected/actual" aligned on own lines. - * - * *FAILED* at myfile.c:421 in myfunc()... - * Blue and Red strings don't match! - * - * or - * - * *FAILED* at myfile.c:421 in myfunc()... - * !!! Expected: - * this is my expected - * string - * !!! Actual: - * not what I expected at all - * - *---------------------------------------------------------------------------- - */ -static inline void -jserr_str(const char *expected, const char *actual, const char *reason) -{ - if (reason != NULL) { - printf("%s\n", reason); - } - else { - printf("!!! Expected:\n%s\n!!!Actual:\n%s\n", expected, actual); - } -} - -#define JSERR_STR(expected, actual, reason) \ - { \ - JSFAILED_AT() \ - jserr_str((expected), (actual), (reason)); \ - } - -#ifdef JSVERIFY_EXP_ACT -/* VERIFY routines with parameter order (, [, ]) - */ - -/*---------------------------------------------------------------------------- - * - * Macro: JSVERIFY() - * - * Purpose: - * - * Verify that two long integers are equal. - * If unequal, print failure message - * (with `reason`, if not NULL; expected/actual if NULL) - * and jump to `error` at end of function - * - *---------------------------------------------------------------------------- - */ -#define JSVERIFY(expected, actual, reason) \ - if ((long)(actual) != (long)(expected)) { \ - JSERR_LONG((expected), (actual), (reason)) \ - goto error; \ - } /* JSVERIFY */ - -#if 0 /* UNUSED */ - -/*---------------------------------------------------------------------------- - * - * Macro: JSVERIFY_NOT() - * - * Purpose: - * - * Verify that two long integers are _not_ equal. - * If equal, print failure message - * (with `reason`, if not NULL; expected/actual if NULL) - * and jump to `error` at end of function - * - *---------------------------------------------------------------------------- - */ -#define JSVERIFY_NOT(expected, actual, reason) \ - if ((long)(actual) == (long)(expected)) { \ - JSERR_LONG((expected), (actual), (reason)) \ - goto error; \ - } /* JSVERIFY_NOT */ -#endif /* JSVERIFY_NOT unused */ - -/*---------------------------------------------------------------------------- - * - * Macro: JSVERIFY_STR() - * - * Purpose: - * - * Verify that two strings are equal. - * If unequal, print failure message - * (with `reason`, if not NULL; expected/actual if NULL) - * and jump to `error` at end of function - * - *---------------------------------------------------------------------------- - */ -#define JSVERIFY_STR(expected, actual, reason) \ - if (strcmp((actual), (expected)) != 0) { \ - JSERR_STR((expected), (actual), (reason)); \ - goto error; \ - } /* JSVERIFY_STR */ - -#else -/* JSVERIFY_EXP_ACT not defined - * - * Repeats macros above, but with actual/expected parameters reversed. - */ - -/*---------------------------------------------------------------------------- - * Macro: JSVERIFY() - * See: JSVERIFY documentation above. - *---------------------------------------------------------------------------- - */ -#define JSVERIFY(actual, expected, reason) \ - if ((long)(actual) != (long)(expected)) { \ - JSERR_LONG((expected), (actual), (reason)); \ - goto error; \ - } /* JSVERIFY */ - -#if 0 /* UNUSED */ - -/*---------------------------------------------------------------------------- - * Macro: JSVERIFY_NOT() - * See: JSVERIFY_NOT documentation above. - *---------------------------------------------------------------------------- - */ -#define JSVERIFY_NOT(actual, expected, reason) \ - if ((long)(actual) == (long)(expected)) { \ - JSERR_LONG((expected), (actual), (reason)) \ - goto error; \ - } /* JSVERIFY_NOT */ -#endif /* JSVERIFY_NOT unused */ - -/*---------------------------------------------------------------------------- - * Macro: JSVERIFY_STR() - * See: JSVERIFY_STR documentation above. - *---------------------------------------------------------------------------- - */ -#define JSVERIFY_STR(actual, expected, reason) \ - if (strcmp((actual), (expected)) != 0) { \ - JSERR_STR((expected), (actual), (reason)); \ - goto error; \ - } /* JSVERIFY_STR */ - -#endif /* ifdef/else JSVERIFY_EXP_ACT */ - #define S3_TEST_PROFILE_NAME "ros3_vfd_test" #define S3_TEST_RESOURCE_TEXT_RESTRICTED "t8.shakespeare.txt" #define S3_TEST_RESOURCE_TEXT_PUBLIC "Poe_Raven.txt" #define S3_TEST_RESOURCE_MISSING "missing.csv" -#define S3_TEST_RUN_TIMEOUT 0 /* run tests that might hang */ -#define S3_TEST_MAX_URL_SIZE 256 /* char array size */ +/* URL max size */ +#define S3_TEST_MAX_URL_SIZE 256 + +/* Read buffer max size */ +#define S3COMMS_READ_BUFFER_SIZE 256 -/* Global variables for aws test profile. +/* Global variables for AWS test profile. + * * An attempt is made to read ~/.aws/credentials and ~/.aws/config upon test * startup -- if unable to open either file or cannot load region, id, and key, - * tests connecting with S3 will not be run + * tests connecting with S3 will not be run. */ static int s3_test_credentials_loaded = 0; static char s3_test_aws_region[16] = ""; @@ -353,23 +49,18 @@ static char s3_test_bucket_url[S3_TEST_MAX_URL_SIZE] = ""; static bool s3_test_bucket_defined = false; /*--------------------------------------------------------------------------- + * Function: test_macro_format_credential * - * Function: test_macro_format_credential() - * - * Purpose: - * - * Demonstrate that the macro `S3COMMS_FORMAT_CREDENTIAL` - * performs as expected. + * Purpose: Demonstrate that the macro `S3COMMS_FORMAT_CREDENTIAL` + * performs as expected. * + * Return: PASS : 0 + * FAIL : 1 *---------------------------------------------------------------------------- */ -static herr_t +static int test_macro_format_credential(void) { - /************************ - * test-local variables * - ************************/ - char dest[256]; const char access[] = "AKIAIOSFODNN7EXAMPLE"; const char date[] = "20130524"; @@ -379,39 +70,36 @@ test_macro_format_credential(void) TESTING("test_macro_format_credential"); - FAIL_IF(S3COMMS_MAX_CREDENTIAL_SIZE < S3COMMS_FORMAT_CREDENTIAL(dest, access, date, region, service)) + if (S3COMMS_MAX_CREDENTIAL_SIZE < S3COMMS_FORMAT_CREDENTIAL(dest, access, date, region, service)) + FAIL_PUTS_ERROR("bad s3comms credential size"); - JSVERIFY_STR(expected, dest, NULL) + if (strncmp(expected, dest, 256)) + FAIL_PUTS_ERROR("incorrect credential string"); PASSED(); return 0; error: - return -1; - + return 1; } /* end test_macro_format_credential() */ /*--------------------------------------------------------------------------- + * Function: test_aws_canonical_request * - * Function: test_aws_canonical_request() - * - * Purpose: - * - * Demonstrate the construction of a Canonical Request (and Signed Headers) + * Purpose: Demonstrate the construction of a Canonical Request (and + * Signed Headers) * * Elided / not yet implemented: * Query strings * request "body" * + * Return: PASS : 0 + * FAIL : 1 *--------------------------------------------------------------------------- */ -static herr_t +static int test_aws_canonical_request(void) { - /************************* - * test-local structures * - *************************/ - struct header { const char *name; const char *value; @@ -422,14 +110,10 @@ test_aws_canonical_request(void) const char *exp_headers; const char *verb; const char *resource; - unsigned int listsize; + int listsize; struct header list[5]; }; - /************************ - * test-local variables * - ************************/ - struct testcase cases[] = { { "GET\n/some/" @@ -469,77 +153,85 @@ test_aws_canonical_request(void) }, /* unused; satisfies compiler */ }, }; /* struct testcase cases[] */ + struct testcase *C = NULL; - char cr_dest[512]; /* canonical request */ - hrb_t *hrb = NULL; /* http request buffer object */ - unsigned int i = 0; /* looping/indexing */ - unsigned int j = 0; /* looping/indexing */ - hrb_node_t *node = NULL; /* http headers list pointer */ - unsigned int n_cases = 3; - char sh_dest[64]; /* signed headers */ + char cr_dest[512]; /* Canonical request */ + hrb_t *hrb = NULL; /* http request buffer object */ + hrb_node_t *node = NULL; /* http headers list pointer */ + const int NCASES = 3; + char sh_dest[64]; /* Signed headers */ + herr_t ret; TESTING("test_aws_canonical_request"); - for (i = 0; i < n_cases; i++) { - /* pre-test bookkeeping - */ + for (int i = 0; i < NCASES; i++) { C = &cases[i]; - for (j = 0; j < 256; j++) { + + for (int j = 0; j < 256; j++) { cr_dest[j] = 0; - } /* zero request buffer */ - for (j = 0; j < 64; j++) { + } + + for (int j = 0; j < 64; j++) { sh_dest[j] = 0; - } /* zero headers buffer */ + } - /* create HTTP request object with given verb, resource/path - */ + /* Create HTTP request object with given verb, resource/path */ hrb = H5FD_s3comms_hrb_init_request(C->verb, C->resource, "HTTP/1.1"); assert(hrb->body == NULL); - /* Create headers list from test case input - */ - for (j = 0; j < C->listsize; j++) { - FAIL_IF(FAIL == H5FD_s3comms_hrb_node_set(&node, C->list[j].name, C->list[j].value)); + /* Create headers list from test case input */ + for (int j = 0; j < C->listsize; j++) { + if (H5FD_s3comms_hrb_node_set(&node, C->list[j].name, C->list[j].value) < 0) + TEST_ERROR; } hrb->first_header = node; - /* test - */ - JSVERIFY(SUCCEED, H5FD_s3comms_aws_canonical_request(cr_dest, 512, sh_dest, 64, hrb), - " unable to compose canonical request") - JSVERIFY_STR(C->exp_headers, sh_dest, NULL) - JSVERIFY_STR(C->exp_request, cr_dest, NULL) + /* Test */ + if (H5FD_s3comms_aws_canonical_request(cr_dest, 512, sh_dest, 64, hrb) < 0) + TEST_ERROR; + if (strncmp(C->exp_headers, sh_dest, 512)) + FAIL_PUTS_ERROR("header string mismatch"); + if (strncmp(C->exp_request, cr_dest, 512)) + FAIL_PUTS_ERROR("request string mismatch"); - /* tear-down - */ + /* Tear-down */ while (node != NULL) { - FAIL_IF(FAIL == H5FD_s3comms_hrb_node_set(&node, node->name, NULL)); + if (H5FD_s3comms_hrb_node_set(&node, node->name, NULL) < 0) + TEST_ERROR; } - assert(NULL == node); - FAIL_IF(FAIL == H5FD_s3comms_hrb_destroy(&hrb)); - assert(NULL == hrb); - - } /* for each test case */ - - /*************** - * ERROR CASES * - ***************/ + if (H5FD_s3comms_hrb_destroy(&hrb) < 0) + TEST_ERROR; + } - /* malformed hrb and/or node-list - */ - JSVERIFY(FAIL, H5FD_s3comms_aws_canonical_request(cr_dest, 20, sh_dest, 20, NULL), - "http request object cannot be null") + /* ERROR CASES - Malformed hrb and/or node-list */ + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_aws_canonical_request(cr_dest, 20, sh_dest, 20, NULL); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("http request object cannot be null"); hrb = H5FD_s3comms_hrb_init_request("GET", "/", "HTTP/1.1"); - JSVERIFY(FAIL, H5FD_s3comms_aws_canonical_request(NULL, 20, sh_dest, 20, hrb), - "canonical request destination cannot be NULL") + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_aws_canonical_request(NULL, 20, sh_dest, 20, hrb); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("canonical request destination cannot be NULL"); - JSVERIFY(FAIL, H5FD_s3comms_aws_canonical_request(cr_dest, 20, NULL, 20, hrb), - "signed headers destination cannot be null") + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_aws_canonical_request(cr_dest, 20, NULL, 20, hrb); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("signed headers destination cannot be null"); - FAIL_IF(FAIL == H5FD_s3comms_hrb_destroy(&hrb)) - assert(NULL == hrb); + if (H5FD_s3comms_hrb_destroy(&hrb) < 0) + TEST_ERROR; PASSED(); return 0; @@ -548,39 +240,28 @@ test_aws_canonical_request(void) if (node != NULL) { while (node != NULL) - (void)H5FD_s3comms_hrb_node_set(&node, node->name, NULL); - assert(node == NULL); + H5FD_s3comms_hrb_node_set(&node, node->name, NULL); } if (hrb != NULL) { - (void)H5FD_s3comms_hrb_destroy(&hrb); + H5FD_s3comms_hrb_destroy(&hrb); } - return -1; + return 1; } /* end test_aws_canonical_request() */ /*--------------------------------------------------------------------------- + * Function: test_bytes_to_hex * - * Function: test_bytes_to_hex - * - * Purpose: - * - * Define and verify behavior of `H5FD_s3comms_bytes_to_hex()`. - * - * Return: - * - * Success: 0 - * Failure: -1 + * Purpose: Define and verify behavior of `H5FD_s3comms_bytes_to_hex()`. * + * Return: PASS : 0 + * FAIL : 1 *--------------------------------------------------------------------------- */ -static herr_t +static int test_bytes_to_hex(void) { - /************************* - * test-local structures * - *************************/ - struct testcase { const char exp[17]; /* in size * 2 + 1 for null terminator */ const unsigned char in[8]; @@ -588,10 +269,6 @@ test_bytes_to_hex(void) bool lower; }; - /************************ - * test-local variables * - ************************/ - struct testcase cases[] = { { "52F3000C9A", @@ -609,57 +286,52 @@ test_bytes_to_hex(void) "", {17, 63, 26, 56}, 0, false, /* irrelevant */ }, }; - int i = 0; - int n_cases = 3; - char out[17]; - int out_off = 0; + const int NCASES = 3; + char out[17]; + herr_t ret; TESTING("bytes-to-hex"); - for (i = 0; i < n_cases; i++) { - for (out_off = 0; out_off < 17; out_off++) { + for (int i = 0; i < NCASES; i++) { + for (int out_off = 0; out_off < 17; out_off++) { out[out_off] = 0; } - JSVERIFY(SUCCEED, H5FD_s3comms_bytes_to_hex(out, cases[i].in, cases[i].size, cases[i].lower), NULL) + if (H5FD_s3comms_bytes_to_hex(out, cases[i].in, cases[i].size, cases[i].lower) < 0) + TEST_ERROR; - JSVERIFY_STR(cases[i].exp, out, NULL) + if (strncmp(cases[i].exp, out, 17)) + FAIL_PUTS_ERROR("incorrect bytes to hex conversion"); } - /* dest cannot be null - */ - JSVERIFY(FAIL, H5FD_s3comms_bytes_to_hex(NULL, (const unsigned char *)"nada", 5, false), - "destination cannot be null") + /* dest cannot be null */ + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_bytes_to_hex(NULL, (const unsigned char *)"nada", 5, false); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("dest parameter cannot be null"); PASSED(); return 0; error: - return -1; - + return 1; } /* end test_bytes_to_hex() */ /*--------------------------------------------------------------------------- + * Function: test_hrb_init_request * - * Function: test_hrb_init_request() - * - * Purpose: - * - * Define and verify behavior of `H5FD_s3comms_hrb_init_request()` + * Purpose: Define and verify behavior of `H5FD_s3comms_hrb_init_request()` * + * Return: PASS : 0 + * FAIL : 1 *--------------------------------------------------------------------------- */ -static herr_t +static int test_hrb_init_request(void) { - /********************* - * test-local macros * - *********************/ - - /************************* - * test-local structures * - *************************/ - struct testcase { const char msg[64]; const char *verb; @@ -669,10 +341,6 @@ test_hrb_init_request(void) bool ret_null; }; - /************************ - * test-local variables * - ************************/ - struct testcase cases[] = { { "get HTTP request just as we provided", @@ -715,67 +383,70 @@ test_hrb_init_request(void) true, }, }; - struct testcase *C = NULL; - unsigned int i = 0; - unsigned int ncases = 5; - hrb_t *req = NULL; + const int NCASES = 5; + hrb_t *req = NULL; TESTING("hrb_init_request"); - for (i = 0; i < ncases; i++) { - C = &cases[i]; + for (int i = 0; i < NCASES; i++) { + struct testcase *C = &cases[i]; + req = H5FD_s3comms_hrb_init_request(C->verb, C->resource, C->version); + if (cases[i].ret_null == true) { - FAIL_IF(req != NULL); + if (req != NULL) + TEST_ERROR; } else { - FAIL_IF(req == NULL); + if (req == NULL) + TEST_ERROR; if (C->verb == NULL) { - JSVERIFY_STR("GET", req->verb, NULL) + if (strcmp("GET", req->verb)) + TEST_ERROR; } else { - JSVERIFY_STR(req->verb, C->verb, NULL) + if (strcmp(req->verb, C->verb)) + TEST_ERROR; } - JSVERIFY_STR("HTTP/1.1", req->version, NULL) - JSVERIFY_STR(C->exp_res, req->resource, NULL) - FAIL_IF(req->first_header != NULL); - FAIL_IF(req->body != NULL); - JSVERIFY(0, req->body_len, NULL) - JSVERIFY(SUCCEED, H5FD_s3comms_hrb_destroy(&req), "unable to destroy hrb_t") - FAIL_IF(NULL != req); /* should annull pointer as well as free */ + if (strcmp("HTTP/1.1", req->version)) + TEST_ERROR; + if (strcmp(C->exp_res, req->resource)) + TEST_ERROR; + if (req->first_header != NULL) + TEST_ERROR; + if (req->body != NULL) + TEST_ERROR; + if (0 != req->body_len) + TEST_ERROR; + if (H5FD_s3comms_hrb_destroy(&req) < 0) + FAIL_PUTS_ERROR("unable to destroy hrb_t"); + /* Should annull pointer as well as free */ + if (NULL != req) + TEST_ERROR; } - - } /* end for each testcase */ + } PASSED(); return 0; error: - (void)H5FD_s3comms_hrb_destroy(&req); - - return -1; - + H5FD_s3comms_hrb_destroy(&req); + return 1; } /* end test_hrb_init_request() */ /*--------------------------------------------------------------------------- + * Function: test_hrb_node_set * - * Function: test_hrb_node_set() - * - * Purpose: - * - * Test operations on hrb_node_t structure + * Purpose: Test operations on hrb_node_t structure * + * Return: PASS : 0 + * FAIL : 1 *--------------------------------------------------------------------------- */ -static herr_t +static int test_hrb_node_set(void) { - /************************* - * test-local structures * - *************************/ - - /* bundle of name/value representing an hrb_node_t - */ + /* Bundle of name/value representing an hrb_node_t */ typedef struct node_mock_t { const char *name; const char *value; @@ -808,10 +479,6 @@ test_hrb_node_set(void) const char *expected[11]; } testcase; - /************************ - * test-local variables * - ************************/ - testcase cases[] = { { "cannot remove node from null list", @@ -1076,92 +743,84 @@ test_hrb_node_set(void) }, }, }; - unsigned testcases_count = 16; - unsigned test_i = 0; - hrb_node_t *list = NULL; + const int NCASES = 16; + hrb_node_t *list = NULL; TESTING("hrb_node_t (test_hrb_node_set)"); - for (test_i = 0; test_i < testcases_count; test_i++) { + for (int i = 0; i < NCASES; i++) { const hrb_node_t *node = NULL; - const testcase *test = &(cases[test_i]); + const testcase *test = &(cases[i]); unsigned mock_i = 0; - /********* - * SETUP * - *********/ + /* SETUP */ for (mock_i = 0; test->given[mock_i] != NULL; mock_i += 2) { const char *name = test->given[mock_i]; const char *value = test->given[mock_i + 1]; - FAIL_IF(SUCCEED != H5FD_s3comms_hrb_node_set(&list, name, value)) + if (H5FD_s3comms_hrb_node_set(&list, name, value) < 0) + TEST_ERROR; } - /******** - * TEST * - ********/ - /* perform modification on list - */ - JSVERIFY(test->returned, H5FD_s3comms_hrb_node_set(&list, test->delta.name, test->delta.value), - test->message) + /* TEST */ + + /* Modify list */ + if (test->returned != H5FD_s3comms_hrb_node_set(&list, test->delta.name, test->delta.value)) + FAIL_PUTS_ERROR(test->message); - /* verify resulting list - */ + /* Verify resulting list */ node = list; mock_i = 0; while (test->expected[mock_i] != NULL && node != NULL) { const char *name = test->expected[mock_i]; const char *value = test->expected[mock_i + 1]; - JSVERIFY_STR(name, node->name, NULL) - JSVERIFY_STR(value, node->value, NULL) + if (strcmp(name, node->name)) + TEST_ERROR; + if (strcmp(value, node->value)) + TEST_ERROR; mock_i += 2; node = node->next; } - FAIL_IF(test->expected[mock_i] != NULL) - FAIL_IF(node != NULL) + if (test->expected[mock_i] != NULL) + TEST_ERROR; + if (node != NULL) + TEST_ERROR; - /************ - * TEARDOWN * - ************/ + /* TEARDOWN */ while (list != NULL) { - FAIL_IF(SUCCEED != H5FD_s3comms_hrb_node_set(&list, list->name, NULL)) + if (H5FD_s3comms_hrb_node_set(&list, list->name, NULL) < 0) + TEST_ERROR; } - } /* end for each testcase */ + } PASSED(); return 0; error: while (list != NULL) { - (void)H5FD_s3comms_hrb_node_set(&list, list->name, NULL); + H5FD_s3comms_hrb_node_set(&list, list->name, NULL); } - return -1; + return 1; -} /* end test_hrb_node_t() */ +} /* end test_hrb_node_set() */ /*--------------------------------------------------------------------------- + * Function: test_HMAC_SHA256 * - * Function: test_HMAC_SHA256() - * - * Purpose: - * - * Define and verify behavior of `H5FD_s3comms_HMAC_SHA256()` + * Purpose: Define and verify behavior of `H5FD_s3comms_HMAC_SHA256()` * + * Return: PASS : 0 + * FAIL : 1 *--------------------------------------------------------------------------- */ -static herr_t +static int test_HMAC_SHA256(void) { - - /************************* - * test-local structures * - *************************/ - struct testcase { herr_t ret; /* SUCCEED/FAIL expected from call */ const unsigned char key[SHA256_DIGEST_LENGTH]; @@ -1172,10 +831,6 @@ test_HMAC_SHA256(void) size_t dest_size; /* if 0, `dest` is not malloc'd */ }; - /************************ - * test-local variables * - ************************/ - struct testcase cases[] = { { SUCCEED, @@ -1206,41 +861,28 @@ test_HMAC_SHA256(void) 0, /* dest -> null, resulting in immediate error */ }, }; - char *dest = NULL; - int i = 0; - int n_cases = 3; + char *dest = NULL; + const int NCASES = 3; TESTING("HMAC_SHA256"); - for (i = 0; i < n_cases; i++) { + for (int i = 0; i < NCASES; i++) { + if (cases[i].dest_size == 0) { dest = NULL; } else { - dest = (char *)malloc(sizeof(char) * cases[i].dest_size); - assert(dest != NULL); + if (NULL == (dest = (char *)malloc(sizeof(char) * cases[i].dest_size))) + TEST_ERROR; } - JSVERIFY( - cases[i].ret, - H5FD_s3comms_HMAC_SHA256(cases[i].key, cases[i].key_len, cases[i].msg, cases[i].msg_len, dest), - cases[i].msg); + if (cases[i].ret != + H5FD_s3comms_HMAC_SHA256(cases[i].key, cases[i].key_len, cases[i].msg, cases[i].msg_len, dest)) + TEST_ERROR; + if (cases[i].ret == SUCCEED) { -#ifdef VERBOSE - if (0 != strncmp(cases[i].exp, dest, strlen(cases[i].exp))) { - /* print out how wrong things are, and then fail - */ - dest = (char *)realloc(dest, cases[i].dest_size + 1); - assert(dest != NULL); - dest[cases[i].dest_size] = 0; - fprintf(stdout, "ERROR:\n!!! \"%s\"\n != \"%s\"\n", cases[i].exp, dest); + if (strncmp(cases[i].exp, dest, strlen(cases[i].exp))) TEST_ERROR; - } -#else /* VERBOSE not defined */ - /* simple pass/fail test - */ - JSVERIFY(0, strncmp(cases[i].exp, dest, strlen(cases[i].exp)), NULL); -#endif /* VERBOSE */ } free(dest); } @@ -1250,27 +892,21 @@ test_HMAC_SHA256(void) error: free(dest); - return -1; + return 1; } /* end test_HMAC_SHA256() */ /*--------------------------------------------------------------------------- + * Function: test_parse_url * - * Function: test_parse_url() * + * Return: PASS : 0 + * FAIL : 1 *--------------------------------------------------------------------------- */ -static herr_t +static int test_parse_url(void) { - /********************* - * test-local macros * - *********************/ - - /************************* - * test-local structures * - *************************/ - typedef struct { const char *scheme; const char *host; @@ -1280,20 +916,14 @@ test_parse_url(void) } const_purl_t; struct testcase { - const char *url; - herr_t exp_ret; /* expected return; */ - /* if FAIL, `expected` is unused */ - const_purl_t expected; + const char *url; + herr_t exp_ret; /* expected return */ + const_purl_t expected; /* unused if exp_ret is FAIL */ const char *msg; }; - /************************ - * test-local variables * - ************************/ - parsed_url_t *purl = NULL; - unsigned int i = 0; - unsigned int ncases = 15; + const int NCASES = 15; struct testcase cases[] = { { NULL, @@ -1471,100 +1101,277 @@ test_parse_url(void) * TESTS * *********/ - for (i = 0; i < ncases; i++) { - assert(purl == NULL); + for (int i = 0; i < NCASES; i++) { - JSVERIFY(cases[i].exp_ret, H5FD_s3comms_parse_url(cases[i].url, &purl), cases[i].msg) + if (cases[i].exp_ret != H5FD_s3comms_parse_url(cases[i].url, &purl)) + TEST_ERROR; if (cases[i].exp_ret == FAIL) { - /* on FAIL, `purl` should be untouched--remains NULL */ - FAIL_UNLESS(purl == NULL) + /* On FAIL, `purl` should be untouched--remains NULL */ + if (purl != NULL) + TEST_ERROR; } else { - /* on SUCCEED, `purl` should be set */ - FAIL_IF(purl == NULL) + /* On SUCCEED, `purl` should be set */ + if (purl == NULL) + TEST_ERROR; if (cases[i].expected.scheme != NULL) { - FAIL_IF(NULL == purl->scheme) - JSVERIFY_STR(cases[i].expected.scheme, purl->scheme, cases[i].msg) + if (NULL == purl->scheme) + TEST_ERROR; + if (strcmp(cases[i].expected.scheme, purl->scheme)) + TEST_ERROR; } else { - FAIL_UNLESS(NULL == purl->scheme) + if (NULL != purl->scheme) + TEST_ERROR; } if (cases[i].expected.host != NULL) { - FAIL_IF(NULL == purl->host) - JSVERIFY_STR(cases[i].expected.host, purl->host, cases[i].msg) + if (NULL == purl->host) + TEST_ERROR; + if (strcmp(cases[i].expected.host, purl->host)) + TEST_ERROR; } else { - FAIL_UNLESS(NULL == purl->host) + if (NULL != purl->host) + TEST_ERROR; } if (cases[i].expected.port != NULL) { - FAIL_IF(NULL == purl->port) - JSVERIFY_STR(cases[i].expected.port, purl->port, cases[i].msg) + if (NULL == purl->port) + TEST_ERROR; + if (strcmp(cases[i].expected.port, purl->port)) + TEST_ERROR; } else { - FAIL_UNLESS(NULL == purl->port) + if (NULL != purl->port) + TEST_ERROR; } if (cases[i].expected.path != NULL) { - FAIL_IF(NULL == purl->path) - JSVERIFY_STR(cases[i].expected.path, purl->path, cases[i].msg) + if (NULL == purl->path) + TEST_ERROR; + if (strcmp(cases[i].expected.path, purl->path)) + TEST_ERROR; } else { - FAIL_UNLESS(NULL == purl->path) + if (NULL != purl->path) + TEST_ERROR; } if (cases[i].expected.query != NULL) { - FAIL_IF(NULL == purl->query) - JSVERIFY_STR(cases[i].expected.query, purl->query, cases[i].msg) + if (NULL == purl->query) + TEST_ERROR; + if (strcmp(cases[i].expected.query, purl->query)) + TEST_ERROR; } else { - FAIL_UNLESS(NULL == purl->query) + if (NULL != purl->query) + TEST_ERROR; } - } /* end if parse-url return SUCCEED/FAIL */ + } - /* per-test cleanup - * well-behaved, even if `purl` is NULL - */ - FAIL_IF(FAIL == H5FD_s3comms_free_purl(purl)) - purl = NULL; + if (H5FD_s3comms_free_purl(purl) < 0) + TEST_ERROR; - } /* end for each testcase */ + purl = NULL; + } PASSED(); return 0; error: - /*********** - * cleanup * - ***********/ - (void)H5FD_s3comms_free_purl(purl); + H5FD_s3comms_free_purl(purl); - return -1; + return 1; } /* end test_parse_url() */ /*--------------------------------------------------------------------------- - * Function: test_s3r_get_filesize() + * Function: test_signing_key + * + * Purpose: Verify behavior of `H5FD_s3comms_signing_key()` + * + * Return: PASS : 0 + * FAIL : 1 *--------------------------------------------------------------------------- */ -static herr_t -test_s3r_get_filesize(void) +static int +test_signing_key(void) { + struct testcase { + const char *region; + const char *secret_key; + const char *when; + unsigned char exp[SHA256_DIGEST_LENGTH]; + }; - /************************ - * test-local variables * - ************************/ + struct testcase cases[] = { + { + "us-east-1", + "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + "20130524T000000Z", + { + 0xdb, 0xb8, 0x93, 0xac, 0xc0, 0x10, 0x96, 0x49, 0x18, 0xf1, 0xfd, + 0x43, 0x3a, 0xdd, 0x87, 0xc7, 0x0e, 0x8b, 0x0d, 0xb6, 0xbe, 0x30, + 0xc1, 0xfb, 0xea, 0xfe, 0xfa, 0x5e, 0xc6, 0xba, 0x83, 0x78, + }, + }, + }; + + unsigned char *key = NULL; + const int NCASES = 1; + herr_t ret; + + TESTING("signing_key"); + + for (int i = 0; i < NCASES; i++) { + if (NULL == (key = (unsigned char *)malloc(sizeof(unsigned char) * SHA256_DIGEST_LENGTH))) + TEST_ERROR; + if (H5FD_s3comms_signing_key(key, cases[i].secret_key, cases[i].region, cases[i].when) < 0) + TEST_ERROR; + + if (strncmp((const char *)cases[i].exp, (const char *)key, SHA256_DIGEST_LENGTH)) + TEST_ERROR; + + free(key); + key = NULL; + } + + /* ERROR CASES */ + + if (NULL == (key = (unsigned char *)malloc(sizeof(unsigned char) * SHA256_DIGEST_LENGTH))) + TEST_ERROR; + + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_signing_key(NULL, cases[0].secret_key, cases[0].region, cases[0].when); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("destination cannot be NULL"); + + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_signing_key(key, NULL, cases[0].region, cases[0].when); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("secret key cannot be NULL"); + + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_signing_key(key, cases[0].secret_key, NULL, cases[0].when); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("aws region cannot be NULL"); + + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_signing_key(key, cases[0].secret_key, cases[0].region, NULL); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("time string cannot be NULL"); + + free(key); + + PASSED(); + return 0; + +error: + free(key); + return 1; +} /* end test_signing_key() */ + +/*--------------------------------------------------------------------------- + * Function: test_tostringtosign() + * + * Purpose: Verify that we can get the "string to sign" from a Canonical + * Request and related information. + * + * Return: PASS : 0 + * FAIL : 1 + *--------------------------------------------------------------------------- + */ +static int +test_tostringtosign(void) +{ + const char canonreq[] = "GET\n/" + "test.txt\n\nhost:examplebucket.s3.amazonaws.com\nrange:bytes=0-9\nx-amz-content-" + "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\nx-amz-" + "date:20130524T000000Z\n\nhost;range;x-amz-content-sha256;x-amz-" + "date\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + const char iso8601now[] = "20130524T000000Z"; + const char region[] = "us-east-1"; + char s2s[512]; + herr_t ret; + + TESTING("s3comms tostringtosign"); + + if (H5FD_s3comms_tostringtosign(s2s, canonreq, iso8601now, region) < 0) + FAIL_PUTS_ERROR("unable to create string to sign"); + + if (strncmp("AWS4-HMAC-SHA256\n20130524T000000Z\n20130524/us-east-1/s3/" + "aws4_request\n7344ae5b7ee6c3e7e6b0fe0640412a37625d1fbfff95c48bbb2dc43964946972", + s2s, 512)) + TEST_ERROR; + + /* ERROR CASES */ + + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_tostringtosign(s2s, NULL, iso8601now, region); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("canonical request string cannot be NULL"); + + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_tostringtosign(s2s, canonreq, NULL, region); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("time string cannot be NULL"); + + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_tostringtosign(s2s, canonreq, iso8601now, NULL); + } + H5E_END_TRY + if (ret == SUCCEED) + FAIL_PUTS_ERROR("aws region cannot be NULL"); + + PASSED(); + return 0; + +error: + return 1; + +} /* end test_tostringtosign() */ + +/*--------------------------------------------------------------------------- + * Function: test_s3r_get_filesize + * + * Purpose: Test H5FD_s3comms_s3r_get_filesize() + * + * Return: PASS : 0 + * FAIL : 1 + *--------------------------------------------------------------------------- + */ +static int +test_s3r_get_filesize(void) +{ char url_raven[S3_TEST_MAX_URL_SIZE]; s3r_t *handle = NULL; TESTING("s3r_get_filesize"); - /* setup -- compose url to target resource - */ + /* Setup -- compose url to target resource */ if (false == s3_test_bucket_defined) { SKIPPED(); puts(" environment variable HDF5_ROS3_TEST_BUCKET_URL not defined"); @@ -1572,52 +1379,51 @@ test_s3r_get_filesize(void) return 0; } - FAIL_IF(S3_TEST_MAX_URL_SIZE < snprintf(url_raven, S3_TEST_MAX_URL_SIZE, "%s/%s", s3_test_bucket_url, - S3_TEST_RESOURCE_TEXT_PUBLIC)); + if (S3_TEST_MAX_URL_SIZE < + snprintf(url_raven, S3_TEST_MAX_URL_SIZE, "%s/%s", s3_test_bucket_url, S3_TEST_RESOURCE_TEXT_PUBLIC)) + TEST_ERROR; - JSVERIFY(0, H5FD_s3comms_s3r_get_filesize(NULL), "filesize of the null handle should be 0") + if (0 != H5FD_s3comms_s3r_get_filesize(NULL)) + FAIL_PUTS_ERROR("filesize of the null handle should be 0"); - handle = H5FD_s3comms_s3r_open(url_raven, NULL, NULL, NULL, NULL); - FAIL_IF(handle == NULL) + if (NULL == (handle = H5FD_s3comms_s3r_open(url_raven, NULL, NULL, NULL, NULL))) + TEST_ERROR; - JSVERIFY(6464, H5FD_s3comms_s3r_get_filesize(handle), NULL) + if (6464 != H5FD_s3comms_s3r_get_filesize(handle)) + FAIL_PUTS_ERROR("incorrect file size - fragile, make sure the file size didn't change"); - FAIL_IF(SUCCEED != H5FD_s3comms_s3r_close(handle)) + if (H5FD_s3comms_s3r_close(handle) < 0) + TEST_ERROR; PASSED(); return 0; error: if (handle != NULL) - (void)H5FD_s3comms_s3r_close(handle); - - return -1; - + H5FD_s3comms_s3r_close(handle); + return 1; } /* end test_s3r_get_filesize() */ /*--------------------------------------------------------------------------- - * Function: test_s3r_open() + * Function: test_s3r_open + * + * Purpose: Test H5FD_s3comms_s3r_open() * + * Return: PASS : 0 + * FAIL : 1 *--------------------------------------------------------------------------- */ -static herr_t +static int test_s3r_open(void) { - - /************************ - * test-local variables * - ************************/ - char url_missing[S3_TEST_MAX_URL_SIZE]; char url_raven[S3_TEST_MAX_URL_SIZE]; - char url_raven_badport[S3_TEST_MAX_URL_SIZE]; char url_shakespeare[S3_TEST_MAX_URL_SIZE]; unsigned char signing_key[SHA256_DIGEST_LENGTH]; struct tm *now = NULL; char iso8601now[ISO8601_SIZE]; - s3r_t *handle = NULL; - bool curl_ready = false; - parsed_url_t *purl = NULL; + s3r_t *handle = NULL; + parsed_url_t *purl = NULL; TESTING("s3r_open"); @@ -1638,191 +1444,190 @@ test_s3r_open(void) * PRE-TEST SETUP * ******************/ - FAIL_IF(S3_TEST_MAX_URL_SIZE < snprintf(url_shakespeare, S3_TEST_MAX_URL_SIZE, "%s/%s", - s3_test_bucket_url, S3_TEST_RESOURCE_TEXT_RESTRICTED)); + if (S3_TEST_MAX_URL_SIZE < snprintf(url_shakespeare, S3_TEST_MAX_URL_SIZE, "%s/%s", s3_test_bucket_url, + S3_TEST_RESOURCE_TEXT_RESTRICTED)) + TEST_ERROR; - FAIL_IF(S3_TEST_MAX_URL_SIZE < snprintf(url_missing, S3_TEST_MAX_URL_SIZE, "%s/%s", s3_test_bucket_url, - S3_TEST_RESOURCE_MISSING)); + if (S3_TEST_MAX_URL_SIZE < + snprintf(url_missing, S3_TEST_MAX_URL_SIZE, "%s/%s", s3_test_bucket_url, S3_TEST_RESOURCE_MISSING)) + TEST_ERROR; - FAIL_IF(S3_TEST_MAX_URL_SIZE < snprintf(url_raven, S3_TEST_MAX_URL_SIZE, "%s/%s", s3_test_bucket_url, - S3_TEST_RESOURCE_TEXT_PUBLIC)); + if (S3_TEST_MAX_URL_SIZE < + snprintf(url_raven, S3_TEST_MAX_URL_SIZE, "%s/%s", s3_test_bucket_url, S3_TEST_RESOURCE_TEXT_PUBLIC)) + TEST_ERROR; /* Set given bucket url with invalid/inactive port number for badport. * Note, this sort of micro-management of parsed_url_t is not advised */ - FAIL_IF(FAIL == H5FD_s3comms_parse_url(s3_test_bucket_url, &purl)) + if (H5FD_s3comms_parse_url(s3_test_bucket_url, &purl) < 0) + TEST_ERROR; + if (purl->port == NULL) { - purl->port = (char *)H5MM_malloc(sizeof(char) * 5); - FAIL_IF(purl->port == NULL); - FAIL_IF(5 < snprintf(purl->port, 5, "9000")) + if (NULL == (purl->port = (char *)H5MM_malloc(sizeof(char) * 5))) + TEST_ERROR; + if (5 < snprintf(purl->port, 5, "9000")) + TEST_ERROR; } else if (strcmp(purl->port, "9000") != 0) { - FAIL_IF(5 < snprintf(purl->port, 5, "9000")) + if (5 < snprintf(purl->port, 5, "9000")) + TEST_ERROR; } else { - FAIL_IF(5 < snprintf(purl->port, 5, "1234")) + if (5 < snprintf(purl->port, 5, "1234")) + TEST_ERROR; } - FAIL_IF(S3_TEST_MAX_URL_SIZE < snprintf(url_raven_badport, S3_TEST_MAX_URL_SIZE, "%s://%s:%s/%s", - purl->scheme, purl->host, purl->port, - S3_TEST_RESOURCE_TEXT_PUBLIC)); - - curl_global_init(CURL_GLOBAL_DEFAULT); - curl_ready = true; - now = gmnow(); - FAIL_IF(now == NULL) - FAIL_IF(ISO8601NOW(iso8601now, now) != (ISO8601_SIZE - 1)); + if (NULL == (now = gmnow())) + TEST_ERROR; + if (ISO8601NOW(iso8601now, now) != (ISO8601_SIZE - 1)) + TEST_ERROR; /* It is desired to have means available to verify that signing_key * was set successfully and to an expected value. */ - FAIL_IF(FAIL == H5FD_s3comms_signing_key(signing_key, (const char *)s3_test_aws_secret_access_key, - (const char *)s3_test_aws_region, (const char *)iso8601now)); + if (H5FD_s3comms_signing_key(signing_key, (const char *)s3_test_aws_secret_access_key, + (const char *)s3_test_aws_region, (const char *)iso8601now) < 0) + TEST_ERROR; /************************* * OPEN NONEXISTENT FILE * *************************/ - /* attempt anonymously - */ - handle = H5FD_s3comms_s3r_open(url_missing, NULL, NULL, NULL, NULL); - FAIL_IF(handle != NULL); - - /* attempt with authentication - */ - handle = H5FD_s3comms_s3r_open( - url_missing, (const char *)s3_test_aws_region, (const char *)s3_test_aws_access_key_id, - (const unsigned char *)signing_key, (const char *)s3_test_aws_security_token); - FAIL_IF(handle != NULL); - - /************************* - * INACTIVE PORT ON HOST * - *************************/ - -#if S3_TEST_RUN_TIMEOUT - printf("Opening on inactive port may hang for a minute; waiting for timeout\n"); - handle = H5FD_s3comms_s3r_open(url_raven_badport, NULL, NULL, NULL, NULL); - FAIL_IF(handle != NULL); -#endif + /* Attempt anonymously */ + H5E_BEGIN_TRY + { + handle = H5FD_s3comms_s3r_open(url_missing, NULL, NULL, NULL, NULL); + } + H5E_END_TRY + if (handle != NULL) + TEST_ERROR; + + /* Attempt with authentication */ + H5E_BEGIN_TRY + { + handle = H5FD_s3comms_s3r_open( + url_missing, (const char *)s3_test_aws_region, (const char *)s3_test_aws_access_key_id, + (const unsigned char *)signing_key, (const char *)s3_test_aws_security_token); + } + H5E_END_TRY + if (handle != NULL) + TEST_ERROR; /******************************* * INVALID AUTHENTICATION INFO * *******************************/ - /* anonymous access on restricted file - */ - handle = H5FD_s3comms_s3r_open(url_shakespeare, NULL, NULL, NULL, NULL); - FAIL_IF(handle != NULL); - - /* passed in a bad ID - */ - handle = - H5FD_s3comms_s3r_open(url_shakespeare, (const char *)s3_test_aws_region, "I_MADE_UP_MY_ID", - (const unsigned char *)signing_key, (const char *)s3_test_aws_security_token); - FAIL_IF(handle != NULL); - - /* using an invalid signing key - */ - handle = H5FD_s3comms_s3r_open( - url_shakespeare, (const char *)s3_test_aws_region, (const char *)s3_test_aws_access_key_id, - (const unsigned char *)EMPTY_SHA256, (const char *)s3_test_aws_security_token); - FAIL_IF(handle != NULL); + /* Anonymous access on restricted file */ + H5E_BEGIN_TRY + { + handle = H5FD_s3comms_s3r_open(url_shakespeare, NULL, NULL, NULL, NULL); + } + H5E_END_TRY + if (handle != NULL) + TEST_ERROR; + + /* Pass in a bad ID */ + H5E_BEGIN_TRY + { + handle = H5FD_s3comms_s3r_open(url_shakespeare, (const char *)s3_test_aws_region, "I_MADE_UP_MY_ID", + (const unsigned char *)signing_key, + (const char *)s3_test_aws_security_token); + } + H5E_END_TRY + if (handle != NULL) + TEST_ERROR; + + /* Using an invalid signing key */ + H5E_BEGIN_TRY + { + handle = H5FD_s3comms_s3r_open( + url_shakespeare, (const char *)s3_test_aws_region, (const char *)s3_test_aws_access_key_id, + (const unsigned char *)EMPTY_SHA256, (const char *)s3_test_aws_security_token); + } + H5E_END_TRY + if (handle != NULL) + TEST_ERROR; /******************************* * SUCCESSFUL OPEN (AND CLOSE) * *******************************/ - /* anonymous - */ + /* Anonymous */ handle = H5FD_s3comms_s3r_open(url_raven, NULL, NULL, NULL, NULL); - FAIL_IF(handle == NULL); - JSVERIFY(6464, H5FD_s3comms_s3r_get_filesize(handle), "did not get expected filesize") - JSVERIFY(SUCCEED, H5FD_s3comms_s3r_close(handle), "unable to close file") + if (handle == NULL) + TEST_ERROR; + if (6464 != H5FD_s3comms_s3r_get_filesize(handle)) + FAIL_PUTS_ERROR("did not get expected filesize"); + if (H5FD_s3comms_s3r_close(handle) < 0) + TEST_ERROR; handle = NULL; - /* using authentication on anonymously-accessible file? - */ + /* Using authentication on anonymously-accessible file? */ handle = H5FD_s3comms_s3r_open( url_raven, (const char *)s3_test_aws_region, (const char *)s3_test_aws_access_key_id, (const unsigned char *)signing_key, (const char *)s3_test_aws_security_token); - FAIL_IF(handle == NULL); - JSVERIFY(6464, H5FD_s3comms_s3r_get_filesize(handle), NULL) - JSVERIFY(SUCCEED, H5FD_s3comms_s3r_close(handle), "unable to close file") + if (handle == NULL) + TEST_ERROR; + if (6464 != H5FD_s3comms_s3r_get_filesize(handle)) + FAIL_PUTS_ERROR("did not get expected filesize"); + if (H5FD_s3comms_s3r_close(handle)) + TEST_ERROR; handle = NULL; - /* authenticating - */ + /* Authenticating */ handle = H5FD_s3comms_s3r_open( url_shakespeare, (const char *)s3_test_aws_region, (const char *)s3_test_aws_access_key_id, (const unsigned char *)signing_key, (const char *)s3_test_aws_security_token); - FAIL_IF(handle == NULL); - JSVERIFY(5458199, H5FD_s3comms_s3r_get_filesize(handle), NULL) - JSVERIFY(SUCCEED, H5FD_s3comms_s3r_close(handle), "unable to close file") + if (handle == NULL) + TEST_ERROR; + if (5458199 != H5FD_s3comms_s3r_get_filesize(handle)) + FAIL_PUTS_ERROR("did not get expected filesize"); + if (H5FD_s3comms_s3r_close(handle) < 0) + TEST_ERROR; handle = NULL; - curl_global_cleanup(); - curl_ready = false; - - FAIL_IF(FAIL == H5FD_s3comms_free_purl(purl)) - purl = NULL; + if (H5FD_s3comms_free_purl(purl) < 0) + TEST_ERROR; PASSED(); return 0; error: - /*********** - * cleanup * - ***********/ - if (handle != NULL) H5FD_s3comms_s3r_close(handle); if (purl != NULL) H5FD_s3comms_free_purl(purl); - if (curl_ready == true) - curl_global_cleanup(); - - return -1; + return 1; } /* end test_s3r_open() */ /*--------------------------------------------------------------------------- + * Function: test_s3r_read * - * Function: test_s3r_read() - * - * Purpose: - * - * Specify and demonstrate the use and life cycle of an S3 Request handle - * `s3r_t`, through its related functions. + * Purpose: Specify and demonstrate the use and life cycle of an S3 + * request handle `s3r_t`, through its related functions. * * H5FD_s3comms_s3r_open * H5FD_s3comms_s3r_getsize << called by open() _only_ * H5FD_s3comms_s3r_read << called by getsize(), multiple times working * H5FD_s3comms_s3r_close * - * Shows most basic curl iteration. + * Shows most basic curl iteration * + * Return: PASS : 0 + * FAIL : 1 *--------------------------------------------------------------------------- */ -static herr_t +static int test_s3r_read(void) { - -#define S3COMMS_TEST_BUFFER_SIZE 256 - - /************************ - * test-local variables * - ************************/ - - char url_raven[S3_TEST_MAX_URL_SIZE]; - char buffer[S3COMMS_TEST_BUFFER_SIZE]; - s3r_t *handle = NULL; - bool curl_ready = false; - unsigned int i = 0; + char url_raven[S3_TEST_MAX_URL_SIZE]; + char buffer[S3COMMS_READ_BUFFER_SIZE]; + s3r_t *handle = NULL; + herr_t ret; TESTING("test_s3r_read"); - /* - * initial setup - */ + /* Initial setup */ if (false == s3_test_bucket_defined) { SKIPPED(); puts(" environment variable HDF5_ROS3_TEST_BUCKET_URL not defined"); @@ -1830,296 +1635,118 @@ test_s3r_read(void) return 0; } - curl_global_init(CURL_GLOBAL_DEFAULT); - curl_ready = true; - FAIL_IF(S3_TEST_MAX_URL_SIZE < snprintf(url_raven, S3_TEST_MAX_URL_SIZE, "%s/%s", s3_test_bucket_url, - S3_TEST_RESOURCE_TEXT_PUBLIC)); - - for (i = 0; i < S3COMMS_TEST_BUFFER_SIZE; i++) - buffer[i] = '\0'; + if (S3_TEST_MAX_URL_SIZE < + snprintf(url_raven, S3_TEST_MAX_URL_SIZE, "%s/%s", s3_test_bucket_url, S3_TEST_RESOURCE_TEXT_PUBLIC)) + TEST_ERROR; - /* open file - */ + /* Open file */ handle = H5FD_s3comms_s3r_open(url_raven, NULL, NULL, NULL, NULL); - FAIL_IF(handle == NULL) - JSVERIFY(6464, H5FD_s3comms_s3r_get_filesize(handle), NULL) - - for (i = 0; i < S3COMMS_TEST_BUFFER_SIZE; i++) - buffer[i] = '\0'; - - /********************** - * read start of file * - **********************/ - - JSVERIFY(SUCCEED, H5FD_s3comms_s3r_read(handle, (haddr_t)0, (size_t)118, buffer), NULL) - JSVERIFY_STR("Once upon a midnight dreary, while I pondered, weak and weary,\n" - "Over many a quaint and curious volume of forgotten lore", - buffer, NULL) - - for (i = 0; i < S3COMMS_TEST_BUFFER_SIZE; i++) - buffer[i] = '\0'; - - /************************ - * read arbitrary range * - ************************/ - - JSVERIFY(SUCCEED, H5FD_s3comms_s3r_read(handle, (haddr_t)2540, (size_t)54, buffer), NULL) - JSVERIFY_STR("the grave and stern decorum of the countenance it wore", buffer, NULL) - - for (i = 0; i < S3COMMS_TEST_BUFFER_SIZE; i++) - buffer[i] = '\0'; - - /********************** - * read one character * - **********************/ - - JSVERIFY(SUCCEED, H5FD_s3comms_s3r_read(handle, (haddr_t)2540, (size_t)1, buffer), NULL) - JSVERIFY_STR("t", buffer, NULL) - - for (i = 0; i < S3COMMS_TEST_BUFFER_SIZE; i++) - buffer[i] = '\0'; - - /*************** - * read to EoF * - ***************/ - - JSVERIFY(SUCCEED, H5FD_s3comms_s3r_read(handle, (haddr_t)6370, (size_t)0, buffer), NULL) - JSVERIFY( - 0, - strncmp( + if (handle == NULL) + TEST_ERROR; + if (6464 != H5FD_s3comms_s3r_get_filesize(handle)) + TEST_ERROR; + + /***************************** + * Tests that should succeed * + *****************************/ + + /* Read from start of file */ + memset(buffer, 0, S3COMMS_READ_BUFFER_SIZE); + if (H5FD_s3comms_s3r_read(handle, (haddr_t)0, (size_t)118, buffer) < 0) + TEST_ERROR; + if (strcmp("Once upon a midnight dreary, while I pondered, weak and weary,\n" + "Over many a quaint and curious volume of forgotten lore", + buffer)) + TEST_ERROR; + + /* Read arbitrary range */ + memset(buffer, 0, S3COMMS_READ_BUFFER_SIZE); + if (H5FD_s3comms_s3r_read(handle, (haddr_t)2540, (size_t)54, buffer) < 0) + TEST_ERROR; + if (strcmp("the grave and stern decorum of the countenance it wore", buffer)) + TEST_ERROR; + + /* Read one character */ + memset(buffer, 0, S3COMMS_READ_BUFFER_SIZE); + if (H5FD_s3comms_s3r_read(handle, (haddr_t)2540, (size_t)1, buffer) < 0) + TEST_ERROR; + if (strcmp("t", buffer)) + TEST_ERROR; + + /* Read to EOF */ + memset(buffer, 0, S3COMMS_READ_BUFFER_SIZE); + if (H5FD_s3comms_s3r_read(handle, (haddr_t)6370, (size_t)0, buffer) < 0) + TEST_ERROR; + if (strncmp( buffer, "And my soul from out that shadow that lies floating on the floor\nShall be lifted—nevermore!\n", - 94), - buffer) - - for (i = 0; i < S3COMMS_TEST_BUFFER_SIZE; i++) - buffer[i] = '\0'; - - /***************** - * read past eof * - *****************/ - - JSVERIFY(FAIL, - H5FD_s3comms_s3r_read(handle, (haddr_t)6400, (size_t)100, /* 6400+100 > 6464 */ - buffer), - NULL) - JSVERIFY(0, strcmp("", buffer), NULL) - - /************************ - * read starts past eof * - ************************/ - - JSVERIFY(FAIL, - H5FD_s3comms_s3r_read(handle, (haddr_t)1200699, /* 1200699 > 6464 */ - (size_t)100, buffer), - NULL) - JSVERIFY(0, strcmp("", buffer), NULL) - - /********************** - * read starts on eof * - **********************/ - - JSVERIFY(FAIL, H5FD_s3comms_s3r_read(handle, (haddr_t)6464, (size_t)0, buffer), NULL) - JSVERIFY(0, strcmp("", buffer), NULL) + 94)) + TEST_ERROR; + + /************************** + * Tests that should fail * + **************************/ + + /* Read past eof */ + memset(buffer, 0, S3COMMS_READ_BUFFER_SIZE); + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_s3r_read(handle, (haddr_t)6400, (size_t)100, /* 6400+100 > 6464 */ buffer); + } + H5E_END_TRY + if (ret == SUCCEED) + TEST_ERROR; + if (strcmp("", buffer)) + TEST_ERROR; + + /* Read starts past eof */ + memset(buffer, 0, S3COMMS_READ_BUFFER_SIZE); + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_s3r_read(handle, (haddr_t)1200699, /* 1200699 > 6464 */ (size_t)100, buffer); + } + H5E_END_TRY + if (ret == SUCCEED) + TEST_ERROR; + if (strcmp("", buffer)) + TEST_ERROR; + + /* Read starts on eof */ + memset(buffer, 0, S3COMMS_READ_BUFFER_SIZE); + H5E_BEGIN_TRY + { + ret = H5FD_s3comms_s3r_read(handle, (haddr_t)6464, (size_t)0, buffer); + } + H5E_END_TRY + if (ret == SUCCEED) + TEST_ERROR; + if (strcmp("", buffer)) + TEST_ERROR; /************* * TEAR DOWN * *************/ - JSVERIFY(SUCCEED, H5FD_s3comms_s3r_close(handle), "unable to close file") - handle = NULL; - - curl_global_cleanup(); - curl_ready = false; + if (H5FD_s3comms_s3r_close(handle) < 0) + TEST_ERROR; PASSED(); return 0; error: - /*********** - * cleanup * - ***********/ - if (handle != NULL) H5FD_s3comms_s3r_close(handle); - - if (curl_ready == true) - curl_global_cleanup(); - - return -1; - -#undef S3COMMS_TEST_BUFFER_SIZE - + return 1; } /* end test_s3r_read() */ -/*--------------------------------------------------------------------------- - * - * Function: test_signing_key() - * - * Purpose: - * - * Define and verify behavior of `H5FD_s3comms_signing_key()` - * - * More test cases would be a very good idea. - * - *--------------------------------------------------------------------------- - */ -static herr_t -test_signing_key(void) -{ - /************************* - * test-local structures * - *************************/ - - struct testcase { - const char *region; - const char *secret_key; - const char *when; - unsigned char exp[SHA256_DIGEST_LENGTH]; - }; - - /************************ - * test-local variables * - ************************/ - - struct testcase cases[] = { - { - "us-east-1", - "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", - "20130524T000000Z", - { - 0xdb, 0xb8, 0x93, 0xac, 0xc0, 0x10, 0x96, 0x49, 0x18, 0xf1, 0xfd, - 0x43, 0x3a, 0xdd, 0x87, 0xc7, 0x0e, 0x8b, 0x0d, 0xb6, 0xbe, 0x30, - 0xc1, 0xfb, 0xea, 0xfe, 0xfa, 0x5e, 0xc6, 0xba, 0x83, 0x78, - }, - }, - }; - int i = 0; - unsigned char *key = NULL; - int ncases = 1; - - TESTING("signing_key"); - - for (i = 0; i < ncases; i++) { - key = (unsigned char *)malloc(sizeof(unsigned char) * SHA256_DIGEST_LENGTH); - assert(key != NULL); - - JSVERIFY(SUCCEED, H5FD_s3comms_signing_key(key, cases[i].secret_key, cases[i].region, cases[i].when), - NULL) - - JSVERIFY(0, strncmp((const char *)cases[i].exp, (const char *)key, SHA256_DIGEST_LENGTH), - (const char *)cases[i].exp) - - free(key); - key = NULL; - } - - /*************** - * ERROR CASES * - ***************/ - - key = (unsigned char *)malloc(sizeof(unsigned char) * SHA256_DIGEST_LENGTH); - assert(key != NULL); - - JSVERIFY(FAIL, H5FD_s3comms_signing_key(NULL, cases[0].secret_key, cases[0].region, cases[0].when), - "destination cannot be NULL") - - JSVERIFY(FAIL, H5FD_s3comms_signing_key(key, NULL, cases[0].region, cases[0].when), - "secret key cannot be NULL") - - JSVERIFY(FAIL, H5FD_s3comms_signing_key(key, cases[0].secret_key, NULL, cases[0].when), - "aws region cannot be NULL") - - JSVERIFY(FAIL, H5FD_s3comms_signing_key(key, cases[0].secret_key, cases[0].region, NULL), - "time string cannot be NULL") - - free(key); - key = NULL; - - PASSED(); - return 0; - -error: - if (key != NULL) { - free(key); - } - - return -1; - -} /* end test_signing_key() */ - -/*--------------------------------------------------------------------------- - * - * Function: test_tostringtosign() - * - * Purpose: - * - * Verify that we can get the "string to sign" from a Canonical Request and - * related information. - * - * Demonstrate failure cases. - * - * Return: - * - * Success: 0 - * Failure: -1 - * - *--------------------------------------------------------------------------- - */ -static herr_t -test_tostringtosign(void) -{ - /************************ - * test-local variables * - ************************/ - - const char canonreq[] = "GET\n/" - "test.txt\n\nhost:examplebucket.s3.amazonaws.com\nrange:bytes=0-9\nx-amz-content-" - "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\nx-amz-" - "date:20130524T000000Z\n\nhost;range;x-amz-content-sha256;x-amz-" - "date\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; - const char iso8601now[] = "20130524T000000Z"; - const char region[] = "us-east-1"; - char s2s[512]; - - TESTING("s3comms tostringtosign"); - - JSVERIFY(SUCCEED, H5FD_s3comms_tostringtosign(s2s, canonreq, iso8601now, region), - "unable to create string to sign") - - JSVERIFY_STR("AWS4-HMAC-SHA256\n20130524T000000Z\n20130524/us-east-1/s3/" - "aws4_request\n7344ae5b7ee6c3e7e6b0fe0640412a37625d1fbfff95c48bbb2dc43964946972", - s2s, NULL) - - JSVERIFY(FAIL, H5FD_s3comms_tostringtosign(s2s, NULL, iso8601now, region), - "canonical request string cannot be NULL") - - JSVERIFY(FAIL, H5FD_s3comms_tostringtosign(s2s, canonreq, NULL, region), "time string cannot be NULL") - - JSVERIFY(FAIL, H5FD_s3comms_tostringtosign(s2s, canonreq, iso8601now, NULL), "aws region cannot be NULL") - - PASSED(); - return 0; - -error: - return -1; - -} /* end test_tostringtosign() */ - #endif /* H5_HAVE_ROS3_VFD */ /*------------------------------------------------------------------------- - * Function: main() - * - * Purpose: + * Function: main() * - * Run unit tests for S3 Communications (s3comms). - * - * Return: - * - * Success: 0 - * Failure: 1 + * Purpose: Run unit tests for S3 communications (s3comms) * + * Return: EXIT_SUCCESS/EXIT_FAILURE *------------------------------------------------------------------------- */ int @@ -2133,7 +1760,7 @@ main(void) #endif /* H5_HAVE_ROS3_VFD */ - printf("Testing S3Communications functionality.\n"); + printf("Testing S3 communications functionality\n"); #ifdef H5_HAVE_ROS3_VFD @@ -2146,6 +1773,7 @@ main(void) /* TODO: unit/regression test for H5FD_s3comms_load_aws_profile() * requires a few test files and/or manipulation of default path */ + /* attempt to load test credentials * if unable, certain tests will be skipped */ @@ -2164,28 +1792,31 @@ main(void) s3_test_bucket_defined = true; } - /* tests ordered roughly by dependence */ - nerrors += test_macro_format_credential() < 0 ? 1 : 0; - nerrors += test_bytes_to_hex() < 0 ? 1 : 0; - nerrors += test_HMAC_SHA256() < 0 ? 1 : 0; - nerrors += test_signing_key() < 0 ? 1 : 0; - nerrors += test_hrb_node_set() < 0 ? 1 : 0; - nerrors += test_hrb_init_request() < 0 ? 1 : 0; - nerrors += test_parse_url() < 0 ? 1 : 0; - nerrors += test_aws_canonical_request() < 0 ? 1 : 0; - nerrors += test_tostringtosign() < 0 ? 1 : 0; - nerrors += test_s3r_open() < 0 ? 1 : 0; - nerrors += test_s3r_get_filesize() < 0 ? 1 : 0; - nerrors += test_s3r_read() < 0 ? 1 : 0; + curl_global_init(CURL_GLOBAL_DEFAULT); + + nerrors += test_macro_format_credential(); + nerrors += test_aws_canonical_request(); + nerrors += test_bytes_to_hex(); + nerrors += test_hrb_init_request(); + nerrors += test_hrb_node_set(); + nerrors += test_HMAC_SHA256(); + nerrors += test_parse_url(); + nerrors += test_signing_key(); + nerrors += test_tostringtosign(); + + nerrors += test_s3r_get_filesize(); + nerrors += test_s3r_open(); + nerrors += test_s3r_read(); + + curl_global_cleanup(); if (nerrors) { - printf("***** %d S3comms TEST%s FAILED! *****\n", nerrors, nerrors > 1 ? "S" : ""); - return 1; + printf("***** %d s3comms TEST%s FAILED! *****\n", nerrors, nerrors > 1 ? "S" : ""); + return EXIT_FAILURE; } - printf("All S3comms tests passed.\n"); - - return 0; + printf("All s3comms tests passed.\n"); + return EXIT_SUCCESS; #else