X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=clients%2Fmemcapable.c;h=69d2557ea959608a0702a2494971595cd17cb313;hb=1591feaed3f710d48f76a6fc8a3a52242ab34fd9;hp=36be2e55577ad6de9e04df4e51166dbfcf215f6f;hpb=c67da677fe0944d3d2d3ff46e65fc3bc775404ae;p=m6w6%2Flibmemcached diff --git a/clients/memcapable.c b/clients/memcapable.c index 36be2e55..69d2557e 100644 --- a/clients/memcapable.c +++ b/clients/memcapable.c @@ -14,11 +14,6 @@ #include "config.h" #include #include -#include -#include -#include -#include -#include #include #include #include @@ -29,11 +24,12 @@ #include #include #include -#include #include +#include #include #include +#include "utilities.h" #ifdef linux /* /usr/include/netinet/in.h defines macros from ntohs() to _bswap_nn to @@ -48,7 +44,7 @@ /* Should we generate coredumps when we enounter an error (-c) */ static bool do_core= false; /* connection to the server */ -static int sock; +static memcached_socket_t sock; /* Should the output from test failures be verbose or quiet? */ static bool verbose= false; @@ -112,14 +108,23 @@ static struct addrinfo *lookuphost(const char *hostname, const char *port) * Set the socket in nonblocking mode * @return -1 if failure, the socket otherwise */ -static int set_noblock(void) +static memcached_socket_t set_noblock(void) { +#ifdef WIN32 + u_long arg = 1; + if (ioctlsocket(sock, FIONBIO, &arg) == SOCKET_ERROR) + { + perror("Failed to set nonblocking io"); + closesocket(sock); + return INVALID_SOCKET; + } +#else int flags= fcntl(sock, F_GETFL, 0); if (flags == -1) { perror("Failed to get socket flags"); - close(sock); - return -1; + closesocket(sock); + return INVALID_SOCKET; } if ((flags & O_NONBLOCK) != O_NONBLOCK) @@ -127,11 +132,11 @@ static int set_noblock(void) if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { perror("Failed to set socket to nonblocking mode"); - close(sock); - return -1; + closesocket(sock); + return INVALID_SOCKET; } } - +#endif return sock; } @@ -141,28 +146,30 @@ static int set_noblock(void) * @param port the port number (or service) to connect to * @return positive integer if success, -1 otherwise */ -static int connect_server(const char *hostname, const char *port) +static memcached_socket_t connect_server(const char *hostname, const char *port) { struct addrinfo *ai= lookuphost(hostname, port); - sock= -1; + sock= INVALID_SOCKET; if (ai != NULL) { - if ((sock=socket(ai->ai_family, ai->ai_socktype, - ai->ai_protocol)) != -1) + if ((sock= socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol)) != INVALID_SOCKET) { - if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) + if (connect(sock, ai->ai_addr, ai->ai_addrlen) == SOCKET_ERROR) { fprintf(stderr, "Failed to connect socket: %s\n", - strerror(errno)); - close(sock); - sock= -1; + strerror(get_socket_errno())); + closesocket(sock); + sock= INVALID_SOCKET; } else { sock= set_noblock(); } - } else - fprintf(stderr, "Failed to create socket: %s\n", strerror(errno)); + } + else + fprintf(stderr, "Failed to create socket: %s\n", + strerror(get_socket_errno())); freeaddrinfo(ai); } @@ -170,28 +177,29 @@ static int connect_server(const char *hostname, const char *port) return sock; } -static ssize_t timeout_io_op(int fd, short direction, void *buf, size_t len) +static ssize_t timeout_io_op(memcached_socket_t fd, short direction, void *buf, size_t len) { ssize_t ret; if (direction == POLLOUT) - ret= write(fd, buf, len); + ret= send(fd, buf, len, 0); else - ret= read(fd, buf, len); + ret= recv(fd, buf, len, 0); - if (ret == -1 && errno == EWOULDBLOCK) { + if (ret == SOCKET_ERROR && get_socket_errno() == EWOULDBLOCK) { struct pollfd fds= { .events= direction, .fd= fd }; + int err= poll(&fds, 1, timeout * 1000); if (err == 1) { if (direction == POLLOUT) - ret= write(fd, buf, len); + ret= send(fd, buf, len, 0); else - ret= read(fd, buf, len); + ret= recv(fd, buf, len, 0); } else if (err == 0) { @@ -245,7 +253,7 @@ static enum test_return retry_write(const void* buf, size_t len) size_t num_bytes= len - offset; ssize_t nw= timeout_io_op(sock, POLLOUT, (void*)(ptr + offset), num_bytes); if (nw == -1) - verify(errno == EINTR || errno == EAGAIN); + verify(get_socket_errno() == EINTR || get_socket_errno() == EAGAIN); else offset+= (size_t)nw; } while (offset < len); @@ -295,7 +303,8 @@ static enum test_return retry_read(void *buf, size_t len) ssize_t nr= timeout_io_op(sock, POLLIN, ((char*) buf) + offset, len - offset); switch (nr) { case -1 : - verify(errno == EINTR || errno == EAGAIN); + fprintf(stderr, "Errno: %d %s\n", get_socket_errno(), strerror(errno)); + verify(get_socket_errno() == EINTR || get_socket_errno() == EAGAIN); break; case 0: return TEST_FAIL; @@ -313,7 +322,7 @@ static enum test_return retry_read(void *buf, size_t len) */ static enum test_return recv_packet(response *rsp) { - execute(retry_read(rsp, sizeof (protocol_binary_response_no_extras))); + execute(retry_read(rsp, sizeof(protocol_binary_response_no_extras))); /* Fix the byte order in the packet header */ rsp->plain.message.header.response.keylen= @@ -342,7 +351,7 @@ static enum test_return recv_packet(response *rsp) * @param dta the data to store with the key * @param dtalen the length of the data to store with the key * @param flags the flags to store along with the key - * @param exp the expiry time for the key + * @param exptime the expiry time for the key */ static void storage_command(command *cmd, uint8_t cc, @@ -351,7 +360,7 @@ static void storage_command(command *cmd, const void* dta, size_t dtalen, uint32_t flags, - uint32_t exp) + uint32_t exptime) { /* all of the storage commands use the same command layout */ protocol_binary_request_set *request= &cmd->set; @@ -364,7 +373,7 @@ static void storage_command(command *cmd, request->message.header.request.bodylen= (uint32_t)(keylen + 8 + dtalen); request->message.header.request.opaque= 0xdeadbeef; request->message.body.flags= flags; - request->message.body.expiration= exp; + request->message.body.expiration= exptime; off_t key_offset= sizeof (protocol_binary_request_no_extras) + 8; memcpy(cmd->bytes + key_offset, key, keylen); @@ -435,7 +444,7 @@ static void flush_command(command *cmd, * @param keylen the number of bytes in the key * @param delta the number to add/subtract * @param initial the initial value if the key doesn't exist - * @param exp when the key should expire if it isn't set + * @param exptime when the key should expire if it isn't set */ static void arithmetic_command(command *cmd, uint8_t cc, @@ -443,7 +452,7 @@ static void arithmetic_command(command *cmd, size_t keylen, uint64_t delta, uint64_t initial, - uint32_t exp) + uint32_t exptime) { memset(cmd, 0, sizeof (cmd->incr)); cmd->incr.message.header.request.magic= PROTOCOL_BINARY_REQ; @@ -454,7 +463,7 @@ static void arithmetic_command(command *cmd, cmd->incr.message.header.request.opaque= 0xdeadbeef; cmd->incr.message.body.delta= htonll(delta); cmd->incr.message.body.initial= htonll(initial); - cmd->incr.message.body.expiration= htonl(exp); + cmd->incr.message.body.expiration= htonl(exptime); off_t key_offset= sizeof (protocol_binary_request_no_extras) + 20; memcpy(cmd->bytes + key_offset, key, keylen); @@ -575,18 +584,31 @@ static enum test_return do_validate_response_header(response *rsp, #define validate_response_header(a,b,c) \ do_validate_response_header(a,b,c) == TEST_PASS -static enum test_return test_binary_noop(void) + +static enum test_return send_binary_noop(void) { command cmd; - response rsp; raw_command(&cmd, PROTOCOL_BINARY_CMD_NOOP, NULL, 0, NULL, 0); execute(send_packet(&cmd)); + return TEST_PASS; +} + +static enum test_return receive_binary_noop(void) +{ + response rsp; execute(recv_packet(&rsp)); verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_NOOP, PROTOCOL_BINARY_RESPONSE_SUCCESS)); return TEST_PASS; } +static enum test_return test_binary_noop(void) +{ + execute(send_binary_noop()); + execute(receive_binary_noop()); + return TEST_PASS; +} + static enum test_return test_binary_quit_impl(uint8_t cc) { command cmd; @@ -601,7 +623,7 @@ static enum test_return test_binary_quit_impl(uint8_t cc) PROTOCOL_BINARY_RESPONSE_SUCCESS)); } - /* Socket should be closed now, read should return 0 */ + /* Socket should be closed now, read should return EXIT_SUCCESS */ verify(timeout_io_op(sock, POLLIN, rsp.bytes, sizeof(rsp.bytes)) == 0); return TEST_PASS_RECONNECT; @@ -672,10 +694,12 @@ static enum test_return test_binary_set_impl(const char* key, uint8_t cc) cmd.plain.message.header.request.cas= htonll(rsp.plain.message.header.response.cas - 1); execute(resend_packet(&cmd)); + execute(send_binary_noop()); execute(recv_packet(&rsp)); verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS)); + execute(receive_binary_noop()); - return test_binary_noop(); + return TEST_PASS; } static enum test_return test_binary_set(void) @@ -712,7 +736,9 @@ static enum test_return test_binary_add_impl(const char* key, uint8_t cc) else expected_result= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS; + execute(send_binary_noop()); execute(recv_packet(&rsp)); + execute(receive_binary_noop()); verify(validate_response_header(&rsp, cc, expected_result)); } else @@ -769,7 +795,9 @@ static enum test_return test_binary_replace_impl(const char* key, uint8_t cc) else expected_result=PROTOCOL_BINARY_RESPONSE_SUCCESS; + execute(send_binary_noop()); execute(recv_packet(&rsp)); + execute(receive_binary_noop()); verify(validate_response_header(&rsp, cc, expected_result)); if (ii == 0) @@ -796,7 +824,9 @@ static enum test_return test_binary_replace_impl(const char* key, uint8_t cc) cmd.plain.message.header.request.cas= htonll(rsp.plain.message.header.response.cas - 1); execute(resend_packet(&cmd)); + execute(send_binary_noop()); execute(recv_packet(&rsp)); + execute(receive_binary_noop()); verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS)); return TEST_PASS; @@ -820,8 +850,10 @@ static enum test_return test_binary_delete_impl(const char *key, uint8_t cc) /* The delete shouldn't work the first time, because the item isn't there */ execute(send_packet(&cmd)); + execute(send_binary_noop()); execute(recv_packet(&rsp)); verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)); + execute(receive_binary_noop()); execute(binary_set_item(key, key)); /* The item should be present now, resend*/ @@ -854,19 +886,23 @@ static enum test_return test_binary_get_impl(const char *key, uint8_t cc) raw_command(&cmd, cc, key, strlen(key), NULL, 0); execute(send_packet(&cmd)); + execute(send_binary_noop()); if (cc == PROTOCOL_BINARY_CMD_GET || cc == PROTOCOL_BINARY_CMD_GETK) { execute(recv_packet(&rsp)); verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)); } - else - execute(test_binary_noop()); + + execute(receive_binary_noop()); execute(binary_set_item(key, key)); execute(resend_packet(&cmd)); + execute(send_binary_noop()); + execute(recv_packet(&rsp)); verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS)); + execute(receive_binary_noop()); return TEST_PASS; } @@ -1114,24 +1150,6 @@ static enum test_return test_binary_stat(void) return TEST_PASS; } -static enum test_return test_binary_illegal(void) -{ - command cmd; - response rsp; - uint8_t cc= 0x1b; - - while (cc != 0x00) - { - raw_command(&cmd, cc, NULL, 0, NULL, 0); - execute(send_packet(&cmd)); - execute(recv_packet(&rsp)); - verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND)); - ++cc; - } - - return TEST_PASS_RECONNECT; -} - static enum test_return send_string(const char *cmd) { execute(retry_write(cmd, strlen(cmd))); @@ -1163,24 +1181,37 @@ static enum test_return receive_line(char *buffer, size_t size) static enum test_return receive_response(const char *msg) { char buffer[80]; execute(receive_line(buffer, sizeof(buffer))); + if (strcmp(msg, buffer) != 0) { + fprintf(stderr, "[%s]\n", buffer); + } verify(strcmp(msg, buffer) == 0); return TEST_PASS; } +static enum test_return receive_error_response(void) +{ + char buffer[80]; + execute(receive_line(buffer, sizeof(buffer))); + verify(strncmp(buffer, "ERROR", 5) == 0 || + strncmp(buffer, "CLIENT_ERROR", 12) == 0 || + strncmp(buffer, "SERVER_ERROR", 12) == 0); + return TEST_PASS; +} + static enum test_return test_ascii_quit(void) { /* Verify that quit handles unknown options */ execute(send_string("quit foo bar\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); /* quit doesn't support noreply */ execute(send_string("quit noreply\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); /* Verify that quit works */ execute(send_string("quit\r\n")); - /* Socket should be closed now, read should return 0 */ + /* Socket should be closed now, read should return EXIT_SUCCESS */ char buffer[80]; verify(timeout_io_op(sock, POLLIN, buffer, sizeof(buffer)) == 0); return TEST_PASS_RECONNECT; @@ -1191,11 +1222,11 @@ static enum test_return test_ascii_version(void) { /* Verify that version command handles unknown options */ execute(send_string("version foo bar\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); /* version doesn't support noreply */ execute(send_string("version noreply\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); /* Verify that verify works */ execute(send_string("version\r\n")); @@ -1210,16 +1241,16 @@ static enum test_return test_ascii_verbosity(void) { /* This command does not adhere to the spec! */ execute(send_string("verbosity foo bar my\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); execute(send_string("verbosity noreply\r\n")); - execute(test_ascii_version()); + execute(receive_error_response()); execute(send_string("verbosity 0 noreply\r\n")); execute(test_ascii_version()); execute(send_string("verbosity\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); execute(send_string("verbosity 1\r\n")); execute(receive_response("OK\r\n")); @@ -1236,8 +1267,7 @@ static enum test_return test_ascii_set_impl(const char* key, bool noreply) { /* @todo add tests for bogus format! */ char buffer[1024]; - sprintf(buffer, "set %s 0 0 5%s\r\nvalue\r\n", key, - noreply ? " noreply" : ""); + snprintf(buffer, sizeof(buffer), "set %s 0 0 5%s\r\nvalue\r\n", key, noreply ? " noreply" : ""); execute(send_string(buffer)); if (!noreply) @@ -1260,8 +1290,7 @@ static enum test_return test_ascii_add_impl(const char* key, bool noreply) { /* @todo add tests for bogus format! */ char buffer[1024]; - sprintf(buffer, "add %s 0 0 5%s\r\nvalue\r\n", key, - noreply ? " noreply" : ""); + snprintf(buffer, sizeof(buffer), "add %s 0 0 5%s\r\nvalue\r\n", key, noreply ? " noreply" : ""); execute(send_string(buffer)); if (!noreply) @@ -1285,6 +1314,41 @@ static enum test_return test_ascii_add_noreply(void) return test_ascii_add_impl("test_ascii_add_noreply", true); } +static enum test_return ascii_get_unknown_value(char **key, char **value, ssize_t *ndata) +{ + char buffer[1024]; + + execute(receive_line(buffer, sizeof(buffer))); + verify(strncmp(buffer, "VALUE ", 6) == 0); + char *end= strchr(buffer + 6, ' '); + verify(end != NULL); + *end= '\0'; + *key= strdup(buffer + 6); + verify(*key != NULL); + char *ptr= end + 1; + + unsigned long val= strtoul(ptr, &end, 10); /* flags */ + verify(ptr != end); + verify(val == 0); + verify(end != NULL); + *ndata = (ssize_t)strtoul(end, &end, 10); /* size */ + verify(ptr != end); + verify(end != NULL); + while (*end != '\n' && isspace(*end)) + ++end; + verify(*end == '\n'); + + *value= malloc((size_t)*ndata); + verify(*value != NULL); + + execute(retry_read(*value, (size_t)*ndata)); + + execute(retry_read(buffer, 2)); + verify(memcmp(buffer, "\r\n", 2) == 0); + + return TEST_PASS; +} + static enum test_return ascii_get_value(const char *key, const char *value) { @@ -1328,7 +1392,7 @@ static enum test_return ascii_get_item(const char *key, const char *value, datasize= strlen(value); verify(datasize < sizeof(buffer)); - sprintf(buffer, "get %s\r\n", key); + snprintf(buffer, sizeof(buffer), "get %s\r\n", key); execute(send_string(buffer)); if (exist) @@ -1389,7 +1453,7 @@ static enum test_return ascii_gets_item(const char *key, const char *value, datasize= strlen(value); verify(datasize < sizeof(buffer)); - sprintf(buffer, "gets %s\r\n", key); + snprintf(buffer, sizeof(buffer), "gets %s\r\n", key); execute(send_string(buffer)); if (exist) @@ -1405,7 +1469,7 @@ static enum test_return ascii_set_item(const char *key, const char *value) { char buffer[300]; size_t len= strlen(value); - sprintf(buffer, "set %s 0 0 %u\r\n", key, (unsigned int)len); + snprintf(buffer, sizeof(buffer), "set %s 0 0 %u\r\n", key, (unsigned int)len); execute(send_string(buffer)); execute(retry_write(value, len)); execute(send_string("\r\n")); @@ -1416,8 +1480,7 @@ static enum test_return ascii_set_item(const char *key, const char *value) static enum test_return test_ascii_replace_impl(const char* key, bool noreply) { char buffer[1024]; - sprintf(buffer, "replace %s 0 0 5%s\r\nvalue\r\n", key, - noreply ? " noreply" : ""); + snprintf(buffer, sizeof(buffer), "replace %s 0 0 5%s\r\nvalue\r\n", key, noreply ? " noreply" : ""); execute(send_string(buffer)); if (noreply) @@ -1457,8 +1520,7 @@ static enum test_return test_ascii_cas_impl(const char* key, bool noreply) execute(ascii_set_item(key, "value")); execute(ascii_gets_item(key, "value", true, &cas)); - sprintf(buffer, "cas %s 0 0 6 %lu%s\r\nvalue2\r\n", key, cas, - noreply ? " noreply" : ""); + snprintf(buffer, sizeof(buffer), "cas %s 0 0 6 %lu%s\r\nvalue2\r\n", key, cas, noreply ? " noreply" : ""); execute(send_string(buffer)); if (noreply) @@ -1492,13 +1554,13 @@ static enum test_return test_ascii_delete_impl(const char *key, bool noreply) execute(ascii_set_item(key, "value")); execute(send_string("delete\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); /* BUG: the server accepts delete a b */ execute(send_string("delete a b c d e\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); char buffer[1024]; - sprintf(buffer, "delete %s%s\r\n", key, noreply ? " noreply" : ""); + snprintf(buffer, sizeof(buffer), "delete %s%s\r\n", key, noreply ? " noreply" : ""); execute(send_string(buffer)); if (noreply) @@ -1531,7 +1593,7 @@ static enum test_return test_ascii_get(void) execute(ascii_set_item("test_ascii_get", "value")); execute(send_string("get\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); execute(ascii_get_item("test_ascii_get", "value", true)); execute(ascii_get_item("test_ascii_get_notfound", "value", false)); @@ -1543,7 +1605,7 @@ static enum test_return test_ascii_gets(void) execute(ascii_set_item("test_ascii_gets", "value")); execute(send_string("gets\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); unsigned long cas; execute(ascii_gets_item("test_ascii_gets", "value", true, &cas)); execute(ascii_gets_item("test_ascii_gets_notfound", "value", false, &cas)); @@ -1553,31 +1615,65 @@ static enum test_return test_ascii_gets(void) static enum test_return test_ascii_mget(void) { - execute(ascii_set_item("test_ascii_mget1", "value")); - execute(ascii_set_item("test_ascii_mget2", "value")); - execute(ascii_set_item("test_ascii_mget3", "value")); - execute(ascii_set_item("test_ascii_mget4", "value")); - execute(ascii_set_item("test_ascii_mget5", "value")); + const uint32_t nkeys= 5; + const char * const keys[]= { + "test_ascii_mget1", + "test_ascii_mget2", + /* test_ascii_mget_3 does not exist :) */ + "test_ascii_mget4", + "test_ascii_mget5", + "test_ascii_mget6" + }; + + for (uint32_t x= 0; x < nkeys; ++x) + execute(ascii_set_item(keys[x], "value")); + /* Ask for a key that doesn't exist as well */ execute(send_string("get test_ascii_mget1 test_ascii_mget2 test_ascii_mget3 " "test_ascii_mget4 test_ascii_mget5 " "test_ascii_mget6\r\n")); - execute(ascii_get_value("test_ascii_mget1", "value")); - execute(ascii_get_value("test_ascii_mget2", "value")); - execute(ascii_get_value("test_ascii_mget3", "value")); - execute(ascii_get_value("test_ascii_mget4", "value")); - execute(ascii_get_value("test_ascii_mget5", "value")); + + char *returned[nkeys]; + + for (uint32_t x= 0; x < nkeys; ++x) + { + ssize_t nbytes = 0; + char *v= NULL; + execute(ascii_get_unknown_value(&returned[x], &v, &nbytes)); + verify(nbytes == 5); + verify(memcmp(v, "value", 5) == 0); + free(v); + } char buffer[5]; execute(retry_read(buffer, 5)); verify(memcmp(buffer, "END\r\n", 5) == 0); - return TEST_PASS; + + /* verify that we got all the keys we expected */ + for (uint32_t x= 0; x < nkeys; ++x) + { + bool found= false; + for (uint32_t y= 0; y < nkeys; ++y) + { + if (strcmp(keys[x], returned[y]) == 0) + { + found = true; + break; + } + } + verify(found); + } + + for (uint32_t x= 0; x < nkeys; ++x) + free(returned[x]); + + return TEST_PASS; } static enum test_return test_ascii_incr_impl(const char* key, bool noreply) { char cmd[300]; - sprintf(cmd, "incr %s 1%s\r\n", key, noreply ? " noreply" : ""); + snprintf(cmd, sizeof(cmd), "incr %s 1%s\r\n", key, noreply ? " noreply" : ""); execute(ascii_set_item(key, "0")); for (int x= 1; x < 11; ++x) @@ -1613,7 +1709,7 @@ static enum test_return test_ascii_incr_noreply(void) static enum test_return test_ascii_decr_impl(const char* key, bool noreply) { char cmd[300]; - sprintf(cmd, "decr %s 1%s\r\n", key, noreply ? " noreply" : ""); + snprintf(cmd, sizeof(cmd), "decr %s 1%s\r\n", key, noreply ? " noreply" : ""); execute(ascii_set_item(key, "9")); for (int x= 8; x > -1; --x) @@ -1664,7 +1760,7 @@ static enum test_return test_ascii_flush_impl(const char *key, bool noreply) /* Verify that the flush_all command handles unknown options */ /* Bug in the current memcached server! */ execute(send_string("flush_all foo bar\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); #endif execute(ascii_set_item(key, key)); @@ -1715,10 +1811,10 @@ static enum test_return test_ascii_concat_impl(const char *key, value="hello"; char cmd[400]; - sprintf(cmd, "%s %s 0 0 %u%s\r\n%s\r\n", - append ? "append" : "prepend", - key, (unsigned int)strlen(value), noreply ? " noreply" : "", - value); + snprintf(cmd, sizeof(cmd), "%s %s 0 0 %u%s\r\n%s\r\n", + append ? "append" : "prepend", + key, (unsigned int)strlen(value), noreply ? " noreply" : "", + value); execute(send_string(cmd)); if (noreply) @@ -1728,10 +1824,10 @@ static enum test_return test_ascii_concat_impl(const char *key, execute(ascii_get_item(key, "hello world", true)); - sprintf(cmd, "%s %s_notfound 0 0 %u%s\r\n%s\r\n", - append ? "append" : "prepend", - key, (unsigned int)strlen(value), noreply ? " noreply" : "", - value); + snprintf(cmd, sizeof(cmd), "%s %s_notfound 0 0 %u%s\r\n%s\r\n", + append ? "append" : "prepend", + key, (unsigned int)strlen(value), noreply ? " noreply" : "", + value); execute(send_string(cmd)); if (noreply) @@ -1765,7 +1861,7 @@ static enum test_return test_ascii_prepend_noreply(void) static enum test_return test_ascii_stat(void) { execute(send_string("stats noreply\r\n")); - execute(receive_response("ERROR\r\n")); + execute(receive_error_response()); execute(send_string("stats\r\n")); char buffer[1024]; do { @@ -1838,28 +1934,49 @@ struct testcase testcases[]= { { "binary prepend", test_binary_prepend }, { "binary prependq", test_binary_prependq }, { "binary stat", test_binary_stat }, - { "binary illegal", test_binary_illegal }, { NULL, NULL} }; +const int ascii_tests = 1; +const int binary_tests = 2; + +struct test_type_st +{ + bool ascii; + bool binary; +}; + int main(int argc, char **argv) { static const char * const status_msg[]= {"[skip]", "[pass]", "[pass]", "[FAIL]"}; + struct test_type_st tests= { true, true }; int total= 0; int failed= 0; const char *hostname= "localhost"; const char *port= "11211"; int cmd; + bool prompt= false; + const char *testname= NULL; + - while ((cmd= getopt(argc, argv, "t:vch:p:?")) != EOF) + + while ((cmd= getopt(argc, argv, "t:vch:p:PT:?ab")) != EOF) { switch (cmd) { + case 'a': + tests.ascii= true; + tests.binary= false; + break; + case 'b': + tests.ascii= false; + tests.binary= true; + break; case 't': timeout= atoi(optarg); if (timeout == 0) { fprintf(stderr, "Invalid timeout. Please specify a number for -t\n"); - return 1; + return EXIT_FAILURE; } break; case 'v': verbose= true; @@ -1870,30 +1987,70 @@ int main(int argc, char **argv) break; case 'p': port= optarg; break; + case 'P': prompt= true; + break; + case 'T': testname= optarg; + break; default: - fprintf(stderr, "Usage: %s [-h hostname] [-p port] [-c] [-v] [-t n]\n" + fprintf(stderr, "Usage: %s [-h hostname] [-p port] [-c] [-v] [-t n] [-P] [-T testname]'\n" "\t-c\tGenerate coredump if a test fails\n" "\t-v\tVerbose test output (print out the assertion)\n" - "\t-t n\tSet the timeout for io-operations to n seconds\n", + "\t-t n\tSet the timeout for io-operations to n seconds\n" + "\t-P\tPrompt the user before starting a test.\n" + "\t\t\t\"skip\" will skip the test\n" + "\t\t\t\"quit\" will terminate memcapable\n" + "\t\t\tEverything else will start the test\n" + "\t-T n\tJust run the test named n\n" + "\t-a\tOnly test the ascii protocol\n" + "\t-b\tOnly test the binary protocol\n", argv[0]); - return 1; + return EXIT_FAILURE; } } + initialize_sockets(); sock= connect_server(hostname, port); - if (sock == -1) + if (sock == INVALID_SOCKET) { fprintf(stderr, "Failed to connect to <%s:%s>: %s\n", - hostname, port, strerror(errno)); - return 1; + hostname, port, strerror(get_socket_errno())); + return EXIT_FAILURE; } for (int ii= 0; testcases[ii].description != NULL; ++ii) { + if (testname != NULL && strcmp(testcases[ii].description, testname) != 0) + continue; + + if ((testcases[ii].description[0] == 'a' && (tests.ascii) == 0) || + (testcases[ii].description[0] == 'b' && (tests.binary) == 0)) + { + continue; + } ++total; fprintf(stdout, "%-40s", testcases[ii].description); fflush(stdout); + if (prompt) + { + fprintf(stdout, "\nPress when you are ready? "); + char buffer[80] = {0}; + if (fgets(buffer, sizeof(buffer), stdin) != NULL) { + if (strncmp(buffer, "skip", 4) == 0) + { + fprintf(stdout, "%-40s%s\n", testcases[ii].description, + status_msg[TEST_SKIP]); + fflush(stdout); + continue; + } + if (strncmp(buffer, "quit", 4) == 0) + exit(0); + } + + fprintf(stdout, "%-40s", testcases[ii].description); + fflush(stdout); + } + bool reconnect= false; enum test_return ret= testcases[ii].function(); if (ret == TEST_FAIL) @@ -1909,18 +2066,18 @@ int main(int argc, char **argv) fprintf(stderr, "%s\n", status_msg[ret]); if (reconnect) { - (void) close(sock); - if ((sock=connect_server(hostname, port)) == -1) + closesocket(sock); + if ((sock= connect_server(hostname, port)) == INVALID_SOCKET) { fprintf(stderr, "Failed to connect to <%s:%s>: %s\n", - hostname, port, strerror(errno)); + hostname, port, strerror(get_socket_errno())); fprintf(stderr, "%d of %d tests failed\n", failed, total); - return 1; + return EXIT_FAILURE; } } } - (void) close(sock); + closesocket(sock); if (failed == 0) fprintf(stdout, "All tests passed\n"); else