166eb94d0SMike Wiederhold/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
266eb94d0SMike Wiederhold/*
366eb94d0SMike Wiederhold *     Copyright 2010 Couchbase, Inc
466eb94d0SMike Wiederhold *
566eb94d0SMike Wiederhold *   Licensed under the Apache License, Version 2.0 (the "License");
666eb94d0SMike Wiederhold *   you may not use this file except in compliance with the License.
766eb94d0SMike Wiederhold *   You may obtain a copy of the License at
866eb94d0SMike Wiederhold *
966eb94d0SMike Wiederhold *       http://www.apache.org/licenses/LICENSE-2.0
1066eb94d0SMike Wiederhold *
1166eb94d0SMike Wiederhold *   Unless required by applicable law or agreed to in writing, software
1266eb94d0SMike Wiederhold *   distributed under the License is distributed on an "AS IS" BASIS,
1366eb94d0SMike Wiederhold *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1466eb94d0SMike Wiederhold *   See the License for the specific language governing permissions and
1566eb94d0SMike Wiederhold *   limitations under the License.
1666eb94d0SMike Wiederhold */
1766eb94d0SMike Wiederhold
18a5899de1SDustin Sallings#include <stdlib.h>
19a5899de1SDustin Sallings#include <string.h>
2046433b61SDustin Sallings#include <arpa/inet.h>
21a5899de1SDustin Sallings
22f8c7c7eaSDustin Sallings#include <memcached/engine.h>
23f8c7c7eaSDustin Sallings
24a5899de1SDustin Sallings#include "suite_stubs.h"
25bf3b555bSMike Wiederhold#include "ep-engine/command_ids.h"
26a5899de1SDustin Sallings
27f8c7c7eaSDustin Sallingsint locktime = 30;
28451bfa4bSDustin Sallingsint expiry = 3600;
29451bfa4bSDustin Sallingsbool hasError = false;
30c5fe806aSTrond Norbyeuint64_t cas = (((uint64_t)1) << 31);
31a5899de1SDustin Sallingsstruct test_harness testHarness;
32f8c7c7eaSDustin Sallingsprotocol_binary_response_status last_status = 0;
33a5899de1SDustin Sallings
34a5899de1SDustin Sallingsstatic const char *key = "key";
35a5899de1SDustin Sallings
3627a41de2SDustin Sallingsstatic void clearCAS(void) {
37c5fe806aSTrond Norbye   cas = (((uint64_t)1) << 31);
3827a41de2SDustin Sallings}
3927a41de2SDustin Sallings
40a5899de1SDustin Sallingsbool teardown(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
41a5899de1SDustin Sallings    (void)h; (void)h1;
4227a41de2SDustin Sallings    clearCAS();
43a5899de1SDustin Sallings    return true;
44a5899de1SDustin Sallings}
45a5899de1SDustin Sallings
46a5899de1SDustin Sallingsvoid delay(int amt) {
47a5899de1SDustin Sallings    testHarness.time_travel(amt);
48a5899de1SDustin Sallings    hasError = false;
49a5899de1SDustin Sallings}
50a5899de1SDustin Sallings
51c85eb519SDustin Sallingsstatic bool add_response(const void *k, uint16_t keylen,
52c85eb519SDustin Sallings                         const void *ext, uint8_t extlen,
53c85eb519SDustin Sallings                         const void *body, uint32_t bodylen,
54c85eb519SDustin Sallings                         uint8_t datatype, uint16_t status,
55c85eb519SDustin Sallings                         uint64_t pcas, const void *cookie) {
56c85eb519SDustin Sallings    (void)k;
57c85eb519SDustin Sallings    (void)keylen;
58c85eb519SDustin Sallings    (void)ext;
59c85eb519SDustin Sallings    (void)extlen;
60c85eb519SDustin Sallings    (void)body;
61c85eb519SDustin Sallings    (void)bodylen;
62c85eb519SDustin Sallings    (void)datatype;
63c85eb519SDustin Sallings    (void)pcas;
64c85eb519SDustin Sallings    (void)cookie;
65c85eb519SDustin Sallings
66c85eb519SDustin Sallings    last_status = status;
67c85eb519SDustin Sallings
68c85eb519SDustin Sallings    return true;
69c85eb519SDustin Sallings}
70c85eb519SDustin Sallings
71a5899de1SDustin Sallingsstatic void storeItem(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
72e6712394SDustin Sallings                      ENGINE_STORE_OPERATION op, bool rememberCAS,
73e6712394SDustin Sallings                      uint64_t usingCASID) {
74a5899de1SDustin Sallings    item *it = NULL;
75e6712394SDustin Sallings    uint64_t mycas = 0;
76a5899de1SDustin Sallings    char *value = "0";
77a5899de1SDustin Sallings    const int flags = 0;
78a5899de1SDustin Sallings    const void *cookie = NULL;
79dc4753dbSTrond Norbye    size_t vlen;
80dc4753dbSTrond Norbye    ENGINE_ERROR_CODE rv;
81dc4753dbSTrond Norbye    item_info info;
82a5899de1SDustin Sallings
83a5899de1SDustin Sallings    if (op == OPERATION_APPEND) {
84a5899de1SDustin Sallings        value = "-suffix";
85a5899de1SDustin Sallings    } else if (op == OPERATION_PREPEND) {
86a5899de1SDustin Sallings        value = "prefix-";
87a5899de1SDustin Sallings    }
88a5899de1SDustin Sallings
89dc4753dbSTrond Norbye    vlen = strlen(value);
90a5899de1SDustin Sallings    rv = h1->allocate(h, cookie, &it,
91a5899de1SDustin Sallings                      key, strlen(key),
92a5899de1SDustin Sallings                      vlen, flags, expiry);
93c649b2d9STrond Norbye    cb_assert(rv == ENGINE_SUCCESS);
94a5899de1SDustin Sallings
95a5899de1SDustin Sallings    info.nvalue = 1;
96a5899de1SDustin Sallings    if (!h1->get_item_info(h, cookie, it, &info)) {
97a5899de1SDustin Sallings        abort();
98a5899de1SDustin Sallings    }
99a5899de1SDustin Sallings
100a5899de1SDustin Sallings    memcpy(info.value[0].iov_base, value, vlen);
101e6712394SDustin Sallings    h1->item_set_cas(h, cookie, it, usingCASID);
102a5899de1SDustin Sallings
103e6712394SDustin Sallings    rv = h1->store(h, cookie, it, (rememberCAS ? &cas : &mycas), op, 0);
104a5899de1SDustin Sallings
105a5899de1SDustin Sallings    hasError = rv != ENGINE_SUCCESS;
106e6712394SDustin Sallings
107dc4753dbSTrond Norbye    /* If we changed the CAS, make sure we don't know it. */
108e6712394SDustin Sallings    if (!hasError && !rememberCAS) {
10927a41de2SDustin Sallings        clearCAS();
110e6712394SDustin Sallings    }
111c649b2d9STrond Norbye    cb_assert(cas != 0);
112a5899de1SDustin Sallings}
113a5899de1SDustin Sallings
114a5899de1SDustin Sallingsvoid add(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
115e6712394SDustin Sallings    storeItem(h, h1, OPERATION_ADD, false, 0);
116a5899de1SDustin Sallings}
117a5899de1SDustin Sallings
118a5899de1SDustin Sallingsvoid append(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
119e6712394SDustin Sallings    storeItem(h, h1, OPERATION_APPEND, false, 0);
120a5899de1SDustin Sallings}
121a5899de1SDustin Sallings
12223fcbda4SDustin Sallingsvoid appendUsingCAS(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
12323fcbda4SDustin Sallings    storeItem(h, h1, OPERATION_APPEND, false, cas);
12423fcbda4SDustin Sallings}
12523fcbda4SDustin Sallings
126a5899de1SDustin Sallingsvoid decr(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
127e6712394SDustin Sallings    uint64_t mycas;
128a5899de1SDustin Sallings    uint64_t result;
12927a41de2SDustin Sallings    clearCAS();
130a5899de1SDustin Sallings    hasError = h1->arithmetic(h, NULL, key, strlen(key), false, false, 1, 0, expiry,
131e6712394SDustin Sallings                              &mycas, &result,
132a5899de1SDustin Sallings                              0) != ENGINE_SUCCESS;
133a5899de1SDustin Sallings}
134a5899de1SDustin Sallings
135a5899de1SDustin Sallingsvoid decrWithDefault(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
136e6712394SDustin Sallings    uint64_t mycas;
137a5899de1SDustin Sallings    uint64_t result;
13827a41de2SDustin Sallings    clearCAS();
139a5899de1SDustin Sallings    hasError = h1->arithmetic(h, NULL, key, strlen(key), false, true, 1, 0, expiry,
140e6712394SDustin Sallings                              &mycas, &result,
141a5899de1SDustin Sallings                              0) != ENGINE_SUCCESS;
142a5899de1SDustin Sallings}
143a5899de1SDustin Sallings
144a5899de1SDustin Sallingsvoid prepend(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
145e6712394SDustin Sallings    storeItem(h, h1, OPERATION_PREPEND, false, 0);
146a5899de1SDustin Sallings}
147a5899de1SDustin Sallings
14823fcbda4SDustin Sallingsvoid prependUsingCAS(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
14923fcbda4SDustin Sallings    storeItem(h, h1, OPERATION_PREPEND, false, cas);
15023fcbda4SDustin Sallings}
15123fcbda4SDustin Sallings
152a5899de1SDustin Sallingsvoid flush(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
153a5899de1SDustin Sallings    hasError = h1->flush(h, NULL, 0);
154a5899de1SDustin Sallings}
155a5899de1SDustin Sallings
156a5899de1SDustin Sallingsvoid del(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
157a5899de1SDustin Sallings    hasError = h1->remove(h, NULL, key, strlen(key), 0, 0) != ENGINE_SUCCESS;
158a5899de1SDustin Sallings}
159a5899de1SDustin Sallings
16027a41de2SDustin Sallingsvoid deleteUsingCAS(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
161d8b5023cSTrond Norbye    hasError = h1->remove(h, NULL, key, strlen(key), &cas, 0) != ENGINE_SUCCESS;
16227a41de2SDustin Sallings}
16327a41de2SDustin Sallings
164a5899de1SDustin Sallingsvoid set(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
165e6712394SDustin Sallings    storeItem(h, h1, OPERATION_SET, false, 0);
166e6712394SDustin Sallings}
167e6712394SDustin Sallings
168e6712394SDustin Sallingsvoid setUsingCAS(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
169e6712394SDustin Sallings    storeItem(h, h1, OPERATION_SET, false, cas);
170e6712394SDustin Sallings}
171e6712394SDustin Sallings
172e6712394SDustin Sallingsvoid setRetainCAS(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
173e6712394SDustin Sallings    storeItem(h, h1, OPERATION_SET, true, 0);
174a5899de1SDustin Sallings}
175a5899de1SDustin Sallings
176a5899de1SDustin Sallingsvoid incr(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
177e6712394SDustin Sallings    uint64_t mycas;
178a5899de1SDustin Sallings    uint64_t result;
179a5899de1SDustin Sallings    hasError = h1->arithmetic(h, NULL, key, strlen(key), true, false, 1, 0, expiry,
180e6712394SDustin Sallings                              &mycas, &result,
181a5899de1SDustin Sallings                              0) != ENGINE_SUCCESS;
182a5899de1SDustin Sallings}
183a5899de1SDustin Sallings
184a5899de1SDustin Sallingsvoid incrWithDefault(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
185e6712394SDustin Sallings    uint64_t mycas;
186a5899de1SDustin Sallings    uint64_t result;
187a5899de1SDustin Sallings    hasError = h1->arithmetic(h, NULL, key, strlen(key), true, true, 1, 0, expiry,
188e6712394SDustin Sallings                              &mycas, &result,
189a5899de1SDustin Sallings                              0) != ENGINE_SUCCESS;
190a5899de1SDustin Sallings}
191a5899de1SDustin Sallings
1921d6f347cSDustin Sallingsvoid get(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
1931d6f347cSDustin Sallings    item *i = NULL;
1941d6f347cSDustin Sallings    ENGINE_ERROR_CODE rv = h1->get(h, NULL, &i, key, strlen(key), 0);
1951d6f347cSDustin Sallings    hasError = rv != ENGINE_SUCCESS;
1961d6f347cSDustin Sallings}
197a5899de1SDustin Sallings
198a5899de1SDustin Sallingsvoid checkValue(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1, const char* exp) {
199a5899de1SDustin Sallings    item *i = NULL;
200dc4753dbSTrond Norbye    item_info info;
201dc4753dbSTrond Norbye    char *buf;
202a5899de1SDustin Sallings    ENGINE_ERROR_CODE rv = h1->get(h, NULL, &i, key, strlen(key), 0);
203c649b2d9STrond Norbye    cb_assert(rv == ENGINE_SUCCESS);
204a5899de1SDustin Sallings
205a5899de1SDustin Sallings    info.nvalue = 1;
206a5899de1SDustin Sallings    h1->get_item_info(h, NULL, i, &info);
207a5899de1SDustin Sallings
208dc4753dbSTrond Norbye    buf = malloc(info.value[0].iov_len + 1);
209c649b2d9STrond Norbye    cb_assert(buf != NULL);
210a5899de1SDustin Sallings    memcpy(buf, info.value[0].iov_base, info.value[0].iov_len);
211a5899de1SDustin Sallings    buf[sizeof(buf) - 1] = 0x00;
212a5899de1SDustin Sallings    if (buf[strlen(buf) - 1] == '\n') {
213a5899de1SDustin Sallings        buf[strlen(buf) - 1] = 0x00;
214a5899de1SDustin Sallings        if (buf[strlen(buf) - 1] == '\r') {
215a5899de1SDustin Sallings            buf[strlen(buf) - 1] = 0x00;
216a5899de1SDustin Sallings        }
217a5899de1SDustin Sallings    }
218a5899de1SDustin Sallings
219c649b2d9STrond Norbye    cb_assert(info.nvalue == 1);
220a5899de1SDustin Sallings    if (strlen(exp) > info.value[0].iov_len) {
221a5899de1SDustin Sallings        fprintf(stderr, "Expected at least %d bytes for ``%s'', got %d as ``%s''\n",
222a5899de1SDustin Sallings                (int)strlen(exp), exp, (int)info.value[0].iov_len, buf);
223a5899de1SDustin Sallings        abort();
224a5899de1SDustin Sallings    }
225a5899de1SDustin Sallings
226a5899de1SDustin Sallings    if (memcmp(info.value[0].iov_base, exp, strlen(exp)) != 0) {
227a5899de1SDustin Sallings        fprintf(stderr, "Expected ``%s'', got ``%s''\n", exp, buf);
228a5899de1SDustin Sallings        abort();
229a5899de1SDustin Sallings    }
230dc4753dbSTrond Norbye    free(buf);
231a5899de1SDustin Sallings}
232a5899de1SDustin Sallings
233f8c7c7eaSDustin Sallingsstatic protocol_binary_request_header* create_packet(uint8_t opcode,
234f8c7c7eaSDustin Sallings                                                     const char *val) {
235f8c7c7eaSDustin Sallings    char *pkt_raw = calloc(1,
236f8c7c7eaSDustin Sallings                           sizeof(protocol_binary_request_header)
237f8c7c7eaSDustin Sallings                           + strlen(key)
238f8c7c7eaSDustin Sallings                           + strlen(val));
239dc4753dbSTrond Norbye    protocol_binary_request_header *req = (void*)pkt_raw;
240c649b2d9STrond Norbye    cb_assert(pkt_raw);
241f8c7c7eaSDustin Sallings    req->request.opcode = opcode;
242f8c7c7eaSDustin Sallings    req->request.bodylen = htonl(strlen(key) + strlen(val));
243f8c7c7eaSDustin Sallings    req->request.keylen = htons(strlen(key));
244f8c7c7eaSDustin Sallings    memcpy(pkt_raw + sizeof(protocol_binary_request_header),
245f8c7c7eaSDustin Sallings           key, strlen(key));
246f8c7c7eaSDustin Sallings    memcpy(pkt_raw + sizeof(protocol_binary_request_header) + strlen(key),
247f8c7c7eaSDustin Sallings           val, strlen(val));
248f8c7c7eaSDustin Sallings    return req;
249f8c7c7eaSDustin Sallings}
250f8c7c7eaSDustin Sallings
251f8c7c7eaSDustin Sallingsvoid getLock(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
252f8c7c7eaSDustin Sallings    uint16_t vbucketId = 0;
253f8c7c7eaSDustin Sallings
254f8c7c7eaSDustin Sallings    protocol_binary_request_header *pkt = create_packet(CMD_GET_LOCKED, "");
255f8c7c7eaSDustin Sallings    pkt->request.vbucket = htons(vbucketId);
256f8c7c7eaSDustin Sallings
257f8c7c7eaSDustin Sallings    if (h1->unknown_command(h, NULL, pkt, add_response) != ENGINE_SUCCESS) {
258f8c7c7eaSDustin Sallings        fprintf(stderr, "Failed to issue getl request.\n");
259f8c7c7eaSDustin Sallings        abort();
260f8c7c7eaSDustin Sallings    }
261f8c7c7eaSDustin Sallings
262f8c7c7eaSDustin Sallings    hasError = last_status != 0;
263f8c7c7eaSDustin Sallings}
264f8c7c7eaSDustin Sallings
265f8c7c7eaSDustin Sallings
266a5899de1SDustin Sallingsvoid assertNotExists(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
267a5899de1SDustin Sallings    item *i;
268a5899de1SDustin Sallings    ENGINE_ERROR_CODE rv = h1->get(h, NULL, &i, key, strlen(key), 0);
269c649b2d9STrond Norbye    cb_assert(rv == ENGINE_KEY_ENOENT);
270a5899de1SDustin Sallings}
271a5899de1SDustin Sallings
272a5899de1SDustin SallingsMEMCACHED_PUBLIC_API
273a5899de1SDustin Sallingsbool setup_suite(struct test_harness *th) {
274a5899de1SDustin Sallings    testHarness = *th;
275a5899de1SDustin Sallings    return true;
276a5899de1SDustin Sallings}
27746433b61SDustin Sallings
27846433b61SDustin Sallingsstatic int test_compare(const void *av, const void *bv) {
27946433b61SDustin Sallings    const engine_test_t *a = av;
28046433b61SDustin Sallings    const engine_test_t *b = bv;
28146433b61SDustin Sallings    return strcmp(a->name, b->name);
28246433b61SDustin Sallings}
28346433b61SDustin Sallings
28446433b61SDustin Sallings#define NSEGS 10
28546433b61SDustin Sallings
286dc4753dbSTrond Norbye/* This is basically a really late linker, but it makes separating the */
287dc4753dbSTrond Norbye/* test thing into multiple compilation units possible. */
28846433b61SDustin SallingsMEMCACHED_PUBLIC_API
28946433b61SDustin Sallingsengine_test_t* get_tests(void) {
29046433b61SDustin Sallings    engine_test_t* testsegs[NSEGS];
29146433b61SDustin Sallings    engine_test_t* rv = NULL;
29246433b61SDustin Sallings    int i = 0, j = 0;
29346433b61SDustin Sallings    size_t num_tests = 0, pos = 0;
29446433b61SDustin Sallings
29546433b61SDustin Sallings    testsegs[i++] = get_tests_0();
29646433b61SDustin Sallings    testsegs[i++] = get_tests_1();
29746433b61SDustin Sallings    testsegs[i++] = get_tests_2();
29846433b61SDustin Sallings    testsegs[i++] = get_tests_3();
29946433b61SDustin Sallings    testsegs[i++] = get_tests_4();
30046433b61SDustin Sallings    testsegs[i++] = get_tests_5();
30146433b61SDustin Sallings    testsegs[i++] = get_tests_6();
30246433b61SDustin Sallings    testsegs[i++] = get_tests_7();
30346433b61SDustin Sallings    testsegs[i++] = get_tests_8();
30446433b61SDustin Sallings    testsegs[i++] = get_tests_9();
305c649b2d9STrond Norbye    cb_assert(i == NSEGS);
30646433b61SDustin Sallings
30746433b61SDustin Sallings    for (i = 0; i < NSEGS; ++i) {
30846433b61SDustin Sallings        for (j = 0; testsegs[i][j].name; ++j) {
30946433b61SDustin Sallings            ++num_tests;
31046433b61SDustin Sallings        }
31146433b61SDustin Sallings    }
31246433b61SDustin Sallings
31346433b61SDustin Sallings    rv = calloc(num_tests+1, sizeof(engine_test_t));
314c649b2d9STrond Norbye    cb_assert(rv);
31546433b61SDustin Sallings
31646433b61SDustin Sallings    for (i = 0; i < NSEGS; ++i) {
31746433b61SDustin Sallings        for (j = 0; testsegs[i][j].name; ++j) {
31846433b61SDustin Sallings            rv[pos++] = testsegs[i][j];
31946433b61SDustin Sallings        }
32046433b61SDustin Sallings    }
32146433b61SDustin Sallings
32246433b61SDustin Sallings    qsort(rv, num_tests, sizeof(engine_test_t), test_compare);
32346433b61SDustin Sallings
32446433b61SDustin Sallings    return rv;
32546433b61SDustin Sallings}