| /* |
| * Copyright (C) 2014-2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <trusty_ipc.h> |
| #include <uapi/err.h> |
| |
| #define TLOG_TAG "ipc-unittest-main" |
| |
| #include <app/ipc_unittest/common.h> |
| #include <app/ipc_unittest/uuids.h> |
| #include <lib/unittest/unittest.h> |
| |
| #include <lk/trace.h> |
| |
| /* */ |
| static unsigned int _tests_total = 0; /* Number of conditions checked */ |
| static unsigned int _tests_failed = 0; /* Number of conditions failed */ |
| static handle_t handle_base; /* base of valid handle range */ |
| |
| static const uuid_t srv_app_uuid = IPC_UNITTEST_SRV_APP_UUID; |
| |
| /* |
| * Begin and end test macro |
| */ |
| #define TEST_BEGIN(name) \ |
| bool _all_ok = true; \ |
| const char* _test = name; \ |
| TLOGI("%s:\n", _test); |
| |
| #define TEST_END \ |
| { \ |
| if (_all_ok) \ |
| TLOGI("%s: PASSED\n", _test); \ |
| else \ |
| TLOGI("%s: FAILED\n", _test); \ |
| } |
| |
| /* |
| * EXPECT_* macros to check test results. |
| */ |
| #define EXPECT_EQ(expected, actual, msg) \ |
| { \ |
| __typeof__(actual) _e = expected; \ |
| __typeof__(actual) _a = actual; \ |
| _tests_total++; \ |
| if (_e != _a) { \ |
| TLOGI("%s: expected " #expected \ |
| " (%d), " \ |
| "actual " #actual " (%d)\n", \ |
| msg, (int)_e, (int)_a); \ |
| _tests_failed++; \ |
| _all_ok = false; \ |
| } \ |
| } |
| |
| #define EXPECT_GT(expected, actual, msg) \ |
| { \ |
| __typeof__(actual) _e = expected; \ |
| __typeof__(actual) _a = actual; \ |
| _tests_total++; \ |
| if (_e <= _a) { \ |
| TLOGI("%s: expected " #expected \ |
| " (%d), " \ |
| "actual " #actual " (%d)\n", \ |
| msg, (int)_e, (int)_a); \ |
| _tests_failed++; \ |
| _all_ok = false; \ |
| } \ |
| } |
| |
| #define EXPECT_GE_ZERO(actual, msg) \ |
| { \ |
| __typeof__(actual) _a = actual; \ |
| _tests_total++; \ |
| if (_a < 0) { \ |
| TLOGI("%s: expected >= 0 " \ |
| "actual " #actual " (%d)\n", \ |
| msg, (int)_a); \ |
| _tests_failed++; \ |
| _all_ok = false; \ |
| } \ |
| } |
| |
| #define EXPECT_GT_ZERO(actual, msg) \ |
| { \ |
| __typeof__(actual) _a = actual; \ |
| _tests_total++; \ |
| if (_a <= 0) { \ |
| TLOGI("%s: expected > 0 " \ |
| "actual " #actual " (%d)\n", \ |
| msg, (int)_a); \ |
| _tests_failed++; \ |
| _all_ok = false; \ |
| } \ |
| } |
| |
| #define ABORT_IF(_cond, lbl) \ |
| { \ |
| if (_cond) { \ |
| goto lbl; \ |
| } \ |
| } |
| |
| #define ABORT_IF_NOT_OK(lbl) ABORT_IF((!_all_ok), lbl) |
| |
| /****************************************************************************/ |
| |
| /* |
| * Fill specified buffer with incremental pattern |
| */ |
| static void fill_test_buf(uint8_t* buf, size_t cnt, uint8_t seed) { |
| if (!buf || !cnt) |
| return; |
| |
| while (cnt--) { |
| *buf++ = seed++; |
| } |
| } |
| |
| /* |
| * Local wrapper on top of async connect that provides |
| * synchronos connect with timeout. |
| */ |
| int sync_connect(const char* path, unsigned int timeout) { |
| int rc; |
| uevent_t evt; |
| handle_t chan; |
| |
| rc = connect(path, IPC_CONNECT_ASYNC | IPC_CONNECT_WAIT_FOR_PORT); |
| if (rc >= 0) { |
| chan = (handle_t)rc; |
| rc = wait(chan, &evt, timeout); |
| if (rc == 0) { |
| rc = ERR_BAD_STATE; |
| if (evt.handle == chan) { |
| if (evt.event & IPC_HANDLE_POLL_READY) |
| return chan; |
| |
| if (evt.event & IPC_HANDLE_POLL_HUP) |
| rc = ERR_CHANNEL_CLOSED; |
| } |
| } |
| close(chan); |
| } |
| return rc; |
| } |
| |
| /****************************************************************************/ |
| |
| /* |
| * wait on handle negative test |
| */ |
| static void run_wait_negative_test(void) { |
| int rc; |
| uevent_t event; |
| uint32_t timeout = 1000; // 1 sec |
| |
| TEST_BEGIN(__func__); |
| |
| /* waiting on invalid handle. */ |
| rc = wait(INVALID_IPC_HANDLE, &event, timeout); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "wait on invalid handle"); |
| |
| /* |
| * call wait on an invalid (out of range) handle |
| * |
| * check handling of the following cases: |
| * - handle is on the upper boundary of valid handle range |
| * - handle is above of the upper boundary of valid handle range |
| * - handle is below of valid handle range |
| * |
| * in all cases, the expected result is ERR_BAD_HANDLE error. |
| */ |
| rc = wait(handle_base + MAX_USER_HANDLES, &event, timeout); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "wait on invalid handle"); |
| |
| rc = wait(handle_base + MAX_USER_HANDLES + 1, &event, timeout); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "wait on invalid handle"); |
| |
| rc = wait(handle_base - 1, &event, timeout); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "wait on invalid handle"); |
| |
| /* waiting on non-existing handle that is in valid range. */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| rc = wait(handle_base + i, &event, timeout); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "wait on invalid handle"); |
| } |
| |
| TEST_END |
| } |
| |
| /* |
| * Close handle unittest |
| */ |
| static void run_close_handle_negative_test(void) { |
| int rc; |
| |
| TEST_BEGIN(__func__); |
| |
| /* closing an invalid (negative value) handle. */ |
| rc = close(INVALID_IPC_HANDLE); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "closing invalid handle"); |
| |
| /* |
| * call close on an invalid (out of range) handle |
| * |
| * check handling of the following cases: |
| * - handle is on the upper boundary of valid handle range |
| * - handle is above of the upper boundary of valid handle range |
| * - handle is below of valid handle range |
| * |
| * in all cases, the expected result is ERR_BAD_HANDLE error. |
| */ |
| rc = close(handle_base + MAX_USER_HANDLES); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "closing invalid handle"); |
| |
| rc = close(handle_base + MAX_USER_HANDLES + 1); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "closing invalid handle"); |
| |
| rc = close(handle_base - 1); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "closing invalid handle"); |
| |
| /* closing non-existing handle that is in valid range. */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| rc = close(handle_base + i); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "closing invalid handle"); |
| } |
| |
| TEST_END |
| } |
| |
| /* |
| * Set cookie negative unittest |
| */ |
| static void run_set_cookie_negative_test(void) { |
| int rc; |
| |
| TEST_BEGIN(__func__); |
| |
| /* set cookie for invalid (negative value) handle. */ |
| rc = set_cookie(INVALID_IPC_HANDLE, (void*)0x1BEEF); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "set cookie for invalid handle"); |
| |
| /* |
| * calling set cookie for an invalid (out of range) handle |
| * |
| * check handling of the following cases: |
| * - handle is on the upper boundary of valid handle range |
| * - handle is above of the upper boundary of valid handle range |
| * - handle is below of valid handle range |
| * |
| * in all cases, the expected result is ERR_BAD_HANDLE error. |
| */ |
| rc = set_cookie(handle_base + MAX_USER_HANDLES, (void*)0x2BEEF); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "set cookie for invalid handle"); |
| |
| rc = set_cookie(handle_base + MAX_USER_HANDLES + 1, (void*)0x2BEEF); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "set cookie for invalid handle"); |
| |
| rc = set_cookie(handle_base - 1, (void*)0x2BEEF); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "set cookie for invalid handle"); |
| |
| /* set cookie for non-existing handle that is in valid range. */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| rc = set_cookie(handle_base + i, (void*)0x3BEEF); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "set cookie for invalid handle"); |
| } |
| |
| TEST_END |
| } |
| |
| /****************************************************************************/ |
| |
| /* |
| * Port create unittest |
| */ |
| static void run_port_create_negative_test(void) { |
| int rc; |
| char path[MAX_PORT_PATH_LEN + 16]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* create port with empty path */ |
| path[0] = '\0'; |
| rc = port_create(path, 2, 64, 0); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "empty path srv"); |
| |
| /* create port with zero buffers */ |
| sprintf(path, "%s.port", SRV_PATH_BASE); |
| rc = port_create(path, 0, 64, 0); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "no buffers"); |
| |
| /* create port with zero buffer size */ |
| sprintf(path, "%s.port", SRV_PATH_BASE); |
| rc = port_create(path, 2, 0, 0); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "zero buf size"); |
| |
| /* create port with large number of buffers */ |
| sprintf(path, "%s.port", SRV_PATH_BASE); |
| rc = port_create(path, MAX_PORT_BUF_NUM * 100, 64, 0); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "large buf num"); |
| |
| /* create port with large buffer size */ |
| sprintf(path, "%s.port", SRV_PATH_BASE); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE * 100, 0); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "large buf size"); |
| |
| /* create port with path oversized name */ |
| int len = sprintf(path, "%s.port", SRV_PATH_BASE); |
| for (size_t i = len; i < sizeof(path); i++) |
| path[i] = 'a'; |
| path[sizeof(path) - 1] = '\0'; |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, 0); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "path is too long"); |
| rc = close(rc); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "close port"); |
| |
| TEST_END |
| } |
| |
| static void run_port_create_test(void) { |
| int rc; |
| unsigned int i; |
| char path[MAX_PORT_PATH_LEN]; |
| handle_t ports[MAX_USER_HANDLES]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* create maximum number of ports */ |
| for (i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES - 1; i++) { |
| sprintf(path, "%s.port.%s%d", SRV_PATH_BASE, "test", i); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, 0); |
| EXPECT_GT_ZERO(rc, "create ports"); |
| ports[i] = (handle_t)rc; |
| |
| /* create a new port that collide with an existing port */ |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, 0); |
| EXPECT_EQ(ERR_ALREADY_EXISTS, rc, "create existing port"); |
| } |
| |
| /* create one more that should succeed */ |
| sprintf(path, "%s.port.%s%d", SRV_PATH_BASE, "test", i); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, 0); |
| EXPECT_GT_ZERO(rc, "create ports"); |
| ports[i] = (handle_t)rc; |
| |
| /* but creating colliding port should fail with different |
| error code because we actually exceeded max number of |
| handles instead of colliding with an existing path */ |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, 0); |
| EXPECT_EQ(ERR_NO_RESOURCES, rc, "create existing port"); |
| |
| sprintf(path, "%s.port.%s%d", SRV_PATH_BASE, "test", MAX_USER_HANDLES); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, 0); |
| EXPECT_EQ(ERR_NO_RESOURCES, rc, "max ports"); |
| |
| /* close them all */ |
| for (i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| /* close a valid port */ |
| rc = close(ports[i]); |
| EXPECT_EQ(NO_ERROR, rc, "closing port"); |
| |
| /* close previously closed port. It should fail! */ |
| rc = close(ports[i]); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "closing closed port"); |
| |
| ports[i] = INVALID_IPC_HANDLE; |
| } |
| |
| TEST_END |
| } |
| |
| /* |
| * |
| */ |
| static void run_wait_on_port_test(void) { |
| int rc; |
| uevent_t event; |
| char path[MAX_PORT_PATH_LEN]; |
| handle_t ports[MAX_USER_HANDLES]; |
| |
| TEST_BEGIN(__func__); |
| |
| #define COOKIE_BASE 100 |
| |
| /* create maximum number of ports */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| sprintf(path, "%s.port.%s%d", SRV_PATH_BASE, "test", i); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, 0); |
| EXPECT_GT_ZERO(rc, "max ports"); |
| ports[i] = (handle_t)rc; |
| |
| rc = set_cookie(ports[i], (void*)(COOKIE_BASE + i)); |
| EXPECT_EQ(NO_ERROR, rc, "set cookie on port"); |
| } |
| |
| /* wait on each individual port */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| /* wait with zero timeout */ |
| rc = wait(ports[i], &event, 0); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "zero timeout"); |
| |
| /* wait with non-zero timeout */ |
| rc = wait(ports[i], &event, 100); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "non-zero timeout"); |
| } |
| |
| /* wait on all ports with zero timeout */ |
| rc = wait_any(&event, 0); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "zero timeout"); |
| |
| /* wait on all ports with non-zero timeout*/ |
| rc = wait_any(&event, 100); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "non-zero timeout"); |
| |
| /* close them all */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| /* close a valid port */ |
| rc = close(ports[i]); |
| EXPECT_EQ(NO_ERROR, rc, "closing closed port"); |
| ports[i] = INVALID_IPC_HANDLE; |
| } |
| |
| TEST_END |
| } |
| |
| /****************************************************************************/ |
| |
| /* |
| * Connect unittests |
| */ |
| static void run_connect_negative_test(void) { |
| int rc; |
| char path[MAX_PORT_PATH_LEN + 16]; |
| uint32_t connect_timeout = 1000; // 1 sec |
| |
| TEST_BEGIN(__func__); |
| |
| /* try to connect to port with an empty name */ |
| rc = sync_connect("", connect_timeout); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "empty path"); |
| |
| /* try to connect to non-existing port */ |
| sprintf(path, "%s.conn.%s", SRV_PATH_BASE, "blah-blah"); |
| rc = connect(path, 0); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "non-existing path"); |
| |
| /* try to connect to non-existing port */ |
| sprintf(path, "%s.conn.%s", SRV_PATH_BASE, "blah-blah"); |
| rc = connect(path, IPC_CONNECT_ASYNC); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "non-existing path"); |
| |
| /* try to connect to port with very long name */ |
| int len = sprintf(path, "%s.conn.", SRV_PATH_BASE); |
| for (size_t i = len; i < sizeof(path); i++) |
| path[i] = 'a'; |
| path[sizeof(path) - 1] = '\0'; |
| rc = sync_connect(path, connect_timeout); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "long path"); |
| |
| rc = close(rc); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "close channel"); |
| |
| TEST_END |
| } |
| |
| static void run_connect_close_test(void) { |
| int rc; |
| char path[MAX_PORT_PATH_LEN]; |
| handle_t chans[16]; |
| |
| TEST_BEGIN(__func__); |
| |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "datasink"); |
| |
| for (unsigned int j = FIRST_FREE_HANDLE; j < MAX_USER_HANDLES; j++) { |
| /* do several iterations to make sure we are not |
| not loosing handles */ |
| for (unsigned int i = 0; i < countof(chans); i++) { |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect/close"); |
| chans[i] = (handle_t)rc; |
| } |
| |
| for (unsigned int i = 0; i < countof(chans); i++) { |
| rc = close(chans[i]); |
| EXPECT_EQ(NO_ERROR, rc, "connect/close"); |
| } |
| } |
| |
| TEST_END |
| } |
| |
| static void run_connect_close_by_peer_test(const char* test) { |
| int rc; |
| char path[MAX_PORT_PATH_LEN]; |
| uevent_t event; |
| handle_t chans[16]; |
| unsigned int chan_cnt = 0; |
| |
| TEST_BEGIN(__func__); |
| |
| #define COOKIE_BASE 100 |
| |
| /* |
| * open up to 16 connection to specified test port which would |
| * close them all in a different way: |
| */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, test); |
| for (unsigned int i = 0; i < countof(chans); i++) { |
| /* open new connection */ |
| unsigned int retry_cnt = 10; |
| while (retry_cnt) { |
| rc = sync_connect(path, 2000); |
| if (rc == ERR_NOT_FOUND) { |
| /* wait a bit and retry */ |
| --retry_cnt; |
| trusty_nanosleep(0, 0, 100 * MSEC); |
| } else { |
| break; |
| } |
| } |
| EXPECT_GT_ZERO(retry_cnt, test); |
| |
| /* |
| * depending on task scheduling connect might return real |
| * handle that will be closed later or it might return |
| * ERR_CHANNEL_CLOSED error if the channel has already been |
| * closed. Both cases are correct and must be handled. |
| */ |
| if (rc >= 0) { |
| /* got real handle */ |
| chans[i] = (handle_t)rc; |
| |
| /* attach cookie for returned channel */ |
| rc = set_cookie((handle_t)rc, (void*)(COOKIE_BASE + i)); |
| EXPECT_EQ(NO_ERROR, rc, test); |
| |
| chan_cnt++; |
| } else { |
| /* could be already closed channel */ |
| EXPECT_EQ(ERR_CHANNEL_CLOSED, rc, test); |
| } |
| |
| /* check if any channels are closed */ |
| while ((rc = wait_any(&event, 0)) == NO_ERROR) { |
| EXPECT_EQ(IPC_HANDLE_POLL_HUP, event.event, test); |
| unsigned int idx = (unsigned int)event.cookie - COOKIE_BASE; |
| EXPECT_EQ(chans[idx], event.handle, test); |
| EXPECT_GT(countof(chans), idx, test); |
| if (idx < countof(chans)) { |
| rc = close(chans[idx]); |
| EXPECT_EQ(NO_ERROR, rc, test); |
| chans[idx] = INVALID_IPC_HANDLE; |
| } |
| chan_cnt--; |
| } |
| } |
| |
| /* wait until all channels are closed */ |
| while (chan_cnt) { |
| rc = wait_any(&event, 10000); |
| EXPECT_EQ(NO_ERROR, rc, test); |
| EXPECT_EQ(IPC_HANDLE_POLL_HUP, event.event, test); |
| |
| unsigned int idx = (unsigned int)event.cookie - COOKIE_BASE; |
| EXPECT_GT(countof(chans), idx, test); |
| EXPECT_EQ(chans[idx], event.handle, test); |
| if (idx < countof(chans)) { |
| rc = close(chans[idx]); |
| EXPECT_EQ(NO_ERROR, rc, test); |
| chans[idx] = INVALID_IPC_HANDLE; |
| } |
| chan_cnt--; |
| } |
| |
| EXPECT_EQ(0, chan_cnt, test); |
| |
| TEST_END |
| } |
| |
| static void run_async_connect_test(void) { |
| int rc; |
| handle_t chan; |
| uevent_t event; |
| uuid_t peer_uuid = UUID_INITIAL_VALUE(peer_uuid); |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| sprintf(path, "%s.main.%s", SRV_PATH_BASE, "async"); |
| |
| /* connect to non existing port synchronously without wait_for_port */ |
| rc = connect(path, 0); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "async"); |
| rc = close((handle_t)rc); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "async"); |
| |
| /* connect to non existing port asynchronously without wait_for_port */ |
| rc = connect(path, IPC_CONNECT_ASYNC); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "async"); |
| rc = close((handle_t)rc); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "async"); |
| |
| /* connect to non existing port asynchronously with wait_for_port */ |
| rc = connect(path, IPC_CONNECT_ASYNC | IPC_CONNECT_WAIT_FOR_PORT); |
| EXPECT_GT_ZERO(rc, "async"); |
| if (rc >= 0) { |
| chan = (handle_t)rc; |
| |
| /* wait on channel */ |
| rc = wait(chan, &event, 1000); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "async"); |
| |
| /* and close it */ |
| rc = close(chan); |
| EXPECT_EQ(NO_ERROR, rc, "async"); |
| } |
| |
| /* connect to non-existing port asyncronously with wait_for_port */ |
| rc = connect(path, IPC_CONNECT_ASYNC | IPC_CONNECT_WAIT_FOR_PORT); |
| EXPECT_GT_ZERO(rc, "async"); |
| chan = (handle_t)rc; |
| |
| if (rc >= 0) { |
| handle_t port; |
| uint32_t exp_event; |
| |
| /* wait on channel for connect */ |
| rc = wait(chan, &event, 100); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "async"); |
| |
| /* now create port */ |
| rc = port_create(path, 1, 64, IPC_PORT_ALLOW_TA_CONNECT); |
| EXPECT_GT_ZERO(rc, "async"); |
| if (rc >= 0) { |
| port = (handle_t)rc; |
| |
| /* and wait for incomming connections */ |
| exp_event = IPC_HANDLE_POLL_READY; |
| rc = wait(port, &event, 1000); |
| EXPECT_EQ(NO_ERROR, rc, "async"); |
| EXPECT_EQ(exp_event, event.event, "async"); |
| |
| if (rc == NO_ERROR) { |
| handle_t srv_chan; |
| |
| /* got one, accept it */ |
| rc = accept(port, &peer_uuid); |
| EXPECT_GT_ZERO(rc, "async"); |
| srv_chan = (handle_t)rc; |
| |
| /* and close it */ |
| close(srv_chan); |
| |
| /* now wait on original chan: |
| * there should be READY and HUP events |
| */ |
| exp_event = IPC_HANDLE_POLL_READY | IPC_HANDLE_POLL_HUP; |
| rc = wait(chan, &event, 1000); |
| EXPECT_EQ(NO_ERROR, rc, "async"); |
| EXPECT_EQ(exp_event, event.event, "async"); |
| } |
| close(port); |
| } |
| close(chan); |
| } |
| |
| TEST_END |
| } |
| |
| static void run_connect_selfie_test(void) { |
| int rc, rc1; |
| uuid_t peer_uuid = UUID_INITIAL_VALUE(peer_uuid); |
| uuid_t zero_uuid = UUID_INITIAL_VALUE(zero_uuid); |
| char path[MAX_PORT_PATH_LEN]; |
| uint32_t connect_timeout = 1000; // 1 sec |
| |
| TEST_BEGIN(__func__); |
| |
| /* Try to connect to port that we register ourself. |
| It is not very usefull scenario, just to make sure that |
| nothing bad is happening */ |
| sprintf(path, "%s.main.%s", SRV_PATH_BASE, "selfie"); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, IPC_PORT_ALLOW_TA_CONNECT); |
| EXPECT_GT_ZERO(rc, "selfie"); |
| |
| if (rc >= 0) { |
| handle_t test_port = rc; |
| |
| /* Since we are single threaded and cannot accept connection |
| * we will always timeout. |
| */ |
| |
| /* with non-zero timeout */ |
| rc = sync_connect(path, connect_timeout); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "selfie"); |
| |
| /* with zero timeout */ |
| rc = sync_connect(path, 0); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "selfie"); |
| |
| /* since we did not call wait on port yet we have |
| * 2 connection requests pending (attached to port) |
| * teared down by peer (us). |
| */ |
| uevent_t event; |
| unsigned int exp_event = IPC_HANDLE_POLL_READY; |
| |
| int rc = wait_any(&event, -1); |
| EXPECT_EQ(NO_ERROR, rc, "wait on port"); |
| EXPECT_EQ(test_port, event.handle, "wait on port"); |
| EXPECT_EQ(exp_event, event.event, "wait on port"); |
| |
| if (rc == NO_ERROR && (event.event & IPC_HANDLE_POLL_READY)) { |
| /* we have pending connection, but it is already closed */ |
| rc = accept(test_port, &peer_uuid); |
| EXPECT_EQ(ERR_CHANNEL_CLOSED, rc, "accept"); |
| |
| rc1 = memcmp(&peer_uuid, &zero_uuid, sizeof(zero_uuid)); |
| EXPECT_EQ(0, rc1, "accept") |
| |
| /* the second one is closed too */ |
| rc = accept(test_port, &peer_uuid); |
| EXPECT_EQ(ERR_CHANNEL_CLOSED, rc, "accept"); |
| |
| rc1 = memcmp(&peer_uuid, &zero_uuid, sizeof(zero_uuid)); |
| EXPECT_EQ(0, rc1, "accept") |
| |
| /* There should be no more pending connections so next |
| accept should return ERR_NO_MSG */ |
| rc = accept(test_port, &peer_uuid); |
| EXPECT_EQ(ERR_NO_MSG, rc, "accept"); |
| |
| rc1 = memcmp(&peer_uuid, &zero_uuid, sizeof(zero_uuid)); |
| EXPECT_EQ(0, rc1, "accept") |
| } |
| |
| /* add couple connections back and destroy them along with port */ |
| rc = sync_connect(path, 0); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "selfie"); |
| |
| rc = sync_connect(path, 0); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "selfie"); |
| |
| /* close selfie port */ |
| rc = close(test_port); |
| EXPECT_EQ(NO_ERROR, rc, "close selfie"); |
| } |
| |
| TEST_END |
| } |
| |
| static void run_connect_access_test(void) { |
| int rc; |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* open connection to NS only accessible service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "ns_only"); |
| rc = sync_connect(path, 1000); |
| |
| /* It is expected to fail */ |
| EXPECT_EQ(ERR_ACCESS_DENIED, rc, "connect to ns_only"); |
| |
| if (rc >= 0) |
| close((handle_t)rc); |
| |
| /* open connection to TA only accessible service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "ta_only"); |
| rc = sync_connect(path, 1000); |
| |
| /* it is expected to succeed */ |
| EXPECT_GT_ZERO(rc, "connect to ta_only"); |
| |
| if (rc >= 0) |
| close((handle_t)rc); |
| |
| TEST_END |
| } |
| |
| /****************************************************************************/ |
| |
| /* |
| * Accept negative test |
| */ |
| static void run_accept_negative_test(void) { |
| int rc, rc1; |
| char path[MAX_PORT_PATH_LEN]; |
| handle_t chan; |
| uuid_t peer_uuid = UUID_INITIAL_VALUE(peer_uuid); |
| uuid_t zero_uuid = UUID_INITIAL_VALUE(zero_uuid); |
| |
| TEST_BEGIN(__func__); |
| |
| /* accept on invalid (negative value) handle */ |
| rc = accept(INVALID_IPC_HANDLE, &peer_uuid); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "accept on invalid handle"); |
| |
| rc1 = memcmp(&peer_uuid, &zero_uuid, sizeof(zero_uuid)); |
| EXPECT_EQ(0, rc1, "accept") |
| |
| /* |
| * calling accept on an invalid (out of range) handle |
| * |
| * check handling of the following cases: |
| * - handle is on the upper boundary of valid handle range |
| * - handle is above of the upper boundary of valid handle range |
| * - handle is below of valid handle range |
| * |
| * in all cases, the expected result is ERR_BAD_HANDLE error. |
| */ |
| rc = accept(handle_base + MAX_USER_HANDLES, &peer_uuid); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "accept on invalid handle"); |
| |
| rc = accept(handle_base + MAX_USER_HANDLES + 1, &peer_uuid); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "accept on invalid handle"); |
| |
| rc = accept(handle_base - 1, &peer_uuid); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "accept on invalid handle"); |
| |
| rc1 = memcmp(&peer_uuid, &zero_uuid, sizeof(zero_uuid)); |
| EXPECT_EQ(0, rc1, "accept") |
| |
| /* accept on non-existing handle that is in valid range */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| rc = accept(handle_base + i, &peer_uuid); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "accept on invalid handle"); |
| |
| rc1 = memcmp(&peer_uuid, &zero_uuid, sizeof(zero_uuid)); |
| EXPECT_EQ(0, rc1, "accept") |
| } |
| |
| /* connect to datasink service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "datasink"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| chan = (handle_t)rc; |
| |
| /* call accept on channel handle which is an invalid operation */ |
| rc = accept(chan, &peer_uuid); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "accept on channel"); |
| |
| rc1 = memcmp(&peer_uuid, &zero_uuid, sizeof(zero_uuid)); |
| EXPECT_EQ(0, rc1, "accept") |
| |
| rc = close(chan); |
| EXPECT_EQ(NO_ERROR, rc, "close channnel") |
| |
| TEST_END |
| } |
| |
| static void run_accept_test(void) { |
| int rc, rc1; |
| uevent_t event; |
| char path[MAX_PORT_PATH_LEN]; |
| handle_t ports[MAX_USER_HANDLES]; |
| uuid_t peer_uuid = UUID_INITIAL_VALUE(peer_uuid); |
| uuid_t zero_uuid = UUID_INITIAL_VALUE(zero_uuid); |
| |
| TEST_BEGIN(__func__); |
| |
| #define COOKIE_BASE 100 |
| |
| /* create maximum number of ports */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| sprintf(path, "%s.port.accept%d", SRV_PATH_BASE, i); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, IPC_PORT_ALLOW_TA_CONNECT); |
| EXPECT_GT_ZERO(rc, "max ports"); |
| ports[i] = (handle_t)rc; |
| |
| rc = set_cookie(ports[i], (void*)(COOKIE_BASE + ports[i])); |
| EXPECT_EQ(NO_ERROR, rc, "set cookie on port"); |
| } |
| |
| /* poke connect service to initiate connections to us */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "connect"); |
| rc = sync_connect(path, 1000); |
| if (rc >= 0) |
| close((handle_t)rc); |
| |
| /* handle incoming connections */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| rc = wait_any(&event, 1000); |
| EXPECT_EQ(NO_ERROR, rc, "accept test"); |
| EXPECT_EQ(IPC_HANDLE_POLL_READY, event.event, "accept test"); |
| |
| /* check port cookie */ |
| void* exp_cookie = (void*)(COOKIE_BASE + event.handle); |
| EXPECT_EQ(exp_cookie, event.cookie, "accept test"); |
| |
| /* accept connection - should fail because we do not |
| have any room for handles */ |
| rc = accept(event.handle, &peer_uuid); |
| EXPECT_EQ(ERR_NO_RESOURCES, rc, "accept test"); |
| |
| /* check peer uuid */ |
| rc1 = memcmp(&peer_uuid, &zero_uuid, sizeof(zero_uuid)); |
| EXPECT_EQ(0, rc1, "accept test") |
| } |
| |
| /* free 1 handle so we have room and repeat test */ |
| rc = close(ports[FIRST_FREE_HANDLE]); |
| EXPECT_EQ(NO_ERROR, 0, "close accept test"); |
| ports[2] = INVALID_IPC_HANDLE; |
| |
| /* poke connect service to initiate connections to us */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "connect"); |
| rc = sync_connect(path, 1000); |
| if (rc >= 0) |
| close((handle_t)rc); |
| |
| /* handle incoming connections */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES - 1; i++) { |
| rc = wait_any(&event, 3000); |
| EXPECT_EQ(NO_ERROR, rc, "accept test"); |
| EXPECT_EQ(IPC_HANDLE_POLL_READY, event.event, "accept test"); |
| |
| /* check port cookie */ |
| void* exp_cookie = (void*)(COOKIE_BASE + event.handle); |
| EXPECT_EQ(exp_cookie, event.cookie, "accept test"); |
| |
| rc = accept(event.handle, &peer_uuid); |
| EXPECT_EQ(handle_base + FIRST_FREE_HANDLE, rc, "accept test"); |
| |
| /* check peer uuid */ |
| rc1 = memcmp(&peer_uuid, &srv_app_uuid, sizeof(srv_app_uuid)); |
| EXPECT_EQ(0, rc1, "accept test") |
| |
| rc = close(rc); |
| EXPECT_EQ(NO_ERROR, rc, "accept test"); |
| } |
| |
| /* close them all */ |
| for (unsigned int i = FIRST_FREE_HANDLE + 1; i < MAX_USER_HANDLES; i++) { |
| /* close a valid port */ |
| rc = close(ports[i]); |
| EXPECT_EQ(NO_ERROR, rc, "close port"); |
| ports[i] = INVALID_IPC_HANDLE; |
| } |
| |
| TEST_END |
| } |
| |
| /****************************************************************************/ |
| |
| static void run_get_msg_negative_test(void) { |
| int rc; |
| ipc_msg_info_t inf; |
| handle_t port; |
| handle_t chan; |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* get_msg on invalid (negative value) handle. */ |
| rc = get_msg(INVALID_IPC_HANDLE, &inf); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "get_msg on invalid handle"); |
| |
| /* |
| * calling get_msg on an invalid (out of range) handle |
| * |
| * check handling of the following cases: |
| * - handle is on the upper boundary of valid handle range |
| * - handle is above of the upper boundary of valid handle range |
| * - handle is below of valid handle range |
| * |
| * in all cases, the expected result is ERR_BAD_HANDLE error. |
| */ |
| rc = get_msg(handle_base + MAX_USER_HANDLES, &inf); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "get_msg on invalid handle"); |
| |
| rc = get_msg(handle_base + MAX_USER_HANDLES + 1, &inf); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "get_msg on invalid handle"); |
| |
| rc = get_msg(handle_base - 1, &inf); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "get_msg on invalid handle"); |
| |
| /* get_msg on non-existing handle that is in valid range. */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| rc = get_msg(handle_base + i, &inf); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "get_msg on invalid handle"); |
| } |
| |
| /* calling get_msg on port handle should fail |
| because get_msg is only applicable to channels */ |
| sprintf(path, "%s.main.%s", SRV_PATH_BASE, "datasink"); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, IPC_PORT_ALLOW_TA_CONNECT); |
| EXPECT_GT_ZERO(rc, "create datasink port"); |
| port = (handle_t)rc; |
| |
| rc = get_msg(port, &inf); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "get_msg on port"); |
| close(port); |
| |
| /* call get_msg on channel that do not have any pending messages */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "datasink"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| chan = (handle_t)rc; |
| |
| rc = get_msg(chan, &inf); |
| EXPECT_EQ(ERR_NO_MSG, rc, "get_msg on empty channel"); |
| |
| rc = close(chan); |
| EXPECT_EQ(NO_ERROR, rc, "close channnel"); |
| |
| TEST_END |
| } |
| |
| static void run_put_msg_negative_test(void) { |
| int rc; |
| handle_t port; |
| handle_t chan; |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* put_msg on invalid (negative value) handle */ |
| rc = put_msg(INVALID_IPC_HANDLE, 0); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "put_msg on invalid handle"); |
| |
| /* |
| * calling put_msg on an invalid (out of range) handle |
| * |
| * check handling of the following cases: |
| * - handle is on the upper boundary of valid handle range |
| * - handle is above of the upper boundary of valid handle range |
| * - handle is below of valid handle range |
| * |
| * in all cases, the expected result is ERR_BAD_HANDLE error. |
| */ |
| rc = put_msg(handle_base + MAX_USER_HANDLES, 0); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "put_msg on invalid handle"); |
| |
| rc = put_msg(handle_base + MAX_USER_HANDLES + 1, 0); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "put_msg on invalid handle"); |
| |
| rc = put_msg(handle_base - 1, 0); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "put_msg on invalid handle"); |
| |
| /* put_msg on non-existing handle that is in valid range */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| rc = put_msg(handle_base + i, 0); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "put_msg on invalid handle"); |
| } |
| |
| /* calling put_msg on port handle should fail |
| because put_msg is only applicable to channels */ |
| sprintf(path, "%s.main.%s", SRV_PATH_BASE, "datasink"); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, IPC_PORT_ALLOW_TA_CONNECT); |
| EXPECT_GT_ZERO(rc, "create datasink port"); |
| port = (handle_t)rc; |
| |
| rc = put_msg(port, 0); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "put_msg on port"); |
| rc = close(port); |
| EXPECT_EQ(NO_ERROR, rc, "close port"); |
| |
| /* call put_msg on channel that do not have any pending messages */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "datasink"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| chan = (handle_t)rc; |
| |
| rc = put_msg(chan, 0); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "put_msg on empty channel"); |
| rc = close(chan); |
| EXPECT_EQ(NO_ERROR, rc, "close channel"); |
| |
| TEST_END |
| } |
| |
| /* |
| * Send 10000 messages to datasink service |
| */ |
| static void run_send_msg_test(void) { |
| int rc; |
| handle_t chan; |
| char path[MAX_PORT_PATH_LEN]; |
| uint8_t buf0[64]; |
| uint8_t buf1[64]; |
| iovec_t iov[2]; |
| ipc_msg_t msg; |
| |
| TEST_BEGIN(__func__); |
| |
| /* prepare test buffer */ |
| fill_test_buf(buf0, sizeof(buf0), 0x55); |
| fill_test_buf(buf1, sizeof(buf1), 0x44); |
| |
| iov[0].base = buf0; |
| iov[0].len = sizeof(buf0); |
| iov[1].base = buf1; |
| iov[1].len = sizeof(buf1); |
| msg.num_handles = 0; |
| msg.handles = NULL; |
| msg.num_iov = 2; |
| msg.iov = iov; |
| |
| /* open connection to datasink service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "datasink"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| |
| if (rc >= 0) { |
| chan = (handle_t)rc; |
| for (unsigned int i = 0; i < 10000; i++) { |
| rc = send_msg(chan, &msg); |
| if (rc == ERR_NOT_ENOUGH_BUFFER) { /* wait for room */ |
| uevent_t uevt; |
| unsigned int exp_event = IPC_HANDLE_POLL_SEND_UNBLOCKED; |
| rc = wait(chan, &uevt, 1000); |
| EXPECT_EQ(NO_ERROR, rc, "waiting for space"); |
| EXPECT_EQ(chan, uevt.handle, "waiting for space"); |
| EXPECT_EQ(exp_event, uevt.event, "waiting for space"); |
| } else { |
| EXPECT_EQ(64, rc, "send_msg bulk") |
| } |
| if (!_all_ok) { |
| TLOGI("%s: abort (rc = %d) test\n", __func__, rc); |
| break; |
| } |
| } |
| rc = close(chan); |
| EXPECT_EQ(NO_ERROR, rc, "close channel"); |
| } |
| |
| TEST_END |
| } |
| |
| static void run_send_msg_negative_test(void) { |
| int rc; |
| handle_t port; |
| handle_t chan; |
| char path[MAX_PORT_PATH_LEN]; |
| uint8_t buf[64]; |
| iovec_t iov[2]; |
| ipc_msg_t msg; |
| |
| TEST_BEGIN(__func__); |
| |
| /* init msg to empty message */ |
| memset(&msg, 0, sizeof(msg)); |
| |
| /* send_msg on invalid (negative value) handle */ |
| rc = send_msg(INVALID_IPC_HANDLE, &msg); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "send_msg on invalid handle"); |
| |
| /* calling send_msg with NULL msg should fail for any handle */ |
| rc = send_msg(INVALID_IPC_HANDLE, NULL); |
| EXPECT_EQ(ERR_FAULT, rc, "send_msg on NULL msg"); |
| |
| /* |
| * calling send_msg on an invalid (out of range) handle |
| * |
| * check handling of the following cases: |
| * - handle is on the upper boundary of valid handle range |
| * - handle is above of the upper boundary of valid handle range |
| * - handle is below of valid handle range |
| * |
| * in all cases, the expected result is ERR_BAD_HANDLE error. |
| */ |
| rc = send_msg(handle_base + MAX_USER_HANDLES, &msg); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "send_msg on invalid handle"); |
| |
| rc = send_msg(handle_base + MAX_USER_HANDLES + 1, &msg); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "send_msg on invalid handle"); |
| |
| rc = send_msg(handle_base - 1, &msg); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "send_msg on invalid handle"); |
| |
| /* calling send_msg with NULL msg should fail for any handle */ |
| rc = send_msg(MAX_USER_HANDLES, NULL); |
| EXPECT_EQ(ERR_FAULT, rc, "send_msg on NULL msg"); |
| |
| /* send_msg on non-existing handle that is in valid range */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| rc = send_msg(handle_base + i, &msg); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "send on invalid handle"); |
| |
| /* calling send_msg with NULL msg should fail for any handle */ |
| rc = send_msg(handle_base + i, NULL); |
| EXPECT_EQ(ERR_FAULT, rc, "send_msg on NULL msg"); |
| } |
| |
| /* calling send_msg on port handle should fail |
| because send_msg is only applicable to channels */ |
| sprintf(path, "%s.main.%s", SRV_PATH_BASE, "datasink"); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, IPC_PORT_ALLOW_TA_CONNECT); |
| EXPECT_GT_ZERO(rc, "create datasink port"); |
| port = (handle_t)rc; |
| |
| rc = send_msg(port, &msg); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "send_msg on port"); |
| close(port); |
| |
| /* open connection to datasink service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "datasink"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| chan = (handle_t)rc; |
| |
| /* send message with handles pointing to NULL */ |
| msg.num_handles = 1; |
| msg.handles = NULL; |
| rc = send_msg(chan, &msg); |
| EXPECT_EQ(ERR_FAULT, rc, "sending handles"); |
| |
| /* reset handles */ |
| msg.num_handles = 0; |
| msg.handles = NULL; |
| |
| /* set num_iov to non zero but keep iov ptr NULL */ |
| msg.num_iov = 1; |
| msg.iov = NULL; |
| rc = send_msg(chan, &msg); |
| EXPECT_EQ(ERR_FAULT, rc, "sending bad iovec array"); |
| |
| /* send msg with iovec with bad base ptr */ |
| iov[0].len = sizeof(buf) / 2; |
| iov[0].base = NULL; |
| iov[1].len = sizeof(buf) / 2; |
| iov[1].base = NULL; |
| msg.num_iov = 2; |
| msg.iov = iov; |
| rc = send_msg(chan, &msg); |
| EXPECT_EQ(ERR_FAULT, rc, "sending bad iovec"); |
| |
| /* send msg with iovec with bad base ptr */ |
| iov[0].len = sizeof(buf) / 2; |
| iov[0].base = buf; |
| iov[1].len = sizeof(buf) / 2; |
| iov[1].base = NULL; |
| msg.num_iov = 2; |
| msg.iov = iov; |
| rc = send_msg(chan, &msg); |
| EXPECT_EQ(ERR_FAULT, rc, "sending bad iovec"); |
| |
| rc = close(chan); |
| EXPECT_EQ(NO_ERROR, rc, "close channel"); |
| |
| TEST_END |
| } |
| |
| static void run_read_msg_negative_test(void) { |
| int rc; |
| handle_t port; |
| handle_t chan; |
| uevent_t uevt; |
| char path[MAX_PORT_PATH_LEN]; |
| uint8_t tx_buf[64]; |
| uint8_t rx_buf[64]; |
| ipc_msg_info_t inf; |
| ipc_msg_t tx_msg; |
| iovec_t tx_iov; |
| ipc_msg_t rx_msg; |
| iovec_t rx_iov[2]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* init msg to empty message */ |
| memset(&rx_msg, 0, sizeof(rx_msg)); |
| memset(&tx_msg, 0, sizeof(tx_msg)); |
| |
| /* read_msg on invalid (negative value) handle */ |
| rc = read_msg(INVALID_IPC_HANDLE, 0, 0, &rx_msg); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "read_msg on invalid handle"); |
| |
| rc = read_msg(INVALID_IPC_HANDLE, 0, 0, NULL); |
| EXPECT_EQ(ERR_FAULT, rc, "read_msg on invalid handle"); |
| |
| /* |
| * calling read_msg on an invalid (out of range) handle |
| * |
| * check handling of the following cases: |
| * - handle is on the upper boundary of valid handle range |
| * - handle is above of the upper boundary of valid handle range |
| * - handle is below of valid handle range |
| * |
| * in all cases, the expected result is ERR_BAD_HANDLE error. |
| */ |
| rc = read_msg(handle_base + MAX_USER_HANDLES, 0, 0, &rx_msg); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "read_msg on bad handle"); |
| |
| rc = read_msg(handle_base + MAX_USER_HANDLES + 1, 0, 0, &rx_msg); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "read_msg on bad handle"); |
| |
| rc = read_msg(handle_base - 1, 0, 0, &rx_msg); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "read_msg on bad handle"); |
| |
| /* calling read_msg with NULL msg should fail for any handle */ |
| rc = read_msg(handle_base + MAX_USER_HANDLES, 0, 0, NULL); |
| EXPECT_EQ(ERR_FAULT, rc, "read_msg on NULL msg"); |
| |
| /* send_msg on non-existing handle that is in valid range */ |
| for (unsigned int i = FIRST_FREE_HANDLE; i < MAX_USER_HANDLES; i++) { |
| rc = read_msg(handle_base + i, 0, 0, &rx_msg); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "read_msg on non existing handle"); |
| |
| /* calling send_msg with NULL msg should fail for any handle */ |
| rc = read_msg(handle_base + i, 0, 0, NULL); |
| EXPECT_EQ(ERR_FAULT, rc, "read_msg on NULL msg"); |
| } |
| |
| /* calling read_msg on port handle should fail |
| because read_msg is only applicable to channels */ |
| sprintf(path, "%s.main.%s", SRV_PATH_BASE, "datasink"); |
| rc = port_create(path, 2, MAX_PORT_BUF_SIZE, IPC_PORT_ALLOW_TA_CONNECT); |
| EXPECT_GT_ZERO(rc, "create datasink port"); |
| port = (handle_t)rc; |
| |
| rc = read_msg(port, 0, 0, &rx_msg); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "read_msg on port"); |
| close(port); |
| |
| /* open connection to echo service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "echo"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| chan = (handle_t)rc; |
| |
| /* NULL msg on valid channel */ |
| rc = read_msg(chan, 0, 0, NULL); |
| EXPECT_EQ(ERR_FAULT, rc, "read_msg on NULL msg"); |
| |
| /* read_msg on invalid msg id */ |
| rc = read_msg(chan, 0, 0, &rx_msg); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "read_msg on invalid msg id"); |
| |
| rc = read_msg(chan, 1000, 0, &rx_msg); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "read_msg on invalid msg id"); |
| |
| /* send a message to echo service */ |
| memset(tx_buf, 0x55, sizeof(tx_buf)); |
| tx_iov.base = tx_buf; |
| tx_iov.len = sizeof(tx_buf); |
| tx_msg.num_iov = 1; |
| tx_msg.iov = &tx_iov; |
| tx_msg.num_handles = 0; |
| tx_msg.handles = NULL; |
| |
| rc = send_msg(chan, &tx_msg); |
| EXPECT_EQ(64, rc, "sending msg to echo"); |
| |
| /* and wait for response */ |
| rc = wait(chan, &uevt, 1000); |
| EXPECT_EQ(NO_ERROR, rc, "waiting on echo response"); |
| EXPECT_EQ(chan, uevt.handle, "wait on channel"); |
| |
| rc = get_msg(chan, &inf); |
| EXPECT_EQ(NO_ERROR, rc, "getting echo msg"); |
| EXPECT_EQ(sizeof(tx_buf), inf.len, "echo message reply length"); |
| |
| /* now we have valid message with valid id */ |
| |
| rx_iov[0].len = sizeof(rx_buf) / 2; |
| rx_iov[1].len = sizeof(rx_buf) / 2; |
| |
| /* read message with invalid iovec array */ |
| rx_msg.iov = NULL; |
| rx_msg.num_iov = 2; |
| rc = read_msg(chan, inf.id, 0, &rx_msg); |
| EXPECT_EQ(ERR_FAULT, rc, "read with invalid iovec array"); |
| |
| /* read with invalid iovec entry */ |
| rx_iov[0].base = NULL; |
| rx_iov[1].base = NULL; |
| rx_msg.iov = rx_iov; |
| rc = read_msg(chan, inf.id, 0, &rx_msg); |
| EXPECT_EQ(ERR_FAULT, rc, "read with invalid iovec"); |
| |
| rx_iov[0].base = rx_buf; |
| rx_iov[1].base = NULL; |
| rc = read_msg(chan, inf.id, 0, &rx_msg); |
| EXPECT_EQ(ERR_FAULT, rc, "read with invalid iovec"); |
| |
| rx_iov[0].base = rx_buf; |
| rx_iov[1].base = rx_buf + sizeof(rx_buf) / 2; |
| |
| /* read with invalid offset with valid iovec array */ |
| rc = read_msg(chan, inf.id, inf.len + 1, &rx_msg); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "read with invalid offset"); |
| |
| /* cleanup */ |
| rc = put_msg(chan, inf.id); |
| EXPECT_EQ(NO_ERROR, rc, "putting echo msg"); |
| |
| rc = close(chan); |
| EXPECT_EQ(NO_ERROR, rc, "close channel"); |
| |
| TEST_END |
| } |
| |
| static void run_end_to_end_msg_test(void) { |
| int rc; |
| handle_t chan; |
| uevent_t uevt; |
| char path[MAX_PORT_PATH_LEN]; |
| uint8_t tx_buf[64]; |
| uint8_t rx_buf[64]; |
| ipc_msg_info_t inf; |
| ipc_msg_t tx_msg; |
| iovec_t tx_iov; |
| ipc_msg_t rx_msg; |
| iovec_t rx_iov; |
| |
| TEST_BEGIN(__func__); |
| |
| tx_iov.base = tx_buf; |
| tx_iov.len = sizeof(tx_buf); |
| tx_msg.num_iov = 1; |
| tx_msg.iov = &tx_iov; |
| tx_msg.num_handles = 0; |
| tx_msg.handles = NULL; |
| |
| rx_iov.base = rx_buf; |
| rx_iov.len = sizeof(rx_buf); |
| rx_msg.num_iov = 1; |
| rx_msg.iov = &rx_iov; |
| rx_msg.num_handles = 0; |
| rx_msg.handles = NULL; |
| |
| memset(tx_buf, 0x55, sizeof(tx_buf)); |
| memset(rx_buf, 0xaa, sizeof(rx_buf)); |
| |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "echo"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to echo"); |
| |
| if (rc >= 0) { |
| unsigned int tx_cnt = 0; |
| unsigned int rx_cnt = 0; |
| |
| chan = (handle_t)rc; |
| |
| /* send 10000 messages synchronously, waiting for reply |
| for each one |
| */ |
| tx_cnt = 10000; |
| while (tx_cnt) { |
| /* send a message */ |
| rc = send_msg(chan, &tx_msg); |
| EXPECT_EQ(64, rc, "sending msg to echo"); |
| |
| /* wait for response */ |
| rc = wait(chan, &uevt, 1000); |
| EXPECT_EQ(NO_ERROR, rc, "waiting on echo response"); |
| EXPECT_EQ(chan, uevt.handle, "wait on channel"); |
| |
| /* get a reply */ |
| rc = get_msg(chan, &inf); |
| EXPECT_EQ(NO_ERROR, rc, "getting echo msg"); |
| |
| /* read reply data */ |
| rc = read_msg(chan, inf.id, 0, &rx_msg); |
| EXPECT_EQ(64, rc, "reading echo msg"); |
| |
| /* discard reply */ |
| rc = put_msg(chan, inf.id); |
| EXPECT_EQ(NO_ERROR, rc, "putting echo msg"); |
| |
| tx_cnt--; |
| } |
| |
| /* send/receive 10000 messages asynchronously. */ |
| rx_cnt = tx_cnt = 10000; |
| while (tx_cnt || rx_cnt) { |
| /* send messages until all buffers are full */ |
| while (tx_cnt) { |
| rc = send_msg(chan, &tx_msg); |
| if (rc == ERR_NOT_ENOUGH_BUFFER) |
| break; /* no more space */ |
| EXPECT_EQ(64, rc, "sending msg to echo"); |
| if (rc != 64) |
| goto abort_test; |
| tx_cnt--; |
| } |
| |
| /* wait for reply msg or room */ |
| rc = wait(chan, &uevt, 1000); |
| EXPECT_EQ(NO_ERROR, rc, "waiting for reply"); |
| EXPECT_EQ(chan, uevt.handle, "wait on channel"); |
| |
| /* drain all messages */ |
| while (rx_cnt) { |
| /* get a reply */ |
| rc = get_msg(chan, &inf); |
| if (rc == ERR_NO_MSG) |
| break; /* no more messages */ |
| |
| EXPECT_EQ(NO_ERROR, rc, "getting echo msg"); |
| |
| /* read reply data */ |
| rc = read_msg(chan, inf.id, 0, &rx_msg); |
| EXPECT_EQ(64, rc, "reading echo msg"); |
| |
| /* discard reply */ |
| rc = put_msg(chan, inf.id); |
| EXPECT_EQ(NO_ERROR, rc, "putting echo msg"); |
| |
| rx_cnt--; |
| } |
| |
| if (!_all_ok) |
| break; |
| } |
| |
| abort_test: |
| EXPECT_EQ(0, tx_cnt, "tx_cnt"); |
| EXPECT_EQ(0, rx_cnt, "rx_cnt"); |
| |
| rc = close(chan); |
| EXPECT_EQ(NO_ERROR, rc, "close channel"); |
| } |
| |
| TEST_END |
| } |
| |
| /****************************************************************************/ |
| |
| static void run_hset_create_test(void) { |
| handle_t hset1; |
| handle_t hset2; |
| |
| TEST_BEGIN(__func__); |
| |
| hset1 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset1, "create handle set1"); |
| |
| hset2 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset2, "create handle set2"); |
| |
| close(hset1); |
| close(hset2); |
| |
| TEST_END |
| } |
| |
| static void run_hset_add_mod_del_test(void) { |
| int rc; |
| handle_t hset1; |
| handle_t hset2; |
| |
| TEST_BEGIN(__func__); |
| |
| hset1 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset1, "create handle set1"); |
| |
| hset2 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset2, "create handle set2"); |
| |
| ABORT_IF_NOT_OK(abort_test); |
| |
| uevent_t evt = { |
| .handle = hset2, |
| .event = ~0, |
| .cookie = NULL, |
| }; |
| |
| /* add handle to handle set */ |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "hset add"); |
| |
| /* modify handle attributes in handle set */ |
| rc = handle_set_ctrl(hset1, HSET_MOD, &evt); |
| EXPECT_EQ(0, rc, "hset mod"); |
| |
| /* remove handle from handle set */ |
| rc = handle_set_ctrl(hset1, HSET_DEL, &evt); |
| EXPECT_EQ(0, rc, "hset del"); |
| |
| abort_test: |
| close(hset1); |
| close(hset2); |
| |
| TEST_END |
| } |
| |
| static void run_hset_add_self_test(void) { |
| int rc; |
| handle_t hset1; |
| |
| TEST_BEGIN(__func__); |
| |
| hset1 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset1, "create handle set1"); |
| |
| ABORT_IF_NOT_OK(abort_test); |
| |
| uevent_t evt = { |
| .handle = hset1, |
| .event = ~0, |
| .cookie = NULL, |
| }; |
| |
| /* add handle to handle set */ |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "hset add self"); |
| |
| abort_test: |
| close(hset1); |
| |
| TEST_END |
| } |
| |
| static void run_hset_add_loop_test(void) { |
| int rc; |
| handle_t hset1; |
| handle_t hset2; |
| handle_t hset3; |
| |
| TEST_BEGIN(__func__); |
| |
| hset1 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset1, "create handle set1"); |
| |
| hset2 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset2, "create handle set2"); |
| |
| hset3 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset3, "create handle set3"); |
| |
| ABORT_IF_NOT_OK(abort_test); |
| |
| uevent_t evt = { |
| .handle = hset2, |
| .event = ~0, |
| .cookie = NULL, |
| }; |
| |
| /* add hset2 to hset1 */ |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add hset2 to hset1"); |
| |
| /* add hset3 to hset2 */ |
| evt.handle = hset3; |
| rc = handle_set_ctrl(hset2, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add hset3 to hset2"); |
| |
| /* add hset1 to hset3 */ |
| evt.handle = hset1; |
| rc = handle_set_ctrl(hset3, HSET_ADD, &evt); |
| EXPECT_EQ(ERR_INVALID_ARGS, rc, "add hset1 to hset3"); |
| |
| abort_test: |
| close(hset2); |
| close(hset1); |
| close(hset3); |
| |
| TEST_END |
| } |
| |
| static void run_hset_add_duplicate_test(void) { |
| int rc; |
| handle_t hset1; |
| handle_t hset2; |
| |
| TEST_BEGIN(__func__); |
| |
| hset1 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset1, "create handle set1"); |
| |
| hset2 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset2, "create handle set2"); |
| |
| ABORT_IF_NOT_OK(abort_test); |
| |
| uevent_t evt = { |
| .handle = hset2, |
| .event = ~0, |
| .cookie = NULL, |
| }; |
| |
| /* add hset2 to hset1 */ |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add hset2 to hset1"); |
| |
| /* add hset2 to hset1 again */ |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(ERR_ALREADY_EXISTS, rc, "add hset2 to hset1"); |
| |
| abort_test: |
| close(hset1); |
| close(hset2); |
| |
| TEST_END |
| } |
| |
| static void run_hset_wait_on_empty_set_test(void) { |
| int rc; |
| uevent_t evt; |
| handle_t hset1; |
| |
| TEST_BEGIN(__func__); |
| |
| hset1 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset1, "create hset"); |
| |
| ABORT_IF_NOT_OK(abort_test); |
| |
| /* wait with zero timeout */ |
| rc = wait(hset1, &evt, 0); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "wait on empty hset"); |
| |
| /* wait with non-zero timeout */ |
| rc = wait(hset1, &evt, 100); |
| EXPECT_EQ(ERR_NOT_FOUND, rc, "wait on empty hset"); |
| |
| close(hset1); |
| |
| abort_test: |
| TEST_END |
| } |
| |
| static void run_hset_wait_on_non_empty_set_test(void) { |
| int rc; |
| handle_t hset1; |
| handle_t hset2; |
| |
| TEST_BEGIN(__func__); |
| |
| hset1 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset1, "create handle set1"); |
| |
| hset2 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset2, "create handle set2"); |
| |
| ABORT_IF_NOT_OK(abort_test); |
| |
| uevent_t evt = { |
| .handle = hset2, |
| .event = ~0, |
| .cookie = NULL, |
| }; |
| |
| /* add hset2 to hset1 */ |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add hset2 to hset1"); |
| |
| /* wait with zero timeout on hset1 */ |
| rc = wait(hset1, &evt, 0); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "wait on empty hset"); |
| |
| /* wait with non-zero timeout on hset1 */ |
| rc = wait(hset1, &evt, 100); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "wait on empty hset"); |
| |
| abort_test: |
| close(hset1); |
| close(hset2); |
| |
| TEST_END |
| } |
| |
| static void run_hset_add_chan_test(void) { |
| int rc; |
| uevent_t evt; |
| handle_t hset1; |
| handle_t hset2; |
| handle_t chan1; |
| handle_t chan2; |
| void* cookie1 = (void*)"cookie1"; |
| void* cookie2 = (void*)"cookie2"; |
| void* cookie11 = (void*)"cookie11"; |
| void* cookie12 = (void*)"cookie12"; |
| void* cookie21 = (void*)"cookie21"; |
| void* cookie22 = (void*)"cookie22"; |
| void* cookiehs2 = (void*)"cookiehs2"; |
| uint8_t buf0[64]; |
| iovec_t iov; |
| ipc_msg_t msg; |
| |
| TEST_BEGIN(__func__); |
| |
| /* prepare test buffer */ |
| fill_test_buf(buf0, sizeof(buf0), 0x55); |
| |
| chan1 = sync_connect(SRV_PATH_BASE ".srv.echo", 1000); |
| EXPECT_GT_ZERO((int)chan1, "connect to echo chan1"); |
| |
| rc = set_cookie(chan1, cookie1); |
| EXPECT_EQ(0, rc, "cookie1"); |
| |
| chan2 = sync_connect(SRV_PATH_BASE ".srv.echo", 1000); |
| EXPECT_GT_ZERO((int)chan2, "connect to echo chan2"); |
| |
| rc = set_cookie(chan2, cookie2); |
| EXPECT_EQ(0, rc, "cookie2"); |
| |
| /* send message over chan1 and chan2 */ |
| iov.base = buf0; |
| iov.len = sizeof(buf0); |
| msg.num_handles = 0; |
| msg.handles = NULL; |
| msg.num_iov = 1; |
| msg.iov = &iov; |
| |
| rc = send_msg(chan1, &msg); |
| EXPECT_EQ(64, rc, "send over chan1"); |
| |
| rc = send_msg(chan2, &msg); |
| EXPECT_EQ(64, rc, "send over chan2"); |
| |
| hset1 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset1, "create handle set1"); |
| |
| hset2 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset2, "create handle set2"); |
| |
| ABORT_IF_NOT_OK(abort_test); |
| |
| /* chan1 to hset2 */ |
| evt.handle = chan1; |
| evt.event = ~0; |
| evt.cookie = cookie12; |
| rc = handle_set_ctrl(hset2, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add hset2 to hset1"); |
| |
| /* chan2 to hset2 */ |
| evt.handle = chan2; |
| evt.event = ~0; |
| evt.cookie = cookie22; |
| rc = handle_set_ctrl(hset2, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add hset2 to hset1"); |
| |
| /* add hset2 to hset1 */ |
| evt.handle = hset2; |
| evt.event = ~0; |
| evt.cookie = cookiehs2; |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add hset2 to hset1"); |
| |
| /* chan1 to hset1 */ |
| evt.handle = chan1; |
| evt.event = ~0; |
| evt.cookie = cookie11; |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add hset2 to hset1"); |
| |
| /* chan2 to hset1 */ |
| evt.handle = chan2; |
| evt.event = ~0; |
| evt.cookie = cookie21; |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add hset2 to hset1"); |
| |
| /* wait on chan1 directly */ |
| rc = wait(chan1, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait on chan1"); |
| EXPECT_EQ(chan1, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie1, evt.cookie, "event.cookie"); |
| |
| /* wait on chan2 directly */ |
| rc = wait(chan2, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait on chan2"); |
| EXPECT_EQ(chan2, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie2, evt.cookie, "event.cookie"); |
| |
| /* wait on hset1 */ |
| rc = wait(hset1, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait on hset1"); |
| EXPECT_EQ(hset2, evt.handle, "event.handle"); |
| EXPECT_EQ(cookiehs2, evt.cookie, "event.cookie"); |
| |
| /* wait on hset1 again (chan1 should be ready) */ |
| rc = wait(hset1, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait on hset1"); |
| EXPECT_EQ(chan1, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie11, evt.cookie, "event.cookie"); |
| |
| /* wait on hset1 again (chan2 should be ready) */ |
| rc = wait(hset1, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait on hset1"); |
| EXPECT_EQ(chan2, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie21, evt.cookie, "event.cookie"); |
| |
| /* wait on hset1 again (hset2 should be ready) */ |
| rc = wait(hset1, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait on hset1"); |
| EXPECT_EQ(hset2, evt.handle, "event.handle"); |
| EXPECT_EQ(cookiehs2, evt.cookie, "event.cookie"); |
| |
| /* wait on hset2 (chan1 should be ready) */ |
| rc = wait(hset2, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait on hset2"); |
| EXPECT_EQ(chan1, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie12, evt.cookie, "event.cookie"); |
| |
| /* wait on hset2 again (chan2 should be ready) */ |
| rc = wait(hset2, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait on hset2"); |
| EXPECT_EQ(chan2, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie22, evt.cookie, "event.cookie"); |
| |
| /* wait on hset2 again (chan1 should be ready) */ |
| rc = wait(hset2, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait on chan1"); |
| EXPECT_EQ(chan1, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie12, evt.cookie, "event.cookie"); |
| |
| abort_test: |
| close(chan1); |
| close(chan2); |
| close(hset1); |
| close(hset2); |
| |
| TEST_END |
| } |
| |
| static void run_hset_event_mask_test(void) { |
| int rc; |
| uevent_t evt; |
| handle_t hset1; |
| handle_t chan1; |
| void* cookie1 = (void*)"cookie1"; |
| void* cookie11 = (void*)"cookie11"; |
| uint8_t buf0[64]; |
| iovec_t iov; |
| ipc_msg_t msg; |
| |
| TEST_BEGIN(__func__); |
| |
| /* prepare test buffer */ |
| fill_test_buf(buf0, sizeof(buf0), 0x55); |
| |
| chan1 = sync_connect(SRV_PATH_BASE ".srv.echo", 1000); |
| EXPECT_GT_ZERO((int)chan1, "connect to echo"); |
| |
| rc = set_cookie(chan1, cookie1); |
| EXPECT_EQ(0, rc, "cookie1"); |
| |
| /* send message over chan1 and chan2 */ |
| iov.base = buf0; |
| iov.len = sizeof(buf0); |
| msg.num_handles = 0; |
| msg.handles = NULL; |
| msg.num_iov = 1; |
| msg.iov = &iov; |
| |
| rc = send_msg(chan1, &msg); |
| EXPECT_EQ(64, rc, "send over chan1"); |
| |
| hset1 = handle_set_create(); |
| EXPECT_GE_ZERO((int)hset1, "create handle set1"); |
| |
| ABORT_IF_NOT_OK(abort_test); |
| |
| /* chan1 to hset1 */ |
| evt.handle = chan1; |
| evt.event = ~0; |
| evt.cookie = cookie11; |
| rc = handle_set_ctrl(hset1, HSET_ADD, &evt); |
| EXPECT_EQ(0, rc, "add chan1 to hset1"); |
| |
| /* wait of chan1 handle */ |
| rc = wait(chan1, &evt, 100); |
| EXPECT_EQ(0, rc, "wait on chan1"); |
| EXPECT_EQ(chan1, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie1, evt.cookie, "event.cookie"); |
| |
| /* wait on hset1 (should get chan1) */ |
| rc = wait(hset1, &evt, 100); |
| EXPECT_EQ(0, rc, "wait on hset1"); |
| EXPECT_EQ(chan1, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie11, evt.cookie, "event.cookie"); |
| |
| /* mask off chan1 in hset1 */ |
| evt.handle = chan1; |
| evt.event = 0; |
| evt.cookie = cookie11; |
| rc = handle_set_ctrl(hset1, HSET_MOD, &evt); |
| EXPECT_EQ(0, rc, "mod chan1 in hset1"); |
| |
| /* wait on hset1 (should get chan1) */ |
| rc = wait(hset1, &evt, 100); |
| EXPECT_EQ(ERR_TIMED_OUT, rc, "wait on hset1"); |
| |
| /* unmask off chan1 in hset1 */ |
| evt.handle = chan1; |
| evt.event = ~0; |
| evt.cookie = cookie11; |
| rc = handle_set_ctrl(hset1, HSET_MOD, &evt); |
| EXPECT_EQ(0, rc, "mod chan1 in hset1"); |
| |
| /* wait on hset1 (should get chan1) */ |
| rc = wait(hset1, &evt, 100); |
| EXPECT_EQ(0, rc, "wait on hset1"); |
| EXPECT_EQ(chan1, evt.handle, "event.handle"); |
| EXPECT_EQ(cookie11, evt.cookie, "event.cookie"); |
| |
| abort_test: |
| close(chan1); |
| close(hset1); |
| TEST_END |
| } |
| |
| /****************************************************************************/ |
| |
| static void run_send_handle_test(void) { |
| int rc; |
| iovec_t iov; |
| ipc_msg_t msg; |
| handle_t hchan1; |
| handle_t hchan2; |
| uint8_t buf0[64]; |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* prepare test buffer */ |
| fill_test_buf(buf0, sizeof(buf0), 0x55); |
| |
| /* open connection to datasink service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "datasink"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| ABORT_IF_NOT_OK(err_connect1); |
| hchan1 = (handle_t)rc; |
| |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| ABORT_IF_NOT_OK(err_connect2); |
| hchan2 = (handle_t)rc; |
| |
| /* send hchan2 handle over hchan1 connection */ |
| iov.base = buf0; |
| iov.len = sizeof(buf0); |
| msg.iov = &iov; |
| msg.num_iov = 1; |
| msg.handles = &hchan2; |
| msg.num_handles = 1; |
| |
| /* send and wait a bit */ |
| rc = send_msg(hchan1, &msg); |
| EXPECT_EQ(64, rc, "send handle"); |
| trusty_nanosleep(0, 0, 100 * MSEC); |
| |
| /* send it again and close it */ |
| rc = send_msg(hchan1, &msg); |
| EXPECT_EQ(64, rc, "send handle"); |
| rc = close(hchan2); |
| EXPECT_EQ(NO_ERROR, rc, "close chan2"); |
| |
| err_connect2: |
| rc = close(hchan1); |
| EXPECT_EQ(NO_ERROR, rc, "close chan1"); |
| err_connect1: |
| TEST_END |
| } |
| |
| static void run_send_handle_negative_test(void) { |
| int rc; |
| ipc_msg_t msg; |
| handle_t hchan; |
| handle_t hsend[10]; |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* open connection to datasink service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "datasink"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| ABORT_IF_NOT_OK(err_connect); |
| hchan = (handle_t)rc; |
| |
| for (unsigned int i = 0; i < countof(hsend); i++) |
| hsend[i] = hchan; |
| |
| /* send 8 copies of yourself to datasync (should be fine) */ |
| msg.iov = NULL; |
| msg.num_iov = 0; |
| msg.handles = &hsend[0]; |
| msg.num_handles = 8; |
| rc = send_msg(hchan, &msg); |
| EXPECT_EQ(0, rc, "send handle"); |
| |
| /* send 8 copies of yourself to datasync with handle poiting to NULL*/ |
| msg.iov = NULL; |
| msg.num_iov = 0; |
| msg.handles = NULL; |
| msg.num_handles = 8; |
| rc = send_msg(hchan, &msg); |
| EXPECT_EQ(ERR_FAULT, rc, "send handle"); |
| |
| /* call with invalid handle should return ERR_FAULT */ |
| msg.handles = (handle_t*)0x100; |
| msg.num_handles = 8; |
| rc = send_msg(hchan, &msg); |
| EXPECT_EQ(ERR_FAULT, rc, "send handle"); |
| |
| /* send more then 8, should fail */ |
| msg.handles = &hsend[0]; |
| msg.num_handles = 10; |
| rc = send_msg(hchan, &msg); |
| EXPECT_EQ(ERR_TOO_BIG, rc, "send handle"); |
| |
| rc = close(hchan); |
| EXPECT_EQ(NO_ERROR, rc, "close chan"); |
| err_connect: |
| TEST_END |
| } |
| |
| static void run_recv_handle_test(void) { |
| int rc; |
| handle_t hchan1; |
| handle_t hchan2; |
| handle_t hrecv[2]; |
| uint8_t buf0[64]; |
| iovec_t iov; |
| ipc_msg_t msg; |
| uevent_t evt; |
| ipc_msg_info_t inf; |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* prepare test buffer */ |
| fill_test_buf(buf0, sizeof(buf0), 0x55); |
| |
| /* open connection to echo service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "echo"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to echo"); |
| ABORT_IF_NOT_OK(err_connect1); |
| hchan1 = (handle_t)rc; |
| |
| /* open second connection to echo service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "echo"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to echo"); |
| ABORT_IF_NOT_OK(err_connect2); |
| hchan2 = (handle_t)rc; |
| |
| /* send message with handle */ |
| iov.base = buf0; |
| iov.len = sizeof(buf0); |
| msg.iov = &iov; |
| msg.num_iov = 1; |
| msg.handles = &hchan2; |
| msg.num_handles = 1; |
| |
| rc = send_msg(hchan1, &msg); |
| EXPECT_EQ(64, rc, "send_handle"); |
| |
| /* wait for reply */ |
| rc = wait(hchan1, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait for reply"); |
| EXPECT_EQ(hchan1, evt.handle, "event.handle"); |
| |
| /* get reply message */ |
| rc = get_msg(hchan1, &inf); |
| EXPECT_EQ(NO_ERROR, rc, "getting echo reply"); |
| EXPECT_EQ(sizeof(buf0), inf.len, "reply len"); |
| EXPECT_EQ(1, inf.num_handles, "reply num_handles"); |
| |
| /* read reply data and no handles */ |
| hrecv[0] = INVALID_IPC_HANDLE; |
| hrecv[1] = INVALID_IPC_HANDLE; |
| msg.handles = &hrecv[0]; |
| msg.num_handles = 0; |
| rc = read_msg(hchan1, inf.id, 0, &msg); |
| EXPECT_EQ(64, rc, "reading echo reply"); |
| |
| rc = close(hrecv[0]); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "close reply handle"); |
| |
| rc = close(hrecv[1]); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "close reply handle"); |
| |
| /* read reply data and 1 handle */ |
| hrecv[0] = INVALID_IPC_HANDLE; |
| hrecv[1] = INVALID_IPC_HANDLE; |
| msg.handles = &hrecv[0]; |
| msg.num_handles = 1; |
| rc = read_msg(hchan1, inf.id, 0, &msg); |
| EXPECT_EQ(64, rc, "reading echo reply"); |
| |
| rc = close(hrecv[0]); |
| EXPECT_EQ(0, rc, "close reply handle"); |
| |
| rc = close(hrecv[1]); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "close reply handle"); |
| |
| /* read reply data and 2 handles (second one should be invalid) */ |
| hrecv[0] = INVALID_IPC_HANDLE; |
| hrecv[1] = INVALID_IPC_HANDLE; |
| msg.handles = &hrecv[0]; |
| msg.num_handles = 2; |
| rc = read_msg(hchan1, inf.id, 0, &msg); |
| EXPECT_EQ(64, rc, "reading echo reply"); |
| |
| rc = close(hrecv[0]); |
| EXPECT_EQ(0, rc, "close reply handle"); |
| |
| rc = close(hrecv[1]); |
| EXPECT_EQ(ERR_BAD_HANDLE, rc, "close reply handle"); |
| |
| /* read 1 handle with no data */ |
| hrecv[0] = INVALID_IPC_HANDLE; |
| hrecv[1] = INVALID_IPC_HANDLE; |
| msg.num_iov = 0; |
| msg.handles = &hrecv[0]; |
| msg.num_handles = 1; |
| rc = read_msg(hchan1, inf.id, 0, &msg); |
| EXPECT_EQ(0, rc, "reading echo reply"); |
| |
| EXPECT_EQ(INVALID_IPC_HANDLE, hrecv[1], "reading echo reply"); |
| |
| /* read same handle for the second time */ |
| msg.handles = &hrecv[1]; |
| msg.num_handles = 1; |
| rc = read_msg(hchan1, inf.id, 0, &msg); |
| EXPECT_EQ(0, rc, "reading echo reply"); |
| |
| rc = close(hrecv[0]); |
| EXPECT_EQ(0, rc, "close reply handle"); |
| |
| rc = close(hrecv[1]); |
| EXPECT_EQ(0, rc, "close reply handle"); |
| |
| /* discard reply */ |
| rc = put_msg(hchan1, inf.id); |
| EXPECT_EQ(NO_ERROR, rc, "putting echo reply"); |
| |
| close(hchan2); |
| EXPECT_EQ(NO_ERROR, rc, "close chan2"); |
| err_connect2: |
| close(hchan1); |
| EXPECT_EQ(NO_ERROR, rc, "close chan1"); |
| err_connect1: |
| TEST_END |
| } |
| |
| static void run_recv_handle_negative_test(void) { |
| int rc; |
| handle_t hchan1; |
| ipc_msg_t msg; |
| uevent_t evt; |
| ipc_msg_info_t inf; |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* open connection to echo service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "echo"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to echo"); |
| ABORT_IF_NOT_OK(err_connect1); |
| hchan1 = (handle_t)rc; |
| |
| /* send message with handle attached */ |
| msg.iov = NULL; |
| msg.num_iov = 0; |
| msg.handles = &hchan1; |
| msg.num_handles = 1; |
| |
| rc = send_msg(hchan1, &msg); |
| EXPECT_EQ(0, rc, "send_handle"); |
| |
| /* wait for reply */ |
| rc = wait(hchan1, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait for reply"); |
| EXPECT_EQ(hchan1, evt.handle, "event.handle"); |
| |
| /* get reply message */ |
| rc = get_msg(hchan1, &inf); |
| EXPECT_EQ(NO_ERROR, rc, "getting echo reply"); |
| EXPECT_EQ(0, inf.len, "reply len"); |
| EXPECT_EQ(1, inf.num_handles, "reply num_handles"); |
| |
| /* read reply data with handles pointing to NULL */ |
| msg.handles = NULL; |
| msg.num_handles = 1; |
| rc = read_msg(hchan1, inf.id, 0, &msg); |
| EXPECT_EQ(ERR_FAULT, rc, "reading echo reply"); |
| |
| /* read reply data and bad handle ptr */ |
| msg.handles = (handle_t*)0x100; |
| msg.num_handles = 1; |
| rc = read_msg(hchan1, inf.id, 0, &msg); |
| EXPECT_EQ(ERR_FAULT, rc, "reading echo reply"); |
| |
| /* discard reply */ |
| rc = put_msg(hchan1, inf.id); |
| EXPECT_EQ(NO_ERROR, rc, "putting echo reply"); |
| |
| rc = close(hchan1); |
| EXPECT_EQ(NO_ERROR, rc, "close chan1"); |
| err_connect1: |
| TEST_END |
| } |
| |
| static void run_send_handle_bulk_test(void) { |
| int rc; |
| iovec_t iov; |
| ipc_msg_t msg; |
| handle_t hchan1; |
| handle_t hchan2; |
| uint8_t buf0[64]; |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* prepare test buffer */ |
| fill_test_buf(buf0, sizeof(buf0), 0x55); |
| |
| /* open connection to datasink service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "datasink"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| ABORT_IF_NOT_OK(err_connect1); |
| hchan1 = (handle_t)rc; |
| |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| ABORT_IF_NOT_OK(err_connect2); |
| hchan2 = (handle_t)rc; |
| |
| /* send hchan2 handle over hchan1 connection */ |
| iov.base = buf0; |
| iov.len = sizeof(buf0); |
| msg.iov = &iov; |
| msg.num_iov = 1; |
| msg.handles = &hchan2; |
| msg.num_handles = 1; |
| |
| for (unsigned int i = 0; (i < 10000) && _all_ok; i++) { |
| while (_all_ok) { |
| rc = send_msg(hchan1, &msg); |
| if (rc == ERR_NOT_ENOUGH_BUFFER) { /* wait for room */ |
| uevent_t uevt; |
| unsigned int exp_event = IPC_HANDLE_POLL_SEND_UNBLOCKED; |
| rc = wait(hchan1, &uevt, 10000); |
| EXPECT_EQ(NO_ERROR, rc, "waiting for space"); |
| EXPECT_EQ(hchan1, uevt.handle, "waiting for space"); |
| EXPECT_EQ(exp_event, uevt.event, "waiting for space"); |
| } else { |
| EXPECT_EQ(64, rc, "send_msg bulk"); |
| break; |
| } |
| } |
| } |
| rc = close(hchan2); |
| EXPECT_EQ(NO_ERROR, rc, "close chan2"); |
| |
| /* repeate the same while closing handle after sending it */ |
| for (unsigned int i = 0; (i < 10000) && _all_ok; i++) { |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to datasink"); |
| ABORT_IF_NOT_OK(err_connect2); |
| hchan2 = (handle_t)rc; |
| |
| /* send hchan2 handle over hchan1 connection */ |
| iov.base = buf0; |
| iov.len = sizeof(buf0); |
| msg.iov = &iov; |
| msg.num_iov = 1; |
| msg.handles = &hchan2; |
| msg.num_handles = 1; |
| |
| while (_all_ok) { |
| rc = send_msg(hchan1, &msg); |
| if (rc == ERR_NOT_ENOUGH_BUFFER) { /* wait for room */ |
| uevent_t uevt; |
| unsigned int exp_event = IPC_HANDLE_POLL_SEND_UNBLOCKED; |
| rc = wait(hchan1, &uevt, 10000); |
| EXPECT_EQ(NO_ERROR, rc, "waiting for space"); |
| EXPECT_EQ(hchan1, uevt.handle, "waiting for space"); |
| EXPECT_EQ(exp_event, uevt.event, "waiting for space"); |
| } else { |
| EXPECT_EQ(64, rc, "send_msg bulk"); |
| break; |
| } |
| } |
| rc = close(hchan2); |
| EXPECT_EQ(NO_ERROR, rc, "close chan2"); |
| } |
| |
| err_connect2: |
| rc = close(hchan1); |
| EXPECT_EQ(NO_ERROR, rc, "close chan1"); |
| err_connect1: |
| TEST_END |
| } |
| |
| static void run_echo_handle_bulk_test(void) { |
| int rc; |
| handle_t hchan1; |
| handle_t hchan2; |
| handle_t hrecv; |
| uint8_t buf0[64]; |
| iovec_t iov; |
| ipc_msg_t msg; |
| uevent_t evt; |
| ipc_msg_info_t inf; |
| char path[MAX_PORT_PATH_LEN]; |
| |
| TEST_BEGIN(__func__); |
| |
| /* prepare test buffer */ |
| fill_test_buf(buf0, sizeof(buf0), 0x55); |
| |
| /* open connection to echo service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "echo"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to echo"); |
| ABORT_IF_NOT_OK(err_connect1); |
| hchan1 = (handle_t)rc; |
| |
| /* open second connection to echo service */ |
| sprintf(path, "%s.srv.%s", SRV_PATH_BASE, "echo"); |
| rc = sync_connect(path, 1000); |
| EXPECT_GT_ZERO(rc, "connect to echo"); |
| ABORT_IF_NOT_OK(err_connect2); |
| hchan2 = (handle_t)rc; |
| |
| /* send the same handle 10000 times */ |
| for (unsigned int i = 0; (i < 10000) && _all_ok; i++) { |
| /* send message with handle */ |
| iov.base = buf0; |
| iov.len = sizeof(buf0); |
| msg.iov = &iov; |
| msg.num_iov = 1; |
| msg.handles = &hchan2; |
| msg.num_handles = 1; |
| |
| while (_all_ok) { |
| rc = send_msg(hchan1, &msg); |
| EXPECT_EQ(64, rc, "send_handle"); |
| if (rc == ERR_NOT_ENOUGH_BUFFER) { /* wait for room */ |
| uevent_t uevt; |
| unsigned int exp_event = IPC_HANDLE_POLL_SEND_UNBLOCKED; |
| rc = wait(hchan1, &uevt, 10000); |
| EXPECT_EQ(NO_ERROR, rc, "waiting for space"); |
| EXPECT_EQ(hchan1, uevt.handle, "waiting for space"); |
| EXPECT_EQ(exp_event, uevt.event, "waiting for space"); |
| } else { |
| EXPECT_EQ(64, rc, "send_msg bulk"); |
| break; |
| } |
| } |
| |
| /* wait for reply */ |
| rc = wait(hchan1, &evt, 1000); |
| EXPECT_EQ(0, rc, "wait for reply"); |
| EXPECT_EQ(hchan1, evt.handle, "event.handle"); |
| |
| /* get reply message */ |
| rc = get_msg(hchan1, &inf); |
| EXPECT_EQ(NO_ERROR, rc, "getting echo reply"); |
| EXPECT_EQ(sizeof(buf0), inf.len, "reply len"); |
| EXPECT_EQ(1, inf.num_handles, "reply num_handles"); |
| |
| /* read reply data and 1 handle */ |
| hrecv = INVALID_IPC_HANDLE; |
| msg.handles = &hrecv; |
| msg.num_handles = 1; |
| rc = read_msg(hchan1, inf.id, 0, &msg); |
| EXPECT_EQ(64, rc, "reading echo reply"); |
| |
| /* discard reply */ |
| rc = put_msg(hchan1, inf.id); |
| EXPECT_EQ(NO_ERROR, rc, "putting echo reply"); |
| |
| /* close received handle */ |
| rc = close(hrecv); |
| EXPECT_EQ(0, rc, "close reply handle"); |
| } |
| |
| rc = close(hchan2); |
| EXPECT_EQ(NO_ERROR, rc, "close chan2"); |
| err_connect2: |
| rc = close(hchan1); |
| EXPECT_EQ(NO_ERROR, rc, "close chan1"); |
| err_connect1: |
| TEST_END |
| } |
| |
| /****************************************************************************/ |
| |
| /* |
| * |
| */ |
| static void run_all_tests(void) { |
| TLOGI("Run all unittest\n"); |
| |
| /* reset test state */ |
| _tests_total = 0; |
| _tests_failed = 0; |
| |
| /* handle sets: part 1 */ |
| run_hset_create_test(); |
| run_hset_add_mod_del_test(); |
| run_hset_add_self_test(); |
| run_hset_add_loop_test(); |
| run_hset_add_duplicate_test(); |
| run_hset_wait_on_empty_set_test(); |
| run_hset_wait_on_non_empty_set_test(); |
| |
| /* positive tests */ |
| run_port_create_test(); |
| run_wait_on_port_test(); |
| run_async_connect_test(); |
| run_connect_close_test(); |
| run_accept_test(); |
| run_send_msg_test(); |
| run_end_to_end_msg_test(); |
| |
| run_connect_close_by_peer_test("closer1"); |
| run_connect_close_by_peer_test("closer2"); |
| run_connect_close_by_peer_test("closer3"); |
| run_connect_selfie_test(); |
| run_connect_access_test(); |
| |
| /* negative tests */ |
| run_wait_negative_test(); |
| run_close_handle_negative_test(); |
| run_set_cookie_negative_test(); |
| run_port_create_negative_test(); |
| run_connect_negative_test(); |
| run_accept_negative_test(); |
| run_get_msg_negative_test(); |
| run_put_msg_negative_test(); |
| run_send_msg_negative_test(); |
| run_read_msg_negative_test(); |
| |
| /* handle sets: part 2 */ |
| run_hset_add_chan_test(); |
| run_hset_event_mask_test(); |
| |
| /* send handle */ |
| run_send_handle_test(); |
| run_send_handle_negative_test(); |
| run_recv_handle_test(); |
| run_recv_handle_negative_test(); |
| |
| run_send_handle_bulk_test(); |
| run_echo_handle_bulk_test(); |
| |
| TLOGI("Conditions checked: %d\n", _tests_total); |
| TLOGI("Conditions failed: %d\n", _tests_failed); |
| if (_tests_failed == 0) |
| TLOGI("All tests PASSED\n"); |
| else |
| TLOGI("Some tests FAILED\n"); |
| } |
| |
| static void kernel_wait_any_bug_workaround(void) { |
| int ret; |
| uevent_t event; |
| |
| /* HACK: clear stuck event on handleset */ |
| ret = wait_any(&event, 0); |
| if (ret == 0) { |
| TLOGI("retry ret %d, event handle %d, event 0x%x\n", ret, event.handle, |
| event.event); |
| ret = wait(event.handle, &event, 0); |
| TLOGI("nested ret %d, event handle %d, event 0x%x\n", ret, event.handle, |
| event.event); |
| } |
| } |
| |
| static bool run_test(struct unittest* test) { |
| /* |
| * HACK: unittest library uses a hset. First handle is one before the port |
| * handle |
| */ |
| handle_base = test->_port_handle - 1; |
| |
| kernel_wait_any_bug_workaround(); |
| |
| run_all_tests(); |
| return _tests_failed == 0; |
| } |
| |
| /* |
| * Application entry point |
| */ |
| int main(void) { |
| struct unittest ipc_unittest = { |
| .port_name = SRV_PATH_BASE ".ctrl", |
| .run_test = run_test, |
| }; |
| struct unittest* unittest = &ipc_unittest; |
| |
| TLOGI("Welcome to IPC unittest!!!\n"); |
| |
| return unittest_main(&unittest, 1); |
| } |