summaryrefslogtreecommitdiff
path: root/auto_tests/friends_test.c
blob: 2448f97c34aed30105a384a9fedc34ec5fd9eaad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/* Unit testing for friend requests, statuses, and messages.
 *  Purpose: Check that messaging functions actually do what
 *          they're supposed to by setting up two local clients.
 *
 *  Design: (Subject to change.)
 *      1. Parent sends a friend request, and waits for a response.
 *          It it doesn't get one, it kills the child.
 *      2. Child gets friend request, accepts, then waits for a status change.
 *      3. The parent waits on a status change, killing the child if it takes
 *          too long.
 *      4. The child gets the status change, then sends a message. After that,
 *          it returns. If if doesn't get the status change, it just loops forever.
 *      5. After getting the status change, the parent waits for a message, on getting
 *          one, it waits on the child to return, then returns 0.
 *
 *  Note about "waiting":
 *      Wait time is decided by WAIT_COUNT and WAIT_TIME. c_sleep(WAIT_TIME) WAIT_COUNT
 *      times. This is used both to ensure that we don't loop forever on a broken build,
 *      and that we don't get too slow with messaging. The current time is 15 seconds. */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "../toxcore/friend_requests.h"
#include "../toxcore/Messenger.h"
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>
#include <sys/wait.h>

#define WAIT_COUNT 30
#define WAIT_TIME 500

#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif

/* first step, second step */
#define FIRST_FLAG 0x1
#define SECOND_FLAG 0x2

/* ensure that we sleep in milliseconds */
#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
#define c_sleep(x) Sleep(x)
#else
#define c_sleep(x) usleep(1000*x)
#endif

#define PORT 33445

static Messenger *m;

uint8_t *parent_id = NULL;
uint8_t *child_id = NULL;

pid_t child_pid = 0;
int request_flags = 0;

void do_tox(DHT *dht)
{
    static int dht_on = 0;

    if (!dht_on && DHT_isconnected(dht)) {
        dht_on = 1;
    } else if (dht_on && !DHT_isconnected(dht)) {
        dht_on = 0;
    }

    doMessenger(m);
}

void parent_confirm_message(Messenger *m, int num, uint8_t *data, uint16_t length, void *userdata)
{
    puts("OK");
    request_flags |= SECOND_FLAG;
}

void parent_confirm_status(Messenger *m, int num, uint8_t *data, uint16_t length, void *userdata)
{
    puts("OK");
    request_flags |= FIRST_FLAG;
}

int parent_friend_request(DHT *dht)
{
    char *message = "Watson, come here, I need you.";
    int len = strlen(message);
    int i = 0;

    fputs("Sending child request.", stdout);
    fflush(stdout);

    m_addfriend(m, child_id, (uint8_t *)message, len);

    /* wait on the status change */
    for (i = 0; i < WAIT_COUNT; i++) {
        do_tox(dht);

        if (request_flags & FIRST_FLAG)
            break;

        fputs(".", stdout);
        fflush(stdout);
        c_sleep(WAIT_TIME);
    }

    if (!(request_flags & FIRST_FLAG)) {
        fputs("\nfriends_test: The child took to long to respond!\n"
              "Friend requests may be broken, failing build!\n", stderr);
        kill(child_pid, SIGKILL);
        return -1;
    }

    return 0;
}

void child_got_request(Messenger *m, uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
{
    fputs("OK\nsending status to parent", stdout);
    fflush(stdout);
    m_addfriend_norequest(m, public_key);
    request_flags |= FIRST_FLAG;
}

void child_got_statuschange(Messenger *m, int friend_num, uint8_t *string, uint16_t length, void *userdata)
{
    request_flags |= SECOND_FLAG;
}

int parent_wait_for_message(DHT *dht)
{
    int i = 0;

    fputs("Parent waiting for message.", stdout);
    fflush(stdout);

    for (i = 0; i < WAIT_COUNT; i++) {
        do_tox(dht);

        if (request_flags & SECOND_FLAG)
            break;

        fputs(".", stdout);
        fflush(stdout);
        c_sleep(WAIT_TIME);
    }

    if (!(request_flags & SECOND_FLAG)) {
        fputs("\nParent hasn't received the message yet!\n"
              "Messaging may be broken, failing the build!\n", stderr);
        kill(child_pid, SIGKILL);
        return -1;
    }

    return 0;
}

void cleanup(void)
{
    munmap(parent_id, crypto_box_PUBLICKEYBYTES);
    munmap(child_id, crypto_box_PUBLICKEYBYTES);
    puts("============= END TEST =============");
}

int main(int argc, char *argv[])
{
    puts("=========== FRIENDS_TEST ===========");

    /* set up the global memory */
    parent_id = mmap(NULL, crypto_box_PUBLICKEYBYTES, PROT_READ | PROT_WRITE,
                     MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    child_id = mmap(NULL, crypto_box_PUBLICKEYBYTES, PROT_READ | PROT_WRITE,
                    MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    fputs("friends_test: Starting test...\n", stdout);

    if ((child_pid = fork()) == 0) {
        /* child */
        int i = 0;
        char *message = "Y-yes Mr. Watson?";

        m = initMessenger();

        Messenger_save(m, child_id);
        msync(child_id, crypto_box_PUBLICKEYBYTES, MS_SYNC);

        m_callback_friendrequest(m, child_got_request, NULL);
        m_callback_statusmessage(m, child_got_statuschange, NULL);

        /* wait on the friend request */
        while (!(request_flags & FIRST_FLAG))
            do_tox(m->dht);

        /* wait for the status change */
        while (!(request_flags & SECOND_FLAG))
            do_tox(m->dht);

        for (i = 0; i < 6; i++) {
            /* send the message six times, just to be sure */
            m_sendmessage(m, 0, (uint8_t *)message, strlen(message));
            do_tox(m->dht);
        }

        cleanupMessenger(m);

        return 0;
    }

    /* parent */
    if (atexit(cleanup) != 0) {
        fputs("friends_test: atexit() failed!\nFailing build...\n", stderr);
        kill(child_pid, SIGKILL);
        return -1;
    }

    m = initMessenger();

    msync(parent_id, crypto_box_PUBLICKEYBYTES, MS_SYNC);
    m_callback_statusmessage(m, parent_confirm_status, NULL);
    m_callback_friendmessage(m, parent_confirm_message, NULL);

    /* hacky way to give the child time to set up */
    c_sleep(50);

    Messenger_save(m, parent_id);

    if (parent_friend_request(m->dht) == -1)
        return -1;

    if (parent_wait_for_message(m->dht) == -1)
        return -1;

    wait(NULL);
    fputs("friends_test: Build passed!\n", stdout);
    return 0;
}