int socket_api_test_create_destroy(socket_stack_t stack, socket_address_family_t disable_family) { struct socket s; int afi, pfi; socket_error_t err; const struct socket_api * api = socket_get_api(stack); TEST_CLEAR(); if (!TEST_NEQ(api, NULL)) { // Test cannot continue without API. return 0; } err = api->init(); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { return 0; } // Create a socket for each address family for (afi = SOCKET_AF_UNINIT; afi <= SOCKET_AF_MAX; afi++) { socket_address_family_t af = static_cast<socket_address_family_t>(afi); // Create a socket for each protocol family for (pfi = SOCKET_PROTO_UNINIT; pfi <= SOCKET_PROTO_MAX; pfi++) { socket_proto_family_t pf = static_cast<socket_proto_family_t>(pfi); // Zero the implementation s.impl = NULL; err = api->create(&s, af, pf, &create_test_handler); // catch expected failing cases if (af == SOCKET_AF_UNINIT || af == SOCKET_AF_MAX || pf == SOCKET_PROTO_UNINIT || pf == SOCKET_PROTO_MAX || af == disable_family) { TEST_NEQ(err, SOCKET_ERROR_NONE); continue; } TEST_EQ(err, SOCKET_ERROR_NONE); if (!TEST_NEQ(s.impl, NULL)) { continue; } // Destroy the socket; err = api->destroy(&s); TEST_EQ(err, SOCKET_ERROR_NONE); // Zero the implementation s.impl = NULL; // Create a socket with a NULL handler err = api->create(&s, af, pf, NULL); TEST_NEQ(err, SOCKET_ERROR_NONE); TEST_EQ(s.impl, NULL); // A NULL impl is not explicitly an exception since it can be zeroed during disconnect // Destroy the socket err = api->destroy(&s); TEST_EQ(err, SOCKET_ERROR_NONE); // Try destroying a socket that hasn't been created s.impl = NULL; err = api->destroy(&s); TEST_EQ(err, SOCKET_ERROR_NONE); /* * Because the allocator is stack-dependent, there is no way to test for a * memory leak in a portable way */ } } TEST_RETURN(); }
int socket_api_test_echo_server_stream(socket_stack_t stack, socket_address_family_t af, const char* local_addr, uint16_t port) { struct socket s; struct socket cs; struct socket_addr addr; socket_error_t err; const struct socket_api *api = socket_get_api(stack); server_socket = &s; client_socket = &cs; mbed::Timeout to; mbed::Ticker ticker; // Create the socket TEST_CLEAR(); if (!TEST_NEQ(api, NULL)) { // Test cannot continue without API. TEST_RETURN(); } err = api->init(); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_RETURN(); } // Zero the socket implementation s.impl = NULL; // Create a socket err = api->create(&s, af, SOCKET_STREAM, &server_cb); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_RETURN(); } err = api->str2addr(&s, &addr, local_addr); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_RETURN(); } // start the TCP timer uint32_t interval_ms = api->periodic_interval(&s); TEST_NEQ(interval_ms, 0); uint32_t interval_us = interval_ms * 1000; socket_api_handler_t periodic_task = api->periodic_task(&s); if (TEST_NEQ(periodic_task, NULL)) { ticker.attach_us(periodic_task, interval_us); } err = api->bind(&s, &addr, port); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_RETURN(); } void *data = malloc(SOCKET_SENDBUF_MAXSIZE); if(!TEST_NEQ(data, NULL)) { TEST_RETURN(); } // Tell the host test to try and connect TEST_PRINT(">>> EC,%s,%d\r\n", local_addr, port); bool quit = false; // For several iterations while (!quit) { timedout = false; server_event_done = false; incoming = false; to.attach(onTimeout, SOCKET_TEST_SERVER_TIMEOUT); // Listen for incoming connections err = api->start_listen(&s, 0); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_EXIT(); } // Reset the state of client_rx_done client_rx_done = false; // Wait for a connect event while (!timedout && !incoming) { __WFI(); } if (TEST_EQ(timedout,0)) { to.detach(); } else { TEST_EXIT(); } if(!TEST_EQ(server_event.event, SOCKET_EVENT_ACCEPT)) { TEST_PRINT("Event: %d\r\n",(int)client_event.event); continue; } // Stop listening server_event_done = false; // err = api->stop_listen(&s); // TEST_EQ(err, SOCKET_ERROR_NONE); // Accept an incoming connection cs.impl = server_event.i.a.newimpl; cs.family = s.family; cs.stack = s.stack; err = api->accept(&cs, client_cb); if(!TEST_EQ(err, SOCKET_ERROR_NONE)) { continue; } to.attach(onTimeout, SOCKET_TEST_SERVER_TIMEOUT); // Client should test for successive connections being rejected // Until Client disconnects while (client_event.event != SOCKET_EVENT_ERROR && client_event.event != SOCKET_EVENT_DISCONNECT) { // Wait for a read event while (!client_event_done && !client_rx_done && !timedout) { __WFI(); } if (!TEST_EQ(client_event_done, false)) { client_event_done = false; continue; } // Reset the state of client_rx_done client_rx_done = false; // Receive some data size_t len = SOCKET_SENDBUF_MAXSIZE; err = api->recv(&cs, data, &len); if (!TEST_NEQ(err, SOCKET_ERROR_WOULD_BLOCK)) { TEST_PRINT("Rx Would Block\r\n"); continue; } if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_PRINT("err: (%d) %s\r\n", err, socket_strerror(err)); break; } // Check if the server should halt if (strncmp((const char *)data, "quit", 4) == 0) { quit = true; break; } // Send some data err = api->send(&cs, data, len); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { break; } } to.detach(); TEST_NEQ(timedout, true); // Close client socket err = api->close(&cs); TEST_EQ(err, SOCKET_ERROR_NONE); } err = api->stop_listen(&s); TEST_EQ(err, SOCKET_ERROR_NONE); test_exit: ticker.detach(); TEST_PRINT(">>> KILL,EC\r\n"); free(data); // Destroy server socket TEST_RETURN(); }
int socket_api_test_connect_close(socket_stack_t stack, socket_address_family_t af, const char* server, uint16_t port) { struct socket s; int pfi; socket_error_t err; const struct socket_api * api = socket_get_api(stack); struct socket_addr addr; ConnectCloseSock = &s; TEST_CLEAR(); if (!TEST_NEQ(api, NULL)) { // Test cannot continue without API. TEST_RETURN(); } err = api->init(); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_RETURN(); } // Create a socket for each protocol family for (pfi = SOCKET_PROTO_UNINIT+1; pfi < SOCKET_PROTO_MAX; pfi++) { socket_proto_family_t pf = static_cast<socket_proto_family_t>(pfi); // Zero the implementation s.impl = NULL; err = api->create(&s, af, pf, &connect_close_handler); // catch expected failing cases TEST_EQ(err, SOCKET_ERROR_NONE); if (!TEST_NEQ(s.impl, NULL)) { continue; } // Tell the host launch a server TEST_PRINT(">>> ES,%d\r\n", pf); // connect to a remote host err = api->str2addr(&s, &addr, server); TEST_EQ(err, SOCKET_ERROR_NONE); timedout = 0; connected = 0; mbed::Timeout to; to.attach(onTimeout, SOCKET_TEST_TIMEOUT); err = api->connect(&s, &addr, port); TEST_EQ(err, SOCKET_ERROR_NONE); if (err!=SOCKET_ERROR_NONE) { printf("err = %d\r\n", err); } switch (pf) { case SOCKET_DGRAM: while ((!api->is_connected(&s)) && (!timedout)) { __WFI(); } break; case SOCKET_STREAM: while (!connected && !timedout) { __WFI(); } break; default: break; } to.detach(); TEST_EQ(timedout, 0); // close the connection timedout = 0; closed = 0; to.attach(onTimeout, SOCKET_TEST_TIMEOUT); err = api->close(&s); TEST_EQ(err, SOCKET_ERROR_NONE); if (err!=SOCKET_ERROR_NONE) { printf("err = %d\r\n", err); } switch (pf) { case SOCKET_DGRAM: while ((api->is_connected(&s)) && (!timedout)) { __WFI(); } break; case SOCKET_STREAM: while (!closed && !timedout) { __WFI(); } break; default: break; } to.detach(); TEST_EQ(timedout, 0); // Tell the host to kill the server TEST_PRINT(">>> KILL ES\r\n"); // Destroy the socket err = api->destroy(&s); TEST_EQ(err, SOCKET_ERROR_NONE); } TEST_RETURN(); }
int socket_api_test_echo_client_connected(socket_stack_t stack, socket_address_family_t af, socket_proto_family_t pf, bool connect, const char* server, uint16_t port) { struct socket s; socket_error_t err; const struct socket_api *api = socket_get_api(stack); client_socket = &s; mbed::Timeout to; // Create the socket TEST_CLEAR(); TEST_PRINT("\r\n%s af: %d, pf: %d, connect: %d, server: %s:%d\r\n",__func__, (int) af, (int) pf, (int) connect, server, (int) port); if (!TEST_NEQ(api, NULL)) { // Test cannot continue without API. TEST_RETURN(); } err = api->init(); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_RETURN(); } struct socket_addr addr; // Resolve the host address err = blocking_resolve(stack, af, server, &addr); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_RETURN(); } // Tell the host launch a server TEST_PRINT(">>> ES,%d\r\n", pf); // Allocate a data buffer for tx/rx void *data = malloc(SOCKET_SENDBUF_MAXSIZE); // Zero the socket implementation s.impl = NULL; // Create a socket err = api->create(&s, af, pf, &client_cb); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_EXIT(); } // Connect to a host if (connect) { client_event_done = false; timedout = 0; to.attach(onTimeout, SOCKET_TEST_TIMEOUT); err = api->connect(&s, &addr, port); if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_EXIT(); } // Override event for dgrams. if (pf == SOCKET_DGRAM) { client_event.event = SOCKET_EVENT_CONNECT; client_event_done = true; } // Wait for onConnect while (!timedout && !client_event_done) { __WFI(); } // Make sure that the correct event occurred if (!TEST_EQ(client_event.event, SOCKET_EVENT_CONNECT)) { TEST_EXIT(); } to.detach(); } // For several iterations for (size_t i = 0; i < SOCKET_SENDBUF_ITERATIONS; i++) { // Fill some data into a buffer const size_t nWords = SOCKET_SENDBUF_BLOCKSIZE * (1 << i) / sizeof(uint16_t); for (size_t j = 0; j < nWords; j++) { *((uint16_t*) data + j) = j; } // Send the data client_tx_done = false; client_rx_done = false; timedout = 0; to.attach(onTimeout, SOCKET_TEST_TIMEOUT); if(connect) { err = api->send(&s, data, nWords * sizeof(uint16_t)); } else { err = api->send_to(&s, data, nWords * sizeof(uint16_t), &addr, port); } if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { TEST_PRINT("Failed to send %u bytes\r\n", nWords * sizeof(uint16_t)); } else { size_t tx_bytes = 0; do { // Wait for the onSent callback while (!timedout && !client_tx_done) { __WFI(); } if (!TEST_EQ(timedout,0)) { break; } if (!TEST_NEQ(client_tx_info.sentbytes, 0)) { break; } tx_bytes += client_tx_info.sentbytes; if (tx_bytes < nWords * sizeof(uint16_t)) { client_tx_done = false; continue; } to.detach(); TEST_EQ(tx_bytes, nWords * sizeof(uint16_t)); break; } while (1); } timedout = 0; to.attach(onTimeout, SOCKET_TEST_TIMEOUT); memset(data, 0, nWords * sizeof(uint16_t)); // Wait for the onReadable callback size_t rx_bytes = 0; do { while (!timedout && !client_rx_done) { __WFI(); } if (!TEST_EQ(timedout,0)) { break; } size_t len = SOCKET_SENDBUF_MAXSIZE - rx_bytes; // Receive data if (connect) { err = api->recv(&s, (void*) ((uintptr_t) data + rx_bytes), &len); } else { struct socket_addr rxaddr; uint16_t rxport = 0; // addr may contain unused data in the storage element. memcpy(&rxaddr, &addr, sizeof(rxaddr)); // Receive from... err = api->recv_from(&s, (void*) ((uintptr_t) data + rx_bytes), &len, &rxaddr, &rxport); int rc = memcmp(&rxaddr.ipv6be, &addr.ipv6be, sizeof(rxaddr.ipv6be)); if(!TEST_EQ(rc, 0)) { TEST_PRINT("Spurious receive packet\r\n"); } TEST_EQ(rxport, port); } if (!TEST_EQ(err, SOCKET_ERROR_NONE)) { break; } rx_bytes += len; if (rx_bytes < nWords * sizeof(uint16_t)) { client_rx_done = false; continue; } to.detach(); break; } while (1); if(!TEST_EQ(rx_bytes, nWords * sizeof(uint16_t))) { TEST_PRINT("Expected %u, got %u\r\n", nWords * sizeof(uint16_t), rx_bytes); } // Validate that the two buffers are the same bool match = true; size_t j; for (j = 0; match && j < nWords; j++) { match = (*((uint16_t*) data + j) == j); } if(!TEST_EQ(match, true)) { TEST_PRINT("Mismatch in %u byte packet at offset %u\r\n", nWords * sizeof(uint16_t), j * sizeof(uint16_t)); } } if (connect) { // close the socket client_event_done = false; timedout = 0; to.attach(onTimeout, SOCKET_TEST_TIMEOUT); err = api->close(&s); TEST_EQ(err, SOCKET_ERROR_NONE); // Override event for dgrams. if (pf == SOCKET_DGRAM) { client_event.event = SOCKET_EVENT_DISCONNECT; client_event_done = true; } // wait for onClose while (!timedout && !client_event_done) { __WFI(); } if (TEST_EQ(timedout,0)) { to.detach(); } // Make sure that the correct event occurred TEST_EQ(client_event.event, SOCKET_EVENT_DISCONNECT); } // destroy the socket err = api->destroy(&s); TEST_EQ(err, SOCKET_ERROR_NONE); test_exit: TEST_PRINT(">>> KILL,ES\r\n"); free(data); TEST_RETURN(); }
int test_socket_stack_registry() { unsigned int i; socket_error_t err = SOCKET_ERROR_NONE; printf("Testing stack registry...\n"); TEST_CLEAR(); // Try to register a stack marked as Uninitialized expect_fail_api.stack = SOCKET_STACK_UNINIT; expect_fail_api.version = SOCKET_ABSTRACTION_LAYER_VERSION; err = socket_register_stack((&expect_fail_api)); TEST_NEQ(err,SOCKET_ERROR_NONE); // Register a NULL socket api memset(&expect_fail_api, 0, sizeof(struct socket_api)); expect_fail_api.version = SOCKET_ABSTRACTION_LAYER_VERSION; err = socket_register_stack(&expect_fail_api); TEST_NEQ(err,SOCKET_ERROR_NONE); // Register a socket api with one zeroed API. memset(&expect_fail_api, 1, sizeof(struct socket_api)); expect_fail_api.version = SOCKET_ABSTRACTION_LAYER_VERSION; expect_fail_api.create = NULL; expect_fail_api.stack = static_cast<socket_stack_t>(SOCKET_STACK_UNINIT + 1); err = socket_register_stack(&expect_fail_api); TEST_NEQ(err,SOCKET_ERROR_NONE); // Register a socket api with the version set wrong memset(&expect_fail_api, 1, sizeof(struct socket_api)); expect_fail_api.version = 0; expect_fail_api.stack = static_cast<socket_stack_t>(SOCKET_STACK_UNINIT + 1); err = socket_register_stack(&expect_fail_api); TEST_NEQ(err,SOCKET_ERROR_NONE); // Register two of the same socket api's memset(&test_api[0], 1, sizeof(struct socket_api)); memset(&expect_fail_api, 1, sizeof(struct socket_api)); test_api[0].stack = static_cast<socket_stack_t>(SOCKET_STACK_UNINIT + 1); test_api[0].version = SOCKET_ABSTRACTION_LAYER_VERSION; expect_fail_api.stack = static_cast<socket_stack_t>(SOCKET_STACK_UNINIT + 1); expect_fail_api.version = SOCKET_ABSTRACTION_LAYER_VERSION; err = socket_register_stack(&test_api[0]); TEST_EQ(err,SOCKET_ERROR_NONE); err = socket_register_stack(&expect_fail_api); TEST_NEQ(err,SOCKET_ERROR_NONE); // Register the same stack again, but with the stack ID changed test_api[0].stack = static_cast<socket_stack_t>(static_cast<uint32_t>(test_api[0].stack) + 1); test_api[0].version = SOCKET_ABSTRACTION_LAYER_VERSION; err = socket_register_stack(&test_api[0]); TEST_NEQ(err,SOCKET_ERROR_NONE); test_api[0].stack = static_cast<socket_stack_t>(static_cast<uint32_t>(test_api[0].stack) - 1); // Try to register a stack outside the accepted range expect_fail_api.stack = SOCKET_STACK_MAX; expect_fail_api.version = SOCKET_ABSTRACTION_LAYER_VERSION; err = socket_register_stack(&expect_fail_api); TEST_NEQ(err,SOCKET_ERROR_NONE); // One stack is already registered // Try to register the maximum number of stacks for (i = 1; i < SOCKET_MAX_STACKS; i++) { socket_stack_t stack = static_cast<socket_stack_t>(SOCKET_STACK_UNINIT + 1 + i); memset (&test_api[i],1,sizeof(struct socket_api)); test_api[i].stack = stack; test_api[i].version = SOCKET_ABSTRACTION_LAYER_VERSION; err = socket_register_stack(&test_api[i]); TEST_EQ(err,SOCKET_ERROR_NONE); } // Then register one more. expect_fail_api.stack = static_cast<socket_stack_t>(SOCKET_MAX_STACKS + 1); expect_fail_api.version = SOCKET_ABSTRACTION_LAYER_VERSION; err = socket_register_stack(&expect_fail_api); TEST_NEQ(err,SOCKET_ERROR_NONE); // Extract an uninit socket api const struct socket_api *papi; papi = socket_get_api(SOCKET_STACK_UNINIT); TEST_EQ(papi, NULL); if (SOCKET_MAX_STACKS < SOCKET_STACK_MAX - 1) { // Get a valid, but unregistered stack papi = socket_get_api(static_cast<socket_stack_t>(SOCKET_STACK_MAX - 1)); TEST_EQ(papi, NULL); } // Verify all registered stacks for (i = 0; i < SOCKET_MAX_STACKS; i++) { socket_stack_t stack = static_cast<socket_stack_t>(SOCKET_STACK_UNINIT + 1 + i); papi = socket_get_api(stack); TEST_EQ(papi, &test_api[i]); } return test_pass_global; }