| Index: app/tests/port_tests.c
|
| diff --git a/app/tests/port_tests.c b/app/tests/port_tests.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..447c5d236e70e447c645aff2e9b4630c97ea9580
|
| --- /dev/null
|
| +++ b/app/tests/port_tests.c
|
| @@ -0,0 +1,422 @@
|
| +/*
|
| + * Copyright (c) 2015 Carlos Pizano-Uribe cpu@chromium.org
|
| + *
|
| + * Permission is hereby granted, free of charge, to any person obtaining
|
| + * a copy of this software and associated documentation files
|
| + * (the "Software"), to deal in the Software without restriction,
|
| + * including without limitation the rights to use, copy, modify, merge,
|
| + * publish, distribute, sublicense, and/or sell copies of the Software,
|
| + * and to permit persons to whom the Software is furnished to do so,
|
| + * subject to the following conditions:
|
| + *
|
| + * The above copyright notice and this permission notice shall be
|
| + * included in all copies or substantial portions of the Software.
|
| + *
|
| + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
| + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
| + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
| + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
| + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
| + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
| + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| + */
|
| +
|
| +#include <debug.h>
|
| +#include <err.h>
|
| +#include <string.h>
|
| +#include <rand.h>
|
| +
|
| +#include <kernel/port.h>
|
| +#include <kernel/thread.h>
|
| +
|
| +#include <platform.h>
|
| +
|
| +void* context1 = (void*) 0x53;
|
| +
|
| +static void dump_port_result(const port_result_t* result)
|
| +{
|
| + const port_packet_t* p = &result->packet;
|
| + printf("[%02x %02x %02x %02x %02x %02x %02x %02x]\n",
|
| + p->value[0], p->value[1], p->value[2], p->value[3],
|
| + p->value[4], p->value[5], p->value[6], p->value[7]);
|
| +}
|
| +
|
| +static int single_thread_basic(void)
|
| +{
|
| + port_t w_port;
|
| + status_t st = port_create("sh_prt1", PORT_MODE_UNICAST, &w_port);
|
| + if (st < 0) {
|
| + printf("could not create port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + port_t r_port;
|
| + st = port_open("sh_prt0", context1, &r_port);
|
| + if (st != ERR_NOT_FOUND) {
|
| + printf("expected not to find port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_open("sh_prt1", context1, &r_port);
|
| + if (st < 0) {
|
| + printf("could not open port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| +
|
| + port_packet_t packet[3] = {
|
| + {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}},
|
| + {{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11}},
|
| + {{0x33, 0x66, 0x99, 0xcc, 0x33, 0x66, 0x99, 0xcc}},
|
| + };
|
| +
|
| + st = port_write(w_port, &packet[0], 1);
|
| + if (st < 0) {
|
| + printf("could not write port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + printf("reading from port:\n");
|
| +
|
| + port_result_t res = {0};
|
| +
|
| + st = port_read(r_port, 0, &res);
|
| + if (st < 0) {
|
| + printf("could not read port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| + if (res.ctx != context1) {
|
| + printf("bad context! = %p\n", res.ctx);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_read(r_port, 0, &res);
|
| + if (st != ERR_TIMED_OUT) {
|
| + printf("expected timeout, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_write(w_port, &packet[1], 1);
|
| + if (st < 0) {
|
| + printf("could not write port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_write(w_port, &packet[0], 1);
|
| + if (st < 0) {
|
| + printf("could not write port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_write(w_port, &packet[2], 1);
|
| + if (st < 0) {
|
| + printf("could not write port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + int expected_count = 3;
|
| + while (true) {
|
| + st = port_read(r_port, 0, &res);
|
| + if (st < 0)
|
| + break;
|
| + dump_port_result(&res);
|
| + --expected_count;
|
| + }
|
| +
|
| + if (expected_count != 0) {
|
| + printf("invalid read count = %d\n", expected_count);
|
| + return __LINE__;
|
| + }
|
| +
|
| + printf("\n");
|
| +
|
| + // port should be empty. should be able to write 8 packets.
|
| + expected_count = 8;
|
| + while (true) {
|
| + st = port_write(w_port, &packet[1], 1);
|
| + if (st < 0)
|
| + break;
|
| + --expected_count;
|
| + st = port_write(w_port, &packet[2], 1);
|
| + if (st < 0)
|
| + break;
|
| + --expected_count;
|
| + }
|
| +
|
| + if (expected_count != 0) {
|
| + printf("invalid write count = %d\n", expected_count);
|
| + return __LINE__;
|
| + }
|
| +
|
| + // tod(cpu) fix this possibly wrong error.
|
| + if (st != ERR_PARTIAL_WRITE) {
|
| + printf("expected buffer error, status =%d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + // read 3 packets.
|
| + for (int ix = 0; ix != 3; ++ix) {
|
| + st = port_read(r_port, 0, &res);
|
| + if (st < 0) {
|
| + printf("could not read port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| + }
|
| +
|
| + // there are 5 packets, now we add another 3.
|
| + st = port_write(w_port, packet, 3);
|
| + if (st < 0) {
|
| + printf("could not write port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + expected_count = 8;
|
| + while (true) {
|
| + st = port_read(r_port, 0, &res);
|
| + if (st < 0)
|
| + break;
|
| + dump_port_result(&res);
|
| + --expected_count;
|
| + }
|
| +
|
| + if (expected_count != 0) {
|
| + printf("invalid read count = %d\n", expected_count);
|
| + return __LINE__;
|
| + }
|
| +
|
| + // attempt to use the wrong port.
|
| + st = port_write(r_port, &packet[1], 1);
|
| + if (st != ERR_BAD_HANDLE) {
|
| + printf("expected bad handle error, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_read(w_port, 0, &res);
|
| + if (st != ERR_BAD_HANDLE) {
|
| + printf("expected bad handle error, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_close(r_port);
|
| + if (st < 0) {
|
| + printf("could not close read port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_close(w_port);
|
| + if (st < 0) {
|
| + printf("could not close write port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_close(r_port);
|
| + if (st != ERR_BAD_HANDLE) {
|
| + printf("expected bad handle error, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_close(w_port);
|
| + if (st != ERR_BAD_HANDLE) {
|
| + printf("expected bad handle error, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_destroy(w_port);
|
| + if (st < 0) {
|
| + printf("could not destroy port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + printf("single_thread_basic : ok\n");
|
| + return 0;
|
| +}
|
| +
|
| +static int ping_pong_thread(void *arg)
|
| +{
|
| + port_t r_port;
|
| + status_t st = port_open("ping_port", NULL, &r_port);
|
| + if (st < 0) {
|
| + printf("thread: could not open port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + bool should_dispose_pong_port = true;
|
| + port_t w_port;
|
| + st = port_create("pong_port", PORT_MODE_UNICAST, &w_port);
|
| + if (st == ERR_ALREADY_EXISTS) {
|
| + // won the race to create the port.
|
| + should_dispose_pong_port = false;
|
| + } else if (st < 0) {
|
| + printf("thread: could not open port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + port_result_t pr;
|
| +
|
| + // the loop is read-mutate-write until the write port
|
| + // is closed by the master thread.
|
| + while (true) {
|
| + st = port_read(r_port, INFINITE_TIME, &pr);
|
| +
|
| + if (st == ERR_CANCELLED) {
|
| + break;
|
| + } else if (st < 0) {
|
| + printf("thread: could not read port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + pr.packet.value[0]++;
|
| + pr.packet.value[5]--;
|
| +
|
| + st = port_write(w_port, &pr.packet, 1);
|
| + if (st < 0) {
|
| + printf("thread: could not write port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| + }
|
| +
|
| + port_close(r_port);
|
| +
|
| + if (should_dispose_pong_port) {
|
| + port_close(w_port);
|
| + port_destroy(w_port);
|
| + }
|
| +
|
| + return 0;
|
| +
|
| +bail:
|
| + return __LINE__;
|
| +}
|
| +
|
| +
|
| +int two_threads_basic(void)
|
| +{
|
| + port_t w_port;
|
| + status_t st = port_create("ping_port", PORT_MODE_BROADCAST, &w_port);
|
| + if (st < 0) {
|
| + printf("could not create port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + thread_t* t1 = thread_create(
|
| + "worker1", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
|
| + thread_t* t2 = thread_create(
|
| + "worker2", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
|
| + thread_resume(t1);
|
| + thread_resume(t2);
|
| +
|
| + // wait for the pong port to be created, the two threads race to do it.
|
| + port_t r_port;
|
| + while (true) {
|
| + status_t st = port_open("pong_port", NULL, &r_port);
|
| + if (st == NO_ERROR) {
|
| + break;
|
| + } else if (st == ERR_NOT_FOUND) {
|
| + thread_sleep(100);
|
| + } else {
|
| + printf("could not open port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| + }
|
| +
|
| + // We have two threads listening to the ping port. Which both reply
|
| + // on the pong port, so we get two packets in per packet out.
|
| + const int passes = 256;
|
| + printf("two_threads_basic test, %d passes\n", passes);
|
| +
|
| + port_packet_t packet_out = {{0xaf, 0x77, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
|
| +
|
| + port_result_t pr;
|
| + for (int ix = 0; ix != passes; ++ix) {
|
| + const size_t count = 1 + ((unsigned int)rand() % 3);
|
| +
|
| + for (size_t jx = 0; jx != count; ++jx) {
|
| + st = port_write(w_port, &packet_out, 1);
|
| + if (st < 0) {
|
| + printf("could not write port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| + }
|
| +
|
| + packet_out.value[0]++;
|
| + packet_out.value[5]--;
|
| +
|
| + for (size_t jx = 0; jx != count * 2; ++jx) {
|
| + st = port_read(r_port, INFINITE_TIME, &pr);
|
| + if (st < 0) {
|
| + printf("could not read port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + if ((pr.packet.value[0] != packet_out.value[0]) ||
|
| + (pr.packet.value[5] != packet_out.value[5])) {
|
| + printf("unexpected data in packet, loop %d", ix);
|
| + return __LINE__;
|
| + }
|
| + }
|
| + }
|
| +
|
| + thread_sleep(100);
|
| +
|
| + // there should be no more packets to read.
|
| + st = port_read(r_port, 0, &pr);
|
| + if (st != ERR_TIMED_OUT) {
|
| + printf("unexpected packet, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + printf("two_threads_basic master shutdown\n");
|
| +
|
| + st = port_close(r_port);
|
| + if (st < 0) {
|
| + printf("could not close port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_close(w_port);
|
| + if (st < 0) {
|
| + printf("could not close port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + st = port_destroy(w_port);
|
| + if (st < 0) {
|
| + printf("could not destroy port, status = %d\n", st);
|
| + return __LINE__;
|
| + }
|
| +
|
| + int retcode = -1;
|
| + thread_join(t1, &retcode, INFINITE_TIME);
|
| + if (retcode)
|
| + goto fail;
|
| +
|
| + thread_join(t2, &retcode, INFINITE_TIME);
|
| + if (retcode)
|
| + goto fail;
|
| +
|
| + return 0;
|
| +
|
| +fail:
|
| + printf("child thread exited with %d\n", retcode);
|
| + return __LINE__;
|
| +}
|
| +
|
| +#define RUN_TEST(t) result = t(); if (result) goto fail
|
| +
|
| +int port_tests(void)
|
| +{
|
| + int result;
|
| + int count = 2;
|
| + while (count--) {
|
| + RUN_TEST(single_thread_basic);
|
| + RUN_TEST(two_threads_basic);
|
| + }
|
| +
|
| + printf("all tests passed\n");
|
| + return 0;
|
| +fail:
|
| + printf("test failed at line %d\n", result);
|
| + return 1;
|
| +}
|
| +
|
| +#undef RUN_TEST
|
|
|