+
+
+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" : "");
+ execute(send_string(buffer));
+
+ if (!noreply)
+ execute(receive_response("STORED\r\n"));
+
+ return test_ascii_version();
+}
+
+static enum test_return test_ascii_set(void)
+{
+ return test_ascii_set_impl("test_ascii_set", false);
+}
+
+static enum test_return test_ascii_set_noreply(void)
+{
+ return test_ascii_set_impl("test_ascii_set_noreply", true);
+}
+
+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" : "");
+ execute(send_string(buffer));
+
+ if (!noreply)
+ execute(receive_response("STORED\r\n"));
+
+ execute(send_string(buffer));
+
+ if (!noreply)
+ execute(receive_response("NOT_STORED\r\n"));
+
+ return test_ascii_version();
+}
+
+static enum test_return test_ascii_add(void)
+{
+ return test_ascii_add_impl("test_ascii_add", false);
+}
+
+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_value(const char *key, const char *value)
+{
+
+ char buffer[1024];
+ size_t datasize= strlen(value);
+
+ verify(datasize < sizeof(buffer));
+ execute(receive_line(buffer, sizeof(buffer)));
+ verify(strncmp(buffer, "VALUE ", 6) == 0);
+ verify(strncmp(buffer + 6, key, strlen(key)) == 0);
+ char *ptr= buffer + 6 + strlen(key) + 1;
+ char *end;
+
+ unsigned long val= strtoul(ptr, &end, 10); /* flags */
+ verify(ptr != end);
+ verify(val == 0);
+ verify(end != NULL);
+ val= strtoul(end, &end, 10); /* size */
+ verify(ptr != end);
+ verify(val == datasize);
+ verify(end != NULL);
+ while (*end != '\n' && isspace(*end))
+ ++end;
+ verify(*end == '\n');
+
+ execute(retry_read(buffer, datasize));
+ verify(memcmp(buffer, value, datasize) == 0);
+
+ execute(retry_read(buffer, 2));
+ verify(memcmp(buffer, "\r\n", 2) == 0);
+
+ return TEST_PASS;
+}
+
+static enum test_return ascii_get_item(const char *key, const char *value,
+ bool exist)
+{
+ char buffer[1024];
+ size_t datasize= 0;
+ if (value != NULL)
+ datasize= strlen(value);
+
+ verify(datasize < sizeof(buffer));
+ sprintf(buffer, "get %s\r\n", key);
+ execute(send_string(buffer));
+
+ if (exist)
+ execute(ascii_get_value(key, value));
+
+ execute(retry_read(buffer, 5));
+ verify(memcmp(buffer, "END\r\n", 5) == 0);
+
+ return TEST_PASS;
+}
+
+static enum test_return ascii_gets_value(const char *key, const char *value,
+ unsigned long *cas)
+{
+
+ char buffer[1024];
+ size_t datasize= strlen(value);
+
+ verify(datasize < sizeof(buffer));
+ execute(receive_line(buffer, sizeof(buffer)));
+ verify(strncmp(buffer, "VALUE ", 6) == 0);
+ verify(strncmp(buffer + 6, key, strlen(key)) == 0);
+ char *ptr= buffer + 6 + strlen(key) + 1;
+ char *end;
+
+ unsigned long val= strtoul(ptr, &end, 10); /* flags */
+ verify(ptr != end);
+ verify(val == 0);
+ verify(end != NULL);
+ val= strtoul(end, &end, 10); /* size */
+ verify(ptr != end);
+ verify(val == datasize);
+ verify(end != NULL);
+ *cas= strtoul(end, &end, 10); /* cas */
+ verify(ptr != end);
+ verify(val == datasize);
+ verify(end != NULL);
+
+ while (*end != '\n' && isspace(*end))
+ ++end;
+ verify(*end == '\n');
+
+ execute(retry_read(buffer, datasize));
+ verify(memcmp(buffer, value, datasize) == 0);
+
+ execute(retry_read(buffer, 2));
+ verify(memcmp(buffer, "\r\n", 2) == 0);
+
+ return TEST_PASS;
+}
+
+static enum test_return ascii_gets_item(const char *key, const char *value,
+ bool exist, unsigned long *cas)
+{
+ char buffer[1024];
+ size_t datasize= 0;
+ if (value != NULL)
+ datasize= strlen(value);
+
+ verify(datasize < sizeof(buffer));
+ sprintf(buffer, "gets %s\r\n", key);
+ execute(send_string(buffer));
+
+ if (exist)
+ execute(ascii_gets_value(key, value, cas));
+
+ execute(retry_read(buffer, 5));
+ verify(memcmp(buffer, "END\r\n", 5) == 0);
+
+ return TEST_PASS;
+}
+
+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);
+ execute(send_string(buffer));
+ execute(retry_write(value, len));
+ execute(send_string("\r\n"));
+ execute(receive_response("STORED\r\n"));
+ return TEST_PASS;
+}
+
+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" : "");
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("NOT_STORED\r\n"));
+
+ execute(ascii_set_item(key, "value"));
+ execute(ascii_get_item(key, "value", true));
+
+
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("STORED\r\n"));
+
+ return test_ascii_version();
+}
+
+static enum test_return test_ascii_replace(void)
+{
+ return test_ascii_replace_impl("test_ascii_replace", false);
+}
+
+static enum test_return test_ascii_replace_noreply(void)
+{
+ return test_ascii_replace_impl("test_ascii_replace_noreply", true);
+}
+
+static enum test_return test_ascii_cas_impl(const char* key, bool noreply)
+{
+ char buffer[1024];
+ unsigned long cas;
+
+ 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" : "");
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("STORED\r\n"));
+
+ /* reexecute the same command should fail due to illegal cas */
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("EXISTS\r\n"));
+
+ return test_ascii_version();
+}
+
+static enum test_return test_ascii_cas(void)
+{
+ return test_ascii_cas_impl("test_ascii_cas", false);
+}
+
+static enum test_return test_ascii_cas_noreply(void)
+{
+ return test_ascii_cas_impl("test_ascii_cas_noreply", true);
+}
+
+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"));
+ /* BUG: the server accepts delete a b */
+ execute(send_string("delete a b c d e\r\n"));
+ execute(receive_response("ERROR\r\n"));
+
+ char buffer[1024];
+ sprintf(buffer, "delete %s%s\r\n", key, noreply ? " noreply" : "");
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("DELETED\r\n"));
+
+ execute(ascii_get_item(key, "value", false));
+ execute(send_string(buffer));
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("NOT_FOUND\r\n"));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_delete(void)
+{
+ return test_ascii_delete_impl("test_ascii_delete", false);
+}
+
+static enum test_return test_ascii_delete_noreply(void)
+{
+ return test_ascii_delete_impl("test_ascii_delete_noreply", true);
+}
+
+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(ascii_get_item("test_ascii_get", "value", true));
+ execute(ascii_get_item("test_ascii_get_notfound", "value", false));
+
+ return TEST_PASS;
+}
+
+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"));
+ unsigned long cas;
+ execute(ascii_gets_item("test_ascii_gets", "value", true, &cas));
+ execute(ascii_gets_item("test_ascii_gets_notfound", "value", false, &cas));
+
+ return TEST_PASS;
+}
+
+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"));
+
+ 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 buffer[5];
+ execute(retry_read(buffer, 5));
+ verify(memcmp(buffer, "END\r\n", 5) == 0);
+ 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" : "");
+
+ execute(ascii_set_item(key, "0"));
+ for (int x= 1; x < 11; ++x)
+ {
+ execute(send_string(cmd));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ {
+ char buffer[80];
+ execute(receive_line(buffer, sizeof(buffer)));
+ int val= atoi(buffer);
+ verify(val == x);
+ }
+ }
+
+ execute(ascii_get_item(key, "10", true));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_incr(void)
+{
+ return test_ascii_incr_impl("test_ascii_incr", false);
+}
+
+static enum test_return test_ascii_incr_noreply(void)
+{
+ return test_ascii_incr_impl("test_ascii_incr_noreply", true);
+}
+
+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" : "");
+
+ execute(ascii_set_item(key, "9"));
+ for (int x= 8; x > -1; --x)
+ {
+ execute(send_string(cmd));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ {
+ char buffer[80];
+ execute(receive_line(buffer, sizeof(buffer)));
+ int val= atoi(buffer);
+ verify(val == x);
+ }
+ }
+
+ execute(ascii_get_item(key, "0", true));
+
+ /* verify that it doesn't wrap */
+ execute(send_string(cmd));
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ {
+ char buffer[80];
+ execute(receive_line(buffer, sizeof(buffer)));
+ }
+ execute(ascii_get_item(key, "0", true));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_decr(void)
+{
+ return test_ascii_decr_impl("test_ascii_decr", false);
+}
+
+static enum test_return test_ascii_decr_noreply(void)
+{
+ return test_ascii_decr_impl("test_ascii_decr_noreply", true);
+}
+
+
+static enum test_return test_ascii_flush_impl(const char *key, bool noreply)
+{
+#if 0
+ /* 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"));
+#endif
+
+ execute(ascii_set_item(key, key));
+ execute(ascii_get_item(key, key, true));
+
+ if (noreply)
+ {
+ execute(send_string("flush_all noreply\r\n"));
+ execute(test_ascii_version());
+ }
+ else
+ {
+ execute(send_string("flush_all\r\n"));
+ execute(receive_response("OK\r\n"));
+ }
+
+ execute(ascii_get_item(key, key, false));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_flush(void)
+{
+ return test_ascii_flush_impl("test_ascii_flush", false);
+}
+
+static enum test_return test_ascii_flush_noreply(void)
+{
+ return test_ascii_flush_impl("test_ascii_flush_noreply", true);
+}
+
+static enum test_return test_ascii_concat_impl(const char *key,
+ bool append,
+ bool noreply)
+{
+ const char *value;
+
+ if (append)
+ value="hello";
+ else
+ value=" world";
+
+ execute(ascii_set_item(key, value));
+
+ if (append)
+ value=" world";
+ else
+ 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);
+ execute(send_string(cmd));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("STORED\r\n"));
+
+ 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);
+ execute(send_string(cmd));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("NOT_STORED\r\n"));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_append(void)
+{
+ return test_ascii_concat_impl("test_ascii_append", true, false);
+}
+
+static enum test_return test_ascii_prepend(void)
+{
+ return test_ascii_concat_impl("test_ascii_prepend", false, false);
+}
+
+static enum test_return test_ascii_append_noreply(void)
+{
+ return test_ascii_concat_impl("test_ascii_append_noreply", true, true);
+}
+
+static enum test_return test_ascii_prepend_noreply(void)
+{
+ return test_ascii_concat_impl("test_ascii_prepend_noreply", false, true);
+}
+
+static enum test_return test_ascii_stat(void)
+{
+ execute(send_string("stats noreply\r\n"));
+ execute(receive_response("ERROR\r\n"));
+ execute(send_string("stats\r\n"));
+ char buffer[1024];
+ do {
+ execute(receive_line(buffer, sizeof(buffer)));
+ } while (strcmp(buffer, "END\r\n") != 0);
+
+ return TEST_PASS_RECONNECT;
+}
+