From 72231250ed81e10d66bfe70701e64fa5fe50f712 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Wed, 15 Jun 2016 23:09:44 +0200 Subject: Publish --- fnet/src/tests/.gitignore | 3 + fnet/src/tests/connect_thread/.gitignore | 1 + fnet/src/tests/connect_thread/CMakeLists.txt | 8 + .../tests/connect_thread/connect_thread_test.cpp | 26 + fnet/src/tests/connection_spread/.gitignore | 1 + fnet/src/tests/connection_spread/CMakeLists.txt | 8 + .../connection_spread/connection_spread_test.cpp | 83 ++ fnet/src/tests/databuffer/.gitignore | 4 + fnet/src/tests/databuffer/CMakeLists.txt | 8 + fnet/src/tests/databuffer/DESC | 2 + fnet/src/tests/databuffer/FILES | 1 + fnet/src/tests/databuffer/databuffer.cpp | 201 +++++ fnet/src/tests/examples/.gitignore | 1 + fnet/src/tests/examples/CMakeLists.txt | 8 + fnet/src/tests/examples/FILES | 1 + fnet/src/tests/examples/examples_test.cpp | 246 ++++++ fnet/src/tests/fdselector/.gitignore | 4 + fnet/src/tests/fdselector/CMakeLists.txt | 8 + fnet/src/tests/fdselector/DESC | 1 + fnet/src/tests/fdselector/FILES | 1 + fnet/src/tests/fdselector/fdselector.cpp | 220 +++++ fnet/src/tests/frt/memorytub/.gitignore | 6 + fnet/src/tests/frt/memorytub/CMakeLists.txt | 8 + fnet/src/tests/frt/memorytub/DESC | 1 + fnet/src/tests/frt/memorytub/FILES | 1 + fnet/src/tests/frt/memorytub/memorytub.cpp | 124 +++ fnet/src/tests/frt/method_pt/.gitignore | 6 + fnet/src/tests/frt/method_pt/CMakeLists.txt | 8 + fnet/src/tests/frt/method_pt/DESC | 2 + fnet/src/tests/frt/method_pt/FILES | 1 + fnet/src/tests/frt/method_pt/method_pt.cpp | 395 +++++++++ fnet/src/tests/frt/parallel_rpc/.gitignore | 1 + fnet/src/tests/frt/parallel_rpc/CMakeLists.txt | 8 + .../tests/frt/parallel_rpc/parallel_rpc_test.cpp | 129 +++ fnet/src/tests/frt/rpc/.gitignore | 12 + fnet/src/tests/frt/rpc/CMakeLists.txt | 29 + fnet/src/tests/frt/rpc/DESC | 1 + fnet/src/tests/frt/rpc/FILES | 3 + fnet/src/tests/frt/rpc/detach_return_invoke.cpp | 69 ++ fnet/src/tests/frt/rpc/invoke.cpp | 938 +++++++++++++++++++++ fnet/src/tests/frt/rpc/session.cpp | 124 +++ fnet/src/tests/frt/rpc/sharedblob.cpp | 256 ++++++ fnet/src/tests/frt/values/.gitignore | 1 + fnet/src/tests/frt/values/CMakeLists.txt | 8 + fnet/src/tests/frt/values/FILES | 1 + fnet/src/tests/frt/values/values_test.cpp | 207 +++++ fnet/src/tests/info/.gitignore | 4 + fnet/src/tests/info/CMakeLists.txt | 8 + fnet/src/tests/info/DESC | 1 + fnet/src/tests/info/FILES | 1 + fnet/src/tests/info/info.cpp | 89 ++ fnet/src/tests/locking/.gitignore | 8 + fnet/src/tests/locking/CMakeLists.txt | 23 + fnet/src/tests/locking/DESC | 1 + fnet/src/tests/locking/FILES | 2 + fnet/src/tests/locking/castspeed.cpp | 219 +++++ fnet/src/tests/locking/drainpackets.cpp | 134 +++ fnet/src/tests/locking/dummy.cpp | 9 + fnet/src/tests/locking/dummy.h | 17 + fnet/src/tests/locking/lockspeed.cpp | 177 ++++ fnet/src/tests/printstuff/.gitignore | 1 + fnet/src/tests/printstuff/CMakeLists.txt | 8 + fnet/src/tests/printstuff/FILES | 1 + fnet/src/tests/printstuff/printstuff_test.cpp | 45 + fnet/src/tests/regress/databuffer/.gitignore | 0 fnet/src/tests/regress/fdselector/.gitignore | 0 fnet/src/tests/regress/frt/memorytub/.gitignore | 0 fnet/src/tests/regress/frt/method_pt/.gitignore | 0 fnet/src/tests/regress/frt/rpc/.gitignore | 0 fnet/src/tests/regress/frt/values/.gitignore | 0 fnet/src/tests/regress/info/.gitignore | 0 fnet/src/tests/regress/locking/.gitignore | 0 fnet/src/tests/regress/scheduling/.gitignore | 0 fnet/src/tests/regress/spiral/.gitignore | 0 fnet/src/tests/regress/sync_execute/.gitignore | 0 fnet/src/tests/regress/thread_id/.gitignore | 0 fnet/src/tests/regress/time/.gitignore | 0 fnet/src/tests/scheduling/.gitignore | 6 + fnet/src/tests/scheduling/CMakeLists.txt | 15 + fnet/src/tests/scheduling/DESC | 1 + fnet/src/tests/scheduling/FILES | 1 + fnet/src/tests/scheduling/schedule.cpp | 153 ++++ fnet/src/tests/scheduling/sloweventloop.cpp | 65 ++ fnet/src/tests/sync_execute/.gitignore | 4 + fnet/src/tests/sync_execute/CMakeLists.txt | 8 + fnet/src/tests/sync_execute/DESC | 1 + fnet/src/tests/sync_execute/FILES | 1 + fnet/src/tests/sync_execute/sync_execute.cpp | 39 + fnet/src/tests/thread_selection/.gitignore | 1 + fnet/src/tests/thread_selection/CMakeLists.txt | 8 + .../thread_selection/thread_selection_test.cpp | 89 ++ fnet/src/tests/time/.gitignore | 4 + fnet/src/tests/time/CMakeLists.txt | 8 + fnet/src/tests/time/DESC | 1 + fnet/src/tests/time/FILES | 1 + fnet/src/tests/time/timespeed.cpp | 68 ++ 96 files changed, 4397 insertions(+) create mode 100644 fnet/src/tests/.gitignore create mode 100644 fnet/src/tests/connect_thread/.gitignore create mode 100644 fnet/src/tests/connect_thread/CMakeLists.txt create mode 100644 fnet/src/tests/connect_thread/connect_thread_test.cpp create mode 100644 fnet/src/tests/connection_spread/.gitignore create mode 100644 fnet/src/tests/connection_spread/CMakeLists.txt create mode 100644 fnet/src/tests/connection_spread/connection_spread_test.cpp create mode 100644 fnet/src/tests/databuffer/.gitignore create mode 100644 fnet/src/tests/databuffer/CMakeLists.txt create mode 100644 fnet/src/tests/databuffer/DESC create mode 100644 fnet/src/tests/databuffer/FILES create mode 100644 fnet/src/tests/databuffer/databuffer.cpp create mode 100644 fnet/src/tests/examples/.gitignore create mode 100644 fnet/src/tests/examples/CMakeLists.txt create mode 100644 fnet/src/tests/examples/FILES create mode 100644 fnet/src/tests/examples/examples_test.cpp create mode 100644 fnet/src/tests/fdselector/.gitignore create mode 100644 fnet/src/tests/fdselector/CMakeLists.txt create mode 100644 fnet/src/tests/fdselector/DESC create mode 100644 fnet/src/tests/fdselector/FILES create mode 100644 fnet/src/tests/fdselector/fdselector.cpp create mode 100644 fnet/src/tests/frt/memorytub/.gitignore create mode 100644 fnet/src/tests/frt/memorytub/CMakeLists.txt create mode 100644 fnet/src/tests/frt/memorytub/DESC create mode 100644 fnet/src/tests/frt/memorytub/FILES create mode 100644 fnet/src/tests/frt/memorytub/memorytub.cpp create mode 100644 fnet/src/tests/frt/method_pt/.gitignore create mode 100644 fnet/src/tests/frt/method_pt/CMakeLists.txt create mode 100644 fnet/src/tests/frt/method_pt/DESC create mode 100644 fnet/src/tests/frt/method_pt/FILES create mode 100644 fnet/src/tests/frt/method_pt/method_pt.cpp create mode 100644 fnet/src/tests/frt/parallel_rpc/.gitignore create mode 100644 fnet/src/tests/frt/parallel_rpc/CMakeLists.txt create mode 100644 fnet/src/tests/frt/parallel_rpc/parallel_rpc_test.cpp create mode 100644 fnet/src/tests/frt/rpc/.gitignore create mode 100644 fnet/src/tests/frt/rpc/CMakeLists.txt create mode 100644 fnet/src/tests/frt/rpc/DESC create mode 100644 fnet/src/tests/frt/rpc/FILES create mode 100644 fnet/src/tests/frt/rpc/detach_return_invoke.cpp create mode 100644 fnet/src/tests/frt/rpc/invoke.cpp create mode 100644 fnet/src/tests/frt/rpc/session.cpp create mode 100644 fnet/src/tests/frt/rpc/sharedblob.cpp create mode 100644 fnet/src/tests/frt/values/.gitignore create mode 100644 fnet/src/tests/frt/values/CMakeLists.txt create mode 100644 fnet/src/tests/frt/values/FILES create mode 100644 fnet/src/tests/frt/values/values_test.cpp create mode 100644 fnet/src/tests/info/.gitignore create mode 100644 fnet/src/tests/info/CMakeLists.txt create mode 100644 fnet/src/tests/info/DESC create mode 100644 fnet/src/tests/info/FILES create mode 100644 fnet/src/tests/info/info.cpp create mode 100644 fnet/src/tests/locking/.gitignore create mode 100644 fnet/src/tests/locking/CMakeLists.txt create mode 100644 fnet/src/tests/locking/DESC create mode 100644 fnet/src/tests/locking/FILES create mode 100644 fnet/src/tests/locking/castspeed.cpp create mode 100644 fnet/src/tests/locking/drainpackets.cpp create mode 100644 fnet/src/tests/locking/dummy.cpp create mode 100644 fnet/src/tests/locking/dummy.h create mode 100644 fnet/src/tests/locking/lockspeed.cpp create mode 100644 fnet/src/tests/printstuff/.gitignore create mode 100644 fnet/src/tests/printstuff/CMakeLists.txt create mode 100644 fnet/src/tests/printstuff/FILES create mode 100644 fnet/src/tests/printstuff/printstuff_test.cpp create mode 100644 fnet/src/tests/regress/databuffer/.gitignore create mode 100644 fnet/src/tests/regress/fdselector/.gitignore create mode 100644 fnet/src/tests/regress/frt/memorytub/.gitignore create mode 100644 fnet/src/tests/regress/frt/method_pt/.gitignore create mode 100644 fnet/src/tests/regress/frt/rpc/.gitignore create mode 100644 fnet/src/tests/regress/frt/values/.gitignore create mode 100644 fnet/src/tests/regress/info/.gitignore create mode 100644 fnet/src/tests/regress/locking/.gitignore create mode 100644 fnet/src/tests/regress/scheduling/.gitignore create mode 100644 fnet/src/tests/regress/spiral/.gitignore create mode 100644 fnet/src/tests/regress/sync_execute/.gitignore create mode 100644 fnet/src/tests/regress/thread_id/.gitignore create mode 100644 fnet/src/tests/regress/time/.gitignore create mode 100644 fnet/src/tests/scheduling/.gitignore create mode 100644 fnet/src/tests/scheduling/CMakeLists.txt create mode 100644 fnet/src/tests/scheduling/DESC create mode 100644 fnet/src/tests/scheduling/FILES create mode 100644 fnet/src/tests/scheduling/schedule.cpp create mode 100644 fnet/src/tests/scheduling/sloweventloop.cpp create mode 100644 fnet/src/tests/sync_execute/.gitignore create mode 100644 fnet/src/tests/sync_execute/CMakeLists.txt create mode 100644 fnet/src/tests/sync_execute/DESC create mode 100644 fnet/src/tests/sync_execute/FILES create mode 100644 fnet/src/tests/sync_execute/sync_execute.cpp create mode 100644 fnet/src/tests/thread_selection/.gitignore create mode 100644 fnet/src/tests/thread_selection/CMakeLists.txt create mode 100644 fnet/src/tests/thread_selection/thread_selection_test.cpp create mode 100644 fnet/src/tests/time/.gitignore create mode 100644 fnet/src/tests/time/CMakeLists.txt create mode 100644 fnet/src/tests/time/DESC create mode 100644 fnet/src/tests/time/FILES create mode 100644 fnet/src/tests/time/timespeed.cpp (limited to 'fnet/src/tests') diff --git a/fnet/src/tests/.gitignore b/fnet/src/tests/.gitignore new file mode 100644 index 00000000000..a3e9c375723 --- /dev/null +++ b/fnet/src/tests/.gitignore @@ -0,0 +1,3 @@ +.depend +Makefile +*_test diff --git a/fnet/src/tests/connect_thread/.gitignore b/fnet/src/tests/connect_thread/.gitignore new file mode 100644 index 00000000000..66bba07002d --- /dev/null +++ b/fnet/src/tests/connect_thread/.gitignore @@ -0,0 +1 @@ +fnet_connect_thread_test_app diff --git a/fnet/src/tests/connect_thread/CMakeLists.txt b/fnet/src/tests/connect_thread/CMakeLists.txt new file mode 100644 index 00000000000..535ac5e3561 --- /dev/null +++ b/fnet/src/tests/connect_thread/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_connect_thread_test_app + SOURCES + connect_thread_test.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_connect_thread_test_app COMMAND fnet_connect_thread_test_app) diff --git a/fnet/src/tests/connect_thread/connect_thread_test.cpp b/fnet/src/tests/connect_thread/connect_thread_test.cpp new file mode 100644 index 00000000000..f8492d147a6 --- /dev/null +++ b/fnet/src/tests/connect_thread/connect_thread_test.cpp @@ -0,0 +1,26 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include +#include + +struct MyConn : public fnet::ExtConnectable { + bool connected = false; + void ext_connect() override { connected = true; } +}; + +TEST("require that connect thread will connect stuff") { + std::vector conns(5); + { + fnet::ConnectThread thread; + thread.connect_later(&conns[0]); + thread.connect_later(&conns[2]); + thread.connect_later(&conns[4]); + } + EXPECT_TRUE(conns[0].connected); + EXPECT_TRUE(!conns[1].connected); + EXPECT_TRUE(conns[2].connected); + EXPECT_TRUE(!conns[3].connected); + EXPECT_TRUE(conns[4].connected); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/connection_spread/.gitignore b/fnet/src/tests/connection_spread/.gitignore new file mode 100644 index 00000000000..34408abbf7c --- /dev/null +++ b/fnet/src/tests/connection_spread/.gitignore @@ -0,0 +1 @@ +fnet_connection_spread_test_app diff --git a/fnet/src/tests/connection_spread/CMakeLists.txt b/fnet/src/tests/connection_spread/CMakeLists.txt new file mode 100644 index 00000000000..9ed541b62e3 --- /dev/null +++ b/fnet/src/tests/connection_spread/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_connection_spread_test_app + SOURCES + connection_spread_test.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_connection_spread_test_app COMMAND fnet_connection_spread_test_app) diff --git a/fnet/src/tests/connection_spread/connection_spread_test.cpp b/fnet/src/tests/connection_spread/connection_spread_test.cpp new file mode 100644 index 00000000000..a10278daf32 --- /dev/null +++ b/fnet/src/tests/connection_spread/connection_spread_test.cpp @@ -0,0 +1,83 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +struct DummyAdapter : FNET_IServerAdapter { + bool InitAdminChannel(FNET_Channel *) override { return false; } + bool InitChannel(FNET_Channel *, uint32_t) override { return false; } +}; + +struct DummyStreamer : FNET_IPacketStreamer { + bool GetPacketInfo(FNET_DataBuffer *, uint32_t *, uint32_t *, uint32_t *, bool *) override { return false; } + FNET_Packet *Decode(FNET_DataBuffer *, uint32_t, uint32_t, FNET_Context) override { return nullptr; } + void Encode(FNET_Packet *, uint32_t, FNET_DataBuffer *) override {} +}; + +struct Fixture { + DummyStreamer streamer; + DummyAdapter adapter; + FastOS_ThreadPool thread_pool; + FNET_Transport client; + FNET_Transport server; + Fixture() : streamer(), adapter(), thread_pool(128 * 1024), client(8), server(8) + { + ASSERT_TRUE(client.Start(&thread_pool)); + ASSERT_TRUE(server.Start(&thread_pool)); + } + void wait_for_components(size_t client_cnt, size_t server_cnt) { + bool ok = false; + for (size_t i = 0; !ok && (i < 10000); ++i) { + std::this_thread::sleep_for(3ms); + ok = ((client.GetNumIOComponents() == client_cnt) && + (server.GetNumIOComponents() == server_cnt)); + } + EXPECT_EQUAL(client.GetNumIOComponents(), client_cnt); + EXPECT_EQUAL(server.GetNumIOComponents(), server_cnt); + } + ~Fixture() { + server.ShutDown(true); + client.ShutDown(true); + thread_pool.Close(); + } +}; + +void check_threads(FNET_Transport &transport, size_t num_threads, const vespalib::string &tag) { + std::set threads; + while (threads.size() < num_threads) { + threads.insert(transport.select_thread(nullptr, 0)); + } + for (auto thread: threads) { + uint32_t cnt = thread->GetNumIOComponents(); + fprintf(stderr, "-- %s thread: %u io components\n", tag.c_str(), cnt); + EXPECT_GREATER(cnt, 1u); + } +} + +TEST_F("require that connections are spread among transport threads", Fixture) +{ + FNET_Connector *listener = f1.server.Listen("tcp/0", &f1.streamer, &f1.adapter); + ASSERT_TRUE(listener); + uint32_t port = listener->GetPortNumber(); + vespalib::string spec = vespalib::make_string("tcp/localhost:%u", port); + std::vector connections; + for (size_t i = 0; i < 256; ++i) { + std::this_thread::sleep_for(1ms); + connections.push_back(f1.client.Connect(spec.c_str(), &f1.streamer)); + ASSERT_TRUE(connections.back()); + } + f1.wait_for_components(256, 257); + check_threads(f1.client, 8, "client"); + check_threads(f1.server, 8, "server"); + listener->SubRef(); + for (FNET_Connection *conn: connections) { + conn->SubRef(); + } +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/databuffer/.gitignore b/fnet/src/tests/databuffer/.gitignore new file mode 100644 index 00000000000..b9c5810fe42 --- /dev/null +++ b/fnet/src/tests/databuffer/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +databuffer_test +fnet_databuffer_test_app diff --git a/fnet/src/tests/databuffer/CMakeLists.txt b/fnet/src/tests/databuffer/CMakeLists.txt new file mode 100644 index 00000000000..47122e11d96 --- /dev/null +++ b/fnet/src/tests/databuffer/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_databuffer_test_app + SOURCES + databuffer.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_databuffer_test_app COMMAND fnet_databuffer_test_app) diff --git a/fnet/src/tests/databuffer/DESC b/fnet/src/tests/databuffer/DESC new file mode 100644 index 00000000000..d947b059899 --- /dev/null +++ b/fnet/src/tests/databuffer/DESC @@ -0,0 +1,2 @@ +Benchmark some databuffer operations and check for consistent +encoding/decoding. diff --git a/fnet/src/tests/databuffer/FILES b/fnet/src/tests/databuffer/FILES new file mode 100644 index 00000000000..c0265d2742f --- /dev/null +++ b/fnet/src/tests/databuffer/FILES @@ -0,0 +1 @@ +databuffer.cpp diff --git a/fnet/src/tests/databuffer/databuffer.cpp b/fnet/src/tests/databuffer/databuffer.cpp new file mode 100644 index 00000000000..7aa75a95e65 --- /dev/null +++ b/fnet/src/tests/databuffer/databuffer.cpp @@ -0,0 +1,201 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + +TEST("test resetIfEmpty") { + FNET_DataBuffer buf(64); + EXPECT_TRUE(buf.GetData() == buf.GetDead()); + EXPECT_TRUE(buf.GetData() == buf.GetFree()); + buf.WriteInt32(11111111); + EXPECT_TRUE(buf.GetData() == buf.GetDead()); + EXPECT_FALSE(buf.GetData() == buf.GetFree()); + buf.resetIfEmpty(); + EXPECT_TRUE(buf.GetData() == buf.GetDead()); + EXPECT_FALSE(buf.GetData() == buf.GetFree()); + EXPECT_EQUAL(11111111u, buf.ReadInt32()); + buf.resetIfEmpty(); + EXPECT_TRUE(buf.GetData() == buf.GetDead()); + EXPECT_TRUE(buf.GetData() == buf.GetFree()); +} + +TEST("testResize") { + FNET_DataBuffer buf(64); + uint32_t initialSize = buf.GetBufSize(); + buf.WriteInt32(11111111); + buf.WriteInt32(22222222); + buf.WriteInt32(33333333); + buf.WriteInt32(44444444); + buf.WriteInt32(55555555); + EXPECT_TRUE(buf.ReadInt32() == 11111111); + buf.EnsureFree(initialSize); + EXPECT_TRUE(buf.GetBufSize() > initialSize); + EXPECT_TRUE(buf.ReadInt32() == 22222222); + EXPECT_TRUE(!buf.Shrink(buf.GetBufSize())); + EXPECT_TRUE(!buf.Shrink(buf.GetBufSize() + 16)); + EXPECT_TRUE(!buf.Shrink(2 * 4)); + EXPECT_TRUE(buf.Shrink(3 * 4)); + EXPECT_TRUE(buf.GetBufSize() == 3 * 4); + EXPECT_TRUE(buf.ReadInt32() == 33333333); + buf.WriteInt32(66666666); + buf.EnsureFree(16); + EXPECT_TRUE(buf.GetDataLen() == 3 * 4); + EXPECT_TRUE(buf.GetBufSize() >= 16 + 3 * 4); + EXPECT_TRUE(buf.ReadInt32() == 44444444); + EXPECT_TRUE(buf.ReadInt32() == 55555555); + EXPECT_TRUE(buf.ReadInt32() == 66666666); + EXPECT_TRUE(buf.Shrink(0)); + EXPECT_TRUE(buf.GetBufSize() == 0); + buf.WriteInt32(42); + EXPECT_TRUE(buf.GetBufSize() >= 4); + EXPECT_TRUE(buf.ReadInt32() == 42); + EXPECT_TRUE(buf.GetDataLen() == 0); +} + +TEST("testSpeed") { + FNET_DataBuffer buf0(20000); + FNET_DataBuffer buf1(20000); + FNET_DataBuffer buf2(20000); + FastOS_Time start; + FastOS_Time stop; + + int i; + int k; + + // fill buf0 with random data + for (i = 0; i < 16000; i++) { + buf0.WriteInt8((uint8_t)rand()); + } + + // copy buf0 into buf1 + for (i = 0; i < 16000; i++) { + buf1.WriteInt8(buf0.ReadInt8()); + } + + // undo read from buf0 + buf0.DeadToData(buf0.GetDeadLen()); + + // test encode/decode speed + start.SetNow(); + + for (i = 0; i < 5000; i++) { + buf2.Clear(); + for (k = 0; k < 500; k++) { + buf2.WriteInt8(buf1.ReadInt8()); + buf2.WriteInt32(buf1.ReadInt32()); + buf2.WriteInt8(buf1.ReadInt8()); + buf2.WriteInt8(buf1.ReadInt8()); + buf2.WriteInt16(buf1.ReadInt16()); + buf2.WriteInt8(buf1.ReadInt8()); + buf2.WriteInt32(buf1.ReadInt32()); + buf2.WriteInt16(buf1.ReadInt16()); + buf2.WriteInt32(buf1.ReadInt32()); + buf2.WriteInt64(buf1.ReadInt64()); + buf2.WriteInt32(buf1.ReadInt32()); + } + buf1.Clear(); + for (k = 0; k < 500; k++) { + buf1.WriteInt8(buf2.ReadInt8()); + buf1.WriteInt16(buf2.ReadInt16()); + buf1.WriteInt8(buf2.ReadInt8()); + buf1.WriteInt32(buf2.ReadInt32()); + buf1.WriteInt32(buf2.ReadInt32()); + buf1.WriteInt8(buf2.ReadInt8()); + buf1.WriteInt64(buf2.ReadInt64()); + buf1.WriteInt32(buf2.ReadInt32()); + buf1.WriteInt8(buf2.ReadInt8()); + buf1.WriteInt16(buf2.ReadInt16()); + buf1.WriteInt32(buf2.ReadInt32()); + } + } + buf2.DeadToData(buf2.GetDeadLen()); + stop.SetNow(); + + stop -= start; + fprintf(stderr, "encode/decode time (~160MB): %1.2f\n", stop.MilliSecs()); + + EXPECT_TRUE(buf0.Equals(&buf1) && buf0.Equals(&buf2)); + + // test encode[fast]/decode speed + start.SetNow(); + + for (i = 0; i < 5000; i++) { + buf2.Clear(); + for (k = 0; k < 500; k++) { + buf2.WriteInt8Fast(buf1.ReadInt8()); + buf2.WriteInt32Fast(buf1.ReadInt32()); + buf2.WriteInt8Fast(buf1.ReadInt8()); + buf2.WriteInt8Fast(buf1.ReadInt8()); + buf2.WriteInt16Fast(buf1.ReadInt16()); + buf2.WriteInt8Fast(buf1.ReadInt8()); + buf2.WriteInt32Fast(buf1.ReadInt32()); + buf2.WriteInt16Fast(buf1.ReadInt16()); + buf2.WriteInt32Fast(buf1.ReadInt32()); + buf2.WriteInt64Fast(buf1.ReadInt64()); + buf2.WriteInt32Fast(buf1.ReadInt32()); + } + buf1.Clear(); + for (k = 0; k < 500; k++) { + buf1.WriteInt8Fast(buf2.ReadInt8()); + buf1.WriteInt16Fast(buf2.ReadInt16()); + buf1.WriteInt8Fast(buf2.ReadInt8()); + buf1.WriteInt32Fast(buf2.ReadInt32()); + buf1.WriteInt32Fast(buf2.ReadInt32()); + buf1.WriteInt8Fast(buf2.ReadInt8()); + buf1.WriteInt64Fast(buf2.ReadInt64()); + buf1.WriteInt32Fast(buf2.ReadInt32()); + buf1.WriteInt8Fast(buf2.ReadInt8()); + buf1.WriteInt16Fast(buf2.ReadInt16()); + buf1.WriteInt32Fast(buf2.ReadInt32()); + } + } + buf2.DeadToData(buf2.GetDeadLen()); + stop.SetNow(); + + stop -= start; + fprintf(stderr, "encode[fast]/decode time (~160MB): %1.2f\n", stop.MilliSecs()); + + EXPECT_TRUE(buf0.Equals(&buf1) && buf0.Equals(&buf2)); + + // init source table for table streaming test + uint32_t table[4000]; + for (i = 0; i < 4000; i++) { + table[i] = i; + } + + // test byte-swap table encoding speed + start.SetNow(); + + for (i = 0; i < 10000; i++) { + buf1.Clear(); + for (k = 0; k < 4000; k += 8) { + buf1.WriteInt32Fast(table[k]); + buf1.WriteInt32Fast(table[k + 1]); + buf1.WriteInt32Fast(table[k + 2]); + buf1.WriteInt32Fast(table[k + 3]); + buf1.WriteInt32Fast(table[k + 4]); + buf1.WriteInt32Fast(table[k + 5]); + buf1.WriteInt32Fast(table[k + 6]); + buf1.WriteInt32Fast(table[k + 7]); + } + } + stop.SetNow(); + stop -= start; + fprintf(stderr, "byte-swap array encoding[fast] (~160 MB): %1.2f ms\n", + stop.MilliSecs()); + + // test direct-copy table encoding speed + start.SetNow(); + + for (i = 0; i < 10000; i++) { + buf2.Clear(); + buf2.EnsureFree(16000); + memcpy(buf2.GetFree(), table, 16000); + buf2.FreeToData(16000); + } + stop.SetNow(); + stop -= start; + fprintf(stderr, "direct-copy array encoding (~160 MB): %1.2f ms\n", + stop.MilliSecs()); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/examples/.gitignore b/fnet/src/tests/examples/.gitignore new file mode 100644 index 00000000000..a3c2ba8686c --- /dev/null +++ b/fnet/src/tests/examples/.gitignore @@ -0,0 +1 @@ +fnet_examples_test_app diff --git a/fnet/src/tests/examples/CMakeLists.txt b/fnet/src/tests/examples/CMakeLists.txt new file mode 100644 index 00000000000..79f9047f626 --- /dev/null +++ b/fnet/src/tests/examples/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_examples_test_app + SOURCES + examples_test.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_examples_test_app NO_VALGRIND COMMAND fnet_examples_test_app) diff --git a/fnet/src/tests/examples/FILES b/fnet/src/tests/examples/FILES new file mode 100644 index 00000000000..58573211940 --- /dev/null +++ b/fnet/src/tests/examples/FILES @@ -0,0 +1 @@ +examples_test.cpp diff --git a/fnet/src/tests/examples/examples_test.cpp b/fnet/src/tests/examples/examples_test.cpp new file mode 100644 index 00000000000..5debd969a60 --- /dev/null +++ b/fnet/src/tests/examples/examples_test.cpp @@ -0,0 +1,246 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include +#include + +// reserved in vespa/factory/doc/port-ranges.txt +static const int PORT0 = 18570; +static const int PORT1 = 18571; +static const int PORT2 = 18572; +static const int PORT3 = 18573; +static const int PORT4 = 18574; +static const int PORT5 = 18575; +static const int PORT6 = 18576; +static const int PORT7 = 18577; +static const int PORT8 = 18578; +static const int PORT9 = 18579; + +using vespalib::SlaveProc; + +bool runProc(SlaveProc &proc, bool &done) { + char buf[4096]; + proc.close(); // close stdin + while (proc.running() && !done) { + if (!proc.eof()) { + uint32_t res = proc.read(buf, sizeof(buf), 10); + std::string tmp(buf, res); + fprintf(stderr, "%s", tmp.c_str()); + } + vespalib::Thread::sleep(10); + } + if (done && proc.running()) { + kill(proc.getPid(), SIGTERM); + return proc.wait(60000); + } + return !proc.failed(); +} + +bool runProc(const std::string &cmd) { + bool ok = false; + for (size_t retry = 0; !ok && retry < 60; ++retry) { + if (retry > 0) { + fprintf(stderr, "retrying command in 500ms...\n"); + vespalib::Thread::sleep(500); + } + bool done = false; + SlaveProc proc(cmd.c_str()); + ok = runProc(proc, done); + } + return ok; +} + +TEST("usage") { + bool done = false; + { + SlaveProc proc("../../examples/proxy/fnet_proxy_app"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/ping/fnet_pingserver_app"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/ping/fnet_pingclient_app"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/frt/rpc/fnet_rpc_client_app"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/frt/rpc/fnet_rpc_server_app"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/frt/rpc/fnet_echo_client_app"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/frt/rpc/rpc_info"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/frt/rpc/rpc_invoke"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/frt/rpc/fnet_rpc_callback_server_app"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/frt/rpc/fnet_rpc_callback_client_app"); + EXPECT_FALSE(runProc(proc, done)); + } + { + SlaveProc proc("../../examples/frt/rpc/rpc_proxy"); + EXPECT_FALSE(runProc(proc, done)); + } +} + +TEST("timeout") { + std::string out; + EXPECT_TRUE(SlaveProc::run("../../examples/timeout/fnet_timeout_app", out)); + fprintf(stderr, "%s\n", out.c_str()); +} + +TEST_MT_F("ping", 2, bool()) { + if (thread_id == 0) { + SlaveProc proc(vespalib::make_string("../../examples/ping/fnet_pingserver_app tcp/%d", + PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else { + TEST_BARRIER(); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/ping/fnet_pingclient_app tcp/localhost:%d", + PORT0).c_str())); + f1 = true; + } +} + +TEST_MT_F("ping times out", 2, bool()) { + if (thread_id == 0) { + SlaveProc proc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else { + TEST_BARRIER(); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/ping/fnet_pingclient_app tcp/localhost:%d", + PORT0).c_str())); + f1 = true; + } +} + +TEST_MT_F("ping with proxy", 3, bool()) { + if (thread_id == 0) { + SlaveProc proc(vespalib::make_string("../../examples/ping/fnet_pingserver_app tcp/%d", + PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else if (thread_id == 1) { + SlaveProc proc(vespalib::make_string("../../examples/proxy/fnet_proxy_app tcp/%d tcp/localhost:%d", + PORT1, PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else { + TEST_BARRIER(); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/ping/fnet_pingclient_app tcp/localhost:%d", + PORT1).c_str())); + f1 = true; + } +} + +TEST_MT_F("rpc client server", 2, bool()) { + if (thread_id == 0) { + SlaveProc proc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else { + TEST_BARRIER(); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_client_app tcp/localhost:%d", + PORT0).c_str())); + f1 = true; + } +} + +TEST_MT_F("rpc echo client", 2, bool()) { + if (thread_id == 0) { + SlaveProc proc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else { + TEST_BARRIER(); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/frt/rpc/fnet_echo_client_app tcp/localhost:%d", + PORT0).c_str())); + f1 = true; + } +} + +TEST_MT_F("rpc info", 2, bool()) { + if (thread_id == 0) { + SlaveProc proc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else { + TEST_BARRIER(); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/frt/rpc/rpc_info tcp/localhost:%d", + PORT0).c_str())); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/frt/rpc/rpc_info tcp/localhost:%d verbose", + PORT0).c_str())); + f1 = true; + } +} + +TEST_MT_F("rpc invoke", 2, bool()) { + if (thread_id == 0) { + SlaveProc proc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_server_app tcp/%d", + PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else { + TEST_BARRIER(); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/frt/rpc/rpc_invoke tcp/localhost:%d frt.rpc.echo " + "b:1 h:2 i:4 l:8 f:0.5 d:0.25 s:foo", + PORT0).c_str())); + f1 = true; + } +} + +TEST_MT_F("rpc callback client server", 2, bool()) { + if (thread_id == 0) { + SlaveProc proc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_callback_server_app tcp/%d", + PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else { + TEST_BARRIER(); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_callback_client_app tcp/localhost:%d", + PORT0).c_str())); + f1 = true; + } +} + +TEST_MT_F("rpc callback client server with proxy", 3, bool()) { + if (thread_id == 0) { + SlaveProc proc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_callback_server_app tcp/%d", + PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else if (thread_id == 1) { + SlaveProc proc(vespalib::make_string("../../examples/frt/rpc/rpc_proxy tcp/%d tcp/localhost:%d", + PORT1, PORT0).c_str()); + TEST_BARRIER(); + EXPECT_TRUE(runProc(proc, f1)); + } else { + TEST_BARRIER(); + EXPECT_TRUE(runProc(vespalib::make_string("../../examples/frt/rpc/fnet_rpc_callback_client_app tcp/localhost:%d", + PORT1).c_str())); + f1 = true; + } +} + +TEST_MAIN_WITH_PROCESS_PROXY() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/fdselector/.gitignore b/fnet/src/tests/fdselector/.gitignore new file mode 100644 index 00000000000..68a9dea5652 --- /dev/null +++ b/fnet/src/tests/fdselector/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +fdselector_test +fnet_fdselector_test_app diff --git a/fnet/src/tests/fdselector/CMakeLists.txt b/fnet/src/tests/fdselector/CMakeLists.txt new file mode 100644 index 00000000000..149d3642983 --- /dev/null +++ b/fnet/src/tests/fdselector/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_fdselector_test_app + SOURCES + fdselector.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_fdselector_test_app COMMAND fnet_fdselector_test_app) diff --git a/fnet/src/tests/fdselector/DESC b/fnet/src/tests/fdselector/DESC new file mode 100644 index 00000000000..050e2a746d1 --- /dev/null +++ b/fnet/src/tests/fdselector/DESC @@ -0,0 +1 @@ +Test selecting on external file descriptors in the FNET event loop. diff --git a/fnet/src/tests/fdselector/FILES b/fnet/src/tests/fdselector/FILES new file mode 100644 index 00000000000..d03840ead52 --- /dev/null +++ b/fnet/src/tests/fdselector/FILES @@ -0,0 +1 @@ +fdselector.cpp diff --git a/fnet/src/tests/fdselector/fdselector.cpp b/fnet/src/tests/fdselector/fdselector.cpp new file mode 100644 index 00000000000..5af2716c0d4 --- /dev/null +++ b/fnet/src/tests/fdselector/fdselector.cpp @@ -0,0 +1,220 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + + +struct Handler : public FNET_IFDSelectorHandler +{ + int readEventCnt[2]; + int writeEventCnt[2]; + + Handler() + : readEventCnt(), + writeEventCnt() + { + reset(); + } + void readEvent(FNET_FDSelector *src) + { + readEventCnt[src->getContext()._value.INT]++; + } + void writeEvent(FNET_FDSelector *src) + { + writeEventCnt[src->getContext()._value.INT]++; + } + bool empty() + { + return ( readEventCnt[0] == 0 + && readEventCnt[1] == 0 + && writeEventCnt[0] == 0 + && writeEventCnt[1] == 0); + } + void reset() + { + readEventCnt[0] = 0; + readEventCnt[1] = 0; + writeEventCnt[0] = 0; + writeEventCnt[1] = 0; + } +}; + + +struct State +{ + int pipefd[2]; + FNET_Transport transport; + Handler handler; + + void eventLoop(int cnt) + { + for (int i = 0; i < cnt; ++i) { + transport.EventLoopIteration(); + } + } + bool checkEmpty() + { + eventLoop(1); + return handler.empty(); + } + void shutDown() + { + transport.ShutDown(false); + for (;;) { + if (!transport.EventLoopIteration()) { + return; + } + } + } + State() : pipefd(), transport(), handler() { + pipefd[0] = -1; + pipefd[1] = -1; + ASSERT_TRUE(pipe(pipefd) == 0); + ASSERT_TRUE(transport.InitEventLoop()); + ASSERT_TRUE(handler.empty()); + } + ~State() { + shutDown(); + } +}; + + +struct Selector : public FNET_FDSelector +{ + static FNET_Mutex mutex; + static int ctorCnt; + static int dtorCnt; + + Selector(State &state, uint32_t idx) + : FNET_FDSelector(&state.transport, state.pipefd[idx], + &state.handler, FNET_Context(idx)) + { + mutex.Lock(); + ctorCnt++; + mutex.Unlock(); + } + ~Selector() + { + mutex.Lock(); + dtorCnt++; + mutex.Unlock(); + } +}; + +FNET_Mutex Selector::mutex; +int Selector::ctorCnt = 0; +int Selector::dtorCnt = 0; + + +TEST_F("testEmptySelection", State()) { + State &state = f1; + Selector *sel_0 = new Selector(state, 0); + Selector *sel_1 = new Selector(state, 1); + + state.eventLoop(5); + EXPECT_TRUE(state.handler.empty()); + + sel_0->dispose(); + sel_1->dispose(); +} + + +TEST_F("testWriteEvent", State()) { + State &state = f1; + Selector *sel = new Selector(state, 1); + + sel->updateWriteSelection(true); + state.eventLoop(10); + EXPECT_TRUE(state.handler.writeEventCnt[1] > 7); + state.handler.writeEventCnt[1] = 0; + EXPECT_TRUE(state.handler.empty()); + + sel->dispose(); + EXPECT_TRUE(state.checkEmpty()); +} + + +TEST_F("testReadEvent", State()) { + State &state = f1; + char buf[16]; + char buf2[16]; + strcpy(buf, "test"); + strcpy(buf2, "bogus"); + + Selector *sel = new Selector(state, 0); + + sel->updateReadSelection(true); + EXPECT_TRUE(state.checkEmpty()); + EXPECT_TRUE(state.checkEmpty()); + EXPECT_TRUE(state.checkEmpty()); + + int res = write(state.pipefd[1], buf, 5); + EXPECT_TRUE(res == 5); + + state.eventLoop(10); + EXPECT_TRUE(state.handler.readEventCnt[0] > 7); + state.handler.readEventCnt[0] = 0; + EXPECT_TRUE(state.handler.empty()); + + res = read(state.pipefd[0], buf2, 10); + EXPECT_TRUE(res == 5); + EXPECT_TRUE(strcmp(buf, buf2) == 0); + + state.eventLoop(10); + EXPECT_TRUE(state.handler.readEventCnt[0] < 4); + state.handler.readEventCnt[0] = 0; + EXPECT_TRUE(state.handler.empty()); + + sel->dispose(); + EXPECT_TRUE(state.checkEmpty()); +} + + +TEST_F("testDispose", State()) { + State &state = f1; + Selector *sel = new Selector(state, 1); + + sel->updateWriteSelection(true); + state.eventLoop(10); + EXPECT_TRUE(state.handler.writeEventCnt[1] > 7); + state.handler.writeEventCnt[1] = 0; + EXPECT_TRUE(state.handler.empty()); + + sel->dispose(); + EXPECT_TRUE(state.checkEmpty()); +} + + +TEST_F("testToggleEvent", State()) { + State &state = f1; + Selector *sel = new Selector(state, 1); + + sel->updateWriteSelection(true); + state.eventLoop(10); + EXPECT_TRUE(state.handler.writeEventCnt[1] > 7); + state.handler.writeEventCnt[1] = 0; + EXPECT_TRUE(state.handler.empty()); + + sel->updateWriteSelection(false); + state.eventLoop(10); + EXPECT_TRUE(state.handler.writeEventCnt[1] < 4); + state.handler.writeEventCnt[1] = 0; + EXPECT_TRUE(state.handler.empty()); + + sel->updateWriteSelection(true); + state.eventLoop(10); + EXPECT_TRUE(state.handler.writeEventCnt[1] > 7); + state.handler.writeEventCnt[1] = 0; + EXPECT_TRUE(state.handler.empty()); + + sel->dispose(); + EXPECT_TRUE(state.checkEmpty()); +} + +TEST_MAIN() { + ASSERT_TRUE(Selector::ctorCnt == 0); + ASSERT_TRUE(Selector::dtorCnt == 0); + TEST_RUN_ALL(); + EXPECT_TRUE(Selector::ctorCnt > 0); + EXPECT_TRUE(Selector::dtorCnt > 0); + EXPECT_TRUE(Selector::ctorCnt == Selector::dtorCnt); +} diff --git a/fnet/src/tests/frt/memorytub/.gitignore b/fnet/src/tests/frt/memorytub/.gitignore new file mode 100644 index 00000000000..e61f6585695 --- /dev/null +++ b/fnet/src/tests/frt/memorytub/.gitignore @@ -0,0 +1,6 @@ +*.core +.depend +Makefile +core +memorytub_test +fnet_memorytub_test_app diff --git a/fnet/src/tests/frt/memorytub/CMakeLists.txt b/fnet/src/tests/frt/memorytub/CMakeLists.txt new file mode 100644 index 00000000000..1b6aa6e778b --- /dev/null +++ b/fnet/src/tests/frt/memorytub/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_memorytub_test_app + SOURCES + memorytub.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_memorytub_test_app COMMAND fnet_memorytub_test_app) diff --git a/fnet/src/tests/frt/memorytub/DESC b/fnet/src/tests/frt/memorytub/DESC new file mode 100644 index 00000000000..b075fd81de5 --- /dev/null +++ b/fnet/src/tests/frt/memorytub/DESC @@ -0,0 +1 @@ +Test the memorytub class. diff --git a/fnet/src/tests/frt/memorytub/FILES b/fnet/src/tests/frt/memorytub/FILES new file mode 100644 index 00000000000..58e3592a833 --- /dev/null +++ b/fnet/src/tests/frt/memorytub/FILES @@ -0,0 +1 @@ +memorytub.cpp diff --git a/fnet/src/tests/frt/memorytub/memorytub.cpp b/fnet/src/tests/frt/memorytub/memorytub.cpp new file mode 100644 index 00000000000..a6be26bc860 --- /dev/null +++ b/fnet/src/tests/frt/memorytub/memorytub.cpp @@ -0,0 +1,124 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + +//--------------------------------------------------------------- + +enum { + SMALL_ALLOCS = 90, + BIG_ALLOCS = 10, + ALLOCS = SMALL_ALLOCS + BIG_ALLOCS, + SMALL_SIZE = 407, + BIG_SIZE = 40700, + NOID = 99999 +}; + +//--------------------------------------------------------------- + +struct Fixture { + FRT_MemoryTub _tub; + char *_res[ALLOCS]; + uint32_t _i; + uint32_t _j; + Fixture() : _tub(), _res(), _i(0), _j(0) {} + bool overlap(char *start1, char *end1, + char *start2, char *end2); + bool inTub(char *pt, char *end); + bool notInTub(char *pt, char *end); +}; + +//--------------------------------------------------------------- + +bool +Fixture::overlap(char *start1, char *end1, + char *start2, char *end2) +{ + if (start1 == end1) + return false; + + if (start2 == end2) + return false; + + if (start2 >= start1 && start2 < end1) + return true; + + if (end2 > start1 && end2 <= end1) + return true; + + if (start1 >= start2 && start1 < end2) + return true; + + if (end1 > start2 && end1 <= end2) + return true; + + return false; +} + + +bool +Fixture::inTub(char *pt, char *end) +{ + for (char *p = pt; p < end; p++) + if (!_tub.InTub(p)) + return false; + return true; +} + + +bool +Fixture::notInTub(char *pt, char *end) +{ + for (char *p = pt; p < end; p++) + if (_tub.InTub(p)) + return false; + return true; +} + +//--------------------------------------------------------------- + +TEST_F("memory tub", Fixture()) { + for(f1._i = 0; f1._i < ALLOCS; f1._i++) + f1._res[f1._i] = NULL; + f1._i = NOID; + f1._j = NOID; + + EXPECT_TRUE(!f1._tub.InTub(&f1._tub)); + EXPECT_TRUE((uint32_t)SMALL_SIZE < (uint32_t)FRT_MemoryTub::ALLOC_LIMIT); + EXPECT_TRUE((uint32_t)BIG_SIZE > (uint32_t)FRT_MemoryTub::ALLOC_LIMIT); + EXPECT_TRUE((SMALL_SIZE * SMALL_ALLOCS) + > (FRT_MemoryTub::FIXED_SIZE + FRT_MemoryTub::CHUNK_SIZE)); + TEST_FLUSH(); + + for (f1._i = 0; f1._i < ALLOCS; f1._i++) { + uint32_t size_i = f1._i < SMALL_ALLOCS ? SMALL_SIZE : BIG_SIZE; + + f1._res[f1._i] = (char *) f1._tub.Alloc(size_i); + EXPECT_TRUE(((void *)f1._res[f1._i]) != ((void *)&f1._tub)); + memset(f1._res[f1._i], 0x55, size_i); + EXPECT_TRUE(f1.inTub(f1._res[f1._i], f1._res[f1._i] + size_i)); + } + TEST_FLUSH(); + + for (f1._i = 0; f1._i < ALLOCS; f1._i++) { + uint32_t size_i = f1._i < SMALL_ALLOCS ? SMALL_SIZE : BIG_SIZE; + EXPECT_TRUE(f1.inTub(f1._res[f1._i], f1._res[f1._i] + size_i)); + + for (f1._j = f1._i + 1; f1._j < ALLOCS; f1._j++) { + uint32_t size_j = f1._j < SMALL_ALLOCS ? SMALL_SIZE : BIG_SIZE; + EXPECT_TRUE(!f1.overlap(f1._res[f1._i], f1._res[f1._i] + size_i, + f1._res[f1._j], f1._res[f1._j] + size_j)); + } + } + TEST_FLUSH(); + + f1._tub.Reset(); + f1._j = NOID; + + for (f1._i = 0; f1._i < ALLOCS; f1._i++) { + uint32_t size_i = f1._i < SMALL_ALLOCS ? SMALL_SIZE : BIG_SIZE; + EXPECT_TRUE(!f1.inTub(f1._res[f1._i], f1._res[f1._i] + size_i)); + } + TEST_FLUSH(); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/frt/method_pt/.gitignore b/fnet/src/tests/frt/method_pt/.gitignore new file mode 100644 index 00000000000..5bb3f455d1d --- /dev/null +++ b/fnet/src/tests/frt/method_pt/.gitignore @@ -0,0 +1,6 @@ +*.core +.depend +Makefile +core +method_pt_test +fnet_method_pt_test_app diff --git a/fnet/src/tests/frt/method_pt/CMakeLists.txt b/fnet/src/tests/frt/method_pt/CMakeLists.txt new file mode 100644 index 00000000000..d5a9566dbba --- /dev/null +++ b/fnet/src/tests/frt/method_pt/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_method_pt_test_app + SOURCES + method_pt.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_method_pt_test_app COMMAND fnet_method_pt_test_app) diff --git a/fnet/src/tests/frt/method_pt/DESC b/fnet/src/tests/frt/method_pt/DESC new file mode 100644 index 00000000000..81e7d2c6d1c --- /dev/null +++ b/fnet/src/tests/frt/method_pt/DESC @@ -0,0 +1,2 @@ +Ensure that the method pointer magic used by FRT works with the +current compiler. diff --git a/fnet/src/tests/frt/method_pt/FILES b/fnet/src/tests/frt/method_pt/FILES new file mode 100644 index 00000000000..9586cd113fb --- /dev/null +++ b/fnet/src/tests/frt/method_pt/FILES @@ -0,0 +1 @@ +method_pt.cpp diff --git a/fnet/src/tests/frt/method_pt/method_pt.cpp b/fnet/src/tests/frt/method_pt/method_pt.cpp new file mode 100644 index 00000000000..539be6846a9 --- /dev/null +++ b/fnet/src/tests/frt/method_pt/method_pt.cpp @@ -0,0 +1,395 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include + +class Test; +class SimpleHandler; + +class MediumHandler1; +class MediumHandler2; +class MediumHandler3; + +class ComplexHandler1; +class ComplexHandler2; +class ComplexHandler3; + +//------------------------------------------------------------- + +Test *_test; + +FRT_Supervisor *_supervisor; +FRT_Target *_target; +SimpleHandler *_simpleHandler; +MediumHandler1 *_mediumHandler1; +MediumHandler2 *_mediumHandler2; +MediumHandler3 *_mediumHandler3; +ComplexHandler1 *_complexHandler1; +ComplexHandler2 *_complexHandler2; +ComplexHandler3 *_complexHandler3; + +bool _mediumHandlerOK; +bool _complexHandlerOK; + +//------------------------------------------------------------- + +class MediumA +{ +public: + + /** + * Destructor. No cleanup needed for base class. + */ + virtual ~MediumA(void) { } + + virtual void foo() = 0; +}; + + +class MediumB +{ +public: + + /** + * Destructor. No cleanup needed for base class. + */ + virtual ~MediumB(void) { } + + virtual void bar() = 0; +}; + +//------------------------------------------------------------- + +class ComplexA +{ +private: + uint32_t _fill1; + uint32_t _fill2; + uint32_t _fill3; + +public: + + /** + * Destructor. No cleanup needed for base class. + */ + virtual ~ComplexA(void) { } + + ComplexA() : _fill1(1), _fill2(2), _fill3(3) {} + virtual void foo() {} +}; + + +class ComplexB +{ +private: + uint32_t _fill1; + uint32_t _fill2; + uint32_t _fill3; + +public: + + /** + * Destructor. No cleanup needed for base class. + */ + virtual ~ComplexB(void) { } + + ComplexB() : _fill1(1), _fill2(2), _fill3(3) {} + virtual void bar() {} +}; + +//------------------------------------------------------------- + +class SimpleHandler : public FRT_Invokable +{ +public: + void RPC_Method(FRT_RPCRequest *req); +}; + +//------------------------------------------------------------- + +class MediumHandler1 : public FRT_Invokable, + public MediumA, + public MediumB +{ +public: + virtual void foo() {} + virtual void bar() {} + void RPC_Method(FRT_RPCRequest *req); +}; + + +class MediumHandler2 : public MediumA, + public FRT_Invokable, + public MediumB +{ +public: + virtual void foo() {} + virtual void bar() {} + void RPC_Method(FRT_RPCRequest *req); +}; + + +class MediumHandler3 : public MediumA, + public MediumB, + public FRT_Invokable +{ +public: + virtual void foo() {} + virtual void bar() {} + void RPC_Method(FRT_RPCRequest *req); +}; + +//------------------------------------------------------------- + +class ComplexHandler1 : public FRT_Invokable, + public ComplexA, + public ComplexB +{ +public: + virtual void foo() {} + virtual void bar() {} + void RPC_Method(FRT_RPCRequest *req); +}; + + +class ComplexHandler2 : public ComplexA, + public FRT_Invokable, + public ComplexB +{ +public: + virtual void foo() {} + virtual void bar() {} + void RPC_Method(FRT_RPCRequest *req); +}; + + +class ComplexHandler3 : public ComplexA, + public ComplexB, + public FRT_Invokable +{ +public: + virtual void foo() {} + virtual void bar() {} + void RPC_Method(FRT_RPCRequest *req); +}; + +//------------------------------------------------------------- + +void initTest() { + _supervisor = new FRT_Supervisor(); + _simpleHandler = new SimpleHandler(); + _mediumHandler1 = new MediumHandler1(); + _mediumHandler2 = new MediumHandler2(); + _mediumHandler3 = new MediumHandler3(); + _complexHandler1 = new ComplexHandler1(); + _complexHandler2 = new ComplexHandler2(); + _complexHandler3 = new ComplexHandler3(); + + ASSERT_TRUE(_supervisor != NULL); + ASSERT_TRUE(_simpleHandler != NULL); + ASSERT_TRUE(_mediumHandler1 != NULL); + ASSERT_TRUE(_mediumHandler2 != NULL); + ASSERT_TRUE(_mediumHandler3 != NULL); + ASSERT_TRUE(_complexHandler1 != NULL); + ASSERT_TRUE(_complexHandler2 != NULL); + ASSERT_TRUE(_complexHandler3 != NULL); + + ASSERT_TRUE(_supervisor->Listen(0)); + std::string spec = vespalib::make_string("tcp/localhost:%d", + _supervisor->GetListenPort()); + _target = _supervisor->GetTarget(spec.c_str()); + ASSERT_TRUE(_target != NULL); + + bool startOK = _supervisor->Start(); + ASSERT_TRUE(startOK); + + FRT_ReflectionBuilder rb(_supervisor); + + //------------------------------------------------------------------- + + rb.DefineMethod("simpleMethod", "", "", true, + FRT_METHOD(SimpleHandler::RPC_Method), + _simpleHandler); + + //------------------------------------------------------------------- + + rb.DefineMethod("mediumMethod1", "", "", true, + FRT_METHOD(MediumHandler1::RPC_Method), + _mediumHandler1); + + rb.DefineMethod("mediumMethod2", "", "", true, + FRT_METHOD(MediumHandler2::RPC_Method), + _mediumHandler2); + + rb.DefineMethod("mediumMethod3", "", "", true, + FRT_METHOD(MediumHandler3::RPC_Method), + _mediumHandler3); + + //------------------------------------------------------------------- + + rb.DefineMethod("complexMethod1", "", "", true, + FRT_METHOD(ComplexHandler1::RPC_Method), + _complexHandler1); + + rb.DefineMethod("complexMethod2", "", "", true, + FRT_METHOD(ComplexHandler2::RPC_Method), + _complexHandler2); + + rb.DefineMethod("complexMethod3", "", "", true, + FRT_METHOD(ComplexHandler3::RPC_Method), + _complexHandler3); + + //------------------------------------------------------------------- + + _mediumHandlerOK = true; + _complexHandlerOK = true; +} + + +void finiTest() { + _supervisor->ShutDown(true); + delete _complexHandler1; + delete _complexHandler2; + delete _complexHandler3; + delete _mediumHandler1; + delete _mediumHandler2; + delete _mediumHandler3; + delete _simpleHandler; + _target->SubRef(); + delete _supervisor; +} + + +TEST("method pt") { + FRT_RPCRequest *req = _supervisor->AllocRPCRequest(); + req->SetMethodName("simpleMethod"); + _target->InvokeSync(req, 60.0); + EXPECT_TRUE(!req->IsError()); + + //-------------------------------- MEDIUM + + req->SubRef(); + req = _supervisor->AllocRPCRequest(); + req->SetMethodName("mediumMethod1"); + _target->InvokeSync(req, 60.0); + EXPECT_TRUE(!req->IsError()); + + req->SubRef(); + req = _supervisor->AllocRPCRequest(); + req->SetMethodName("mediumMethod2"); + _target->InvokeSync(req, 60.0); + EXPECT_TRUE(!req->IsError()); + + req->SubRef(); + req = _supervisor->AllocRPCRequest(); + req->SetMethodName("mediumMethod3"); + _target->InvokeSync(req, 60.0); + EXPECT_TRUE(!req->IsError()); + + //-------------------------------- COMPLEX + + req->SubRef(); + req = _supervisor->AllocRPCRequest(); + req->SetMethodName("complexMethod1"); + _target->InvokeSync(req, 60.0); + EXPECT_TRUE(!req->IsError()); + + req->SubRef(); + req = _supervisor->AllocRPCRequest(); + req->SetMethodName("complexMethod2"); + _target->InvokeSync(req, 60.0); + EXPECT_TRUE(!req->IsError()); + + req->SubRef(); + req = _supervisor->AllocRPCRequest(); + req->SetMethodName("complexMethod3"); + _target->InvokeSync(req, 60.0); + EXPECT_TRUE(!req->IsError()); + + if (_mediumHandlerOK) { + fprintf(stderr, "Interface inheritance OK for method handlers\n"); + } else { + fprintf(stderr, "Interface inheritance NOT ok for method handlers\n"); + } + + if (_complexHandlerOK) { + fprintf(stderr, "Object inheritance OK for method handlers\n"); + } else { + fprintf(stderr, "Object inheritance NOT ok for method handlers\n"); + } + + req->SubRef(); +} + +//------------------------------------------------------------- + +void +SimpleHandler::RPC_Method(FRT_RPCRequest *req) +{ + (void) req; + EXPECT_TRUE(this == _simpleHandler); +} + +//------------------------------------------------------------- + +void +MediumHandler1::RPC_Method(FRT_RPCRequest *req) +{ + (void) req; + _mediumHandlerOK = (_mediumHandlerOK && + this == _mediumHandler1); +} + + +void +MediumHandler2::RPC_Method(FRT_RPCRequest *req) +{ + (void) req; + _mediumHandlerOK = (_mediumHandlerOK && + this == _mediumHandler2); +} + + +void +MediumHandler3::RPC_Method(FRT_RPCRequest *req) +{ + (void) req; + _mediumHandlerOK = (_mediumHandlerOK && + this == _mediumHandler3); +} + +//------------------------------------------------------------- + +void +ComplexHandler1::RPC_Method(FRT_RPCRequest *req) +{ + (void) req; + _complexHandlerOK = (_complexHandlerOK && + this == _complexHandler1); +} + + +void +ComplexHandler2::RPC_Method(FRT_RPCRequest *req) +{ + (void) req; + _complexHandlerOK = (_complexHandlerOK && + this == _complexHandler2); +} + + +void +ComplexHandler3::RPC_Method(FRT_RPCRequest *req) +{ + (void) req; + _complexHandlerOK = (_complexHandlerOK && + this == _complexHandler3); +} + +//------------------------------------------------------------- + +TEST_MAIN() { + initTest(); + TEST_RUN_ALL(); + finiTest(); +} diff --git a/fnet/src/tests/frt/parallel_rpc/.gitignore b/fnet/src/tests/frt/parallel_rpc/.gitignore new file mode 100644 index 00000000000..7b4b7428e52 --- /dev/null +++ b/fnet/src/tests/frt/parallel_rpc/.gitignore @@ -0,0 +1 @@ +fnet_parallel_rpc_test_app diff --git a/fnet/src/tests/frt/parallel_rpc/CMakeLists.txt b/fnet/src/tests/frt/parallel_rpc/CMakeLists.txt new file mode 100644 index 00000000000..00a0c12e413 --- /dev/null +++ b/fnet/src/tests/frt/parallel_rpc/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_parallel_rpc_test_app + SOURCES + parallel_rpc_test.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_parallel_rpc_test_app COMMAND fnet_parallel_rpc_test_app) diff --git a/fnet/src/tests/frt/parallel_rpc/parallel_rpc_test.cpp b/fnet/src/tests/frt/parallel_rpc/parallel_rpc_test.cpp new file mode 100644 index 00000000000..723f519cd37 --- /dev/null +++ b/fnet/src/tests/frt/parallel_rpc/parallel_rpc_test.cpp @@ -0,0 +1,129 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include +#include +#include + +using vespalib::BenchmarkTimer; + +struct Rpc : FRT_Invokable { + FastOS_ThreadPool thread_pool; + FNET_Transport transport; + FRT_Supervisor orb; + Rpc(size_t num_threads) + : thread_pool(128 * 1024), transport(num_threads), orb(&transport, &thread_pool) {} + void start() { + ASSERT_TRUE(transport.Start(&thread_pool)); + } + uint32_t listen() { + ASSERT_TRUE(orb.Listen(0)); + return orb.GetListenPort(); + } + FRT_Target *connect(uint32_t port) { + return orb.GetTarget(port); + } + ~Rpc() { + transport.ShutDown(true); + thread_pool.Close(); + } +}; + +struct Server : Rpc { + uint32_t port; + Server(size_t num_threads) : Rpc(num_threads), port(listen()) { + init_rpc(); + start(); + } + void init_rpc() { + FRT_ReflectionBuilder rb(&orb); + rb.DefineMethod("inc", "l", "l", true, FRT_METHOD(Server::rpc_inc), this); + rb.MethodDesc("increment a 64-bit integer"); + rb.ParamDesc("in", "an integer (64 bit)"); + rb.ReturnDesc("out", "in + 1 (64 bit)"); + } + void rpc_inc(FRT_RPCRequest *req) { + FRT_Values ¶ms = *req->GetParams(); + FRT_Values &ret = *req->GetReturn(); + ret.AddInt64(params[0]._intval64 + 1); + } +}; + +struct Client : Rpc { + uint32_t port; + Client(size_t num_threads, const Server &server) : Rpc(num_threads), port(server.port) { + start(); + } + FRT_Target *connect() { return Rpc::connect(port); } +}; + +struct Result { + std::vector req_per_sec; + Result(size_t num_threads) : req_per_sec(num_threads, 0.0) {} + double throughput() const { + double sum = 0.0; + for (double sample: req_per_sec) { + sum += sample; + } + return sum; + } + double latency_ms() const { + double avg_req_per_sec = throughput() / req_per_sec.size(); + double avg_sec_per_req = 1.0 / avg_req_per_sec; + return avg_sec_per_req * 1000.0; + } + void print() const { + fprintf(stderr, "total throughput: %f req/s\n", throughput()); + fprintf(stderr, "average latency : %f ms\n", latency_ms()); + } +}; + +void perform_test(size_t thread_id, Client &client, Result &result) { + uint64_t seq = 0; + FRT_Target *target = client.connect(); + FRT_RPCRequest *req = client.orb.AllocRPCRequest(); + auto invoke = [&seq, target, &client, &req](){ + req = client.orb.AllocRPCRequest(req); + req->SetMethodName("inc"); + req->GetParams()->AddInt64(seq); + target->InvokeSync(req, 60.0); + ASSERT_TRUE(req->CheckReturnTypes("l")); + uint64_t ret = req->GetReturn()->GetValue(0)._intval64; + EXPECT_EQUAL(ret, seq + 1); + seq = ret; + }; + size_t loop_cnt = 128; + BenchmarkTimer::benchmark(invoke, invoke, 1.0); + BenchmarkTimer timer(3.0); + while (timer.has_budget()) { + timer.before(); + for (size_t i = 0; i < loop_cnt; ++i) { + invoke(); + } + timer.after(); + } + double t = timer.min_time(); + BenchmarkTimer::benchmark(invoke, invoke, 1.0); + EXPECT_GREATER_EQUAL(seq, loop_cnt); + result.req_per_sec[thread_id] = double(loop_cnt) / t; + req->SubRef(); + target->SubRef(); + TEST_BARRIER(); + if (thread_id == 0) { + result.print(); + } +} + +TEST_MT_FFF("parallel rpc with 1/1 transport threads and 128 user threads", + 128, Server(1), Client(1, f1), Result(num_threads)) { perform_test(thread_id, f2, f3); } + +TEST_MT_FFF("parallel rpc with 1/8 transport threads and 128 user threads", + 128, Server(8), Client(1, f1), Result(num_threads)) { perform_test(thread_id, f2, f3); } + +TEST_MT_FFF("parallel rpc with 8/1 transport threads and 128 user threads", + 128, Server(1), Client(8, f1), Result(num_threads)) { perform_test(thread_id, f2, f3); } + +TEST_MT_FFF("parallel rpc with 8/8 transport threads and 128 user threads", + 128, Server(8), Client(8, f1), Result(num_threads)) { perform_test(thread_id, f2, f3); } + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/frt/rpc/.gitignore b/fnet/src/tests/frt/rpc/.gitignore new file mode 100644 index 00000000000..be31ed66868 --- /dev/null +++ b/fnet/src/tests/frt/rpc/.gitignore @@ -0,0 +1,12 @@ +*.core +.depend +Makefile +core +detach_return_invoke_test +invoke_test +session_test +sharedblob_test +fnet_detach_return_invoke_test_app +fnet_invoke_test_app +fnet_session_test_app +fnet_sharedblob_test_app diff --git a/fnet/src/tests/frt/rpc/CMakeLists.txt b/fnet/src/tests/frt/rpc/CMakeLists.txt new file mode 100644 index 00000000000..806a78ec6b7 --- /dev/null +++ b/fnet/src/tests/frt/rpc/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_invoke_test_app + SOURCES + invoke.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_invoke_test_app COMMAND fnet_invoke_test_app) +vespa_add_executable(fnet_detach_return_invoke_test_app + SOURCES + detach_return_invoke.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_detach_return_invoke_test_app COMMAND fnet_detach_return_invoke_test_app) +vespa_add_executable(fnet_session_test_app + SOURCES + session.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_session_test_app COMMAND fnet_session_test_app) +vespa_add_executable(fnet_sharedblob_test_app + SOURCES + sharedblob.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_sharedblob_test_app COMMAND fnet_sharedblob_test_app) diff --git a/fnet/src/tests/frt/rpc/DESC b/fnet/src/tests/frt/rpc/DESC new file mode 100644 index 00000000000..017c68b41f8 --- /dev/null +++ b/fnet/src/tests/frt/rpc/DESC @@ -0,0 +1 @@ +Various tests related to rpc invocation. diff --git a/fnet/src/tests/frt/rpc/FILES b/fnet/src/tests/frt/rpc/FILES new file mode 100644 index 00000000000..e038169da5d --- /dev/null +++ b/fnet/src/tests/frt/rpc/FILES @@ -0,0 +1,3 @@ +invoke.cpp +session.cpp +sharedblob.cpp diff --git a/fnet/src/tests/frt/rpc/detach_return_invoke.cpp b/fnet/src/tests/frt/rpc/detach_return_invoke.cpp new file mode 100644 index 00000000000..b689671372b --- /dev/null +++ b/fnet/src/tests/frt/rpc/detach_return_invoke.cpp @@ -0,0 +1,69 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include + +struct Receptor : public FRT_IRequestWait +{ + FRT_RPCRequest *req; + + Receptor() : req(0) {} + void RequestDone(FRT_RPCRequest *r) { + req = r; + } +}; + +struct Server : public FRT_Invokable +{ + FRT_Supervisor &orb; + Receptor &receptor; + + Server(FRT_Supervisor &s, Receptor &r) : orb(s), receptor(r) { + FRT_ReflectionBuilder rb(&s); + rb.DefineMethod("hook", "", "", true, + FRT_METHOD(Server::rpc_hook), this); + } + + void rpc_hook(FRT_RPCRequest *req) { + FNET_Connection *conn = req->GetConnection(); + conn->AddRef(); // need to keep it alive + req->Detach(); + req->Return(); // will free request channel + FRT_RPCRequest *r = orb.AllocRPCRequest(); + r->SetMethodName("frt.rpc.ping"); + // might re-use request channel before it is unlinked from hashmap + orb.InvokeAsync(orb.GetTransport(), conn, r, 5.0, &receptor); + conn->SubRef(); // invocation will now keep the connection alive as needed + } +}; + +TEST("detach return invoke") { + Receptor receptor; + FRT_Supervisor orb; + Server server(orb, receptor); + ASSERT_TRUE(orb.Listen(0)); + ASSERT_TRUE(orb.Start()); + std::string spec = vespalib::make_string("tcp/localhost:%d", orb.GetListenPort()); + FRT_Target *target = orb.Get2WayTarget(spec.c_str()); + FRT_RPCRequest *req = orb.AllocRPCRequest(); + + req->SetMethodName("hook"); + target->InvokeSync(req, 5.0); + EXPECT_TRUE(!req->IsError()); + for (uint32_t i = 0; i < 1000; ++i) { + if (receptor.req != 0) { + break; + } + FastOS_Thread::Sleep(10); + } + req->SubRef(); + target->SubRef(); + orb.ShutDown(true); + if (receptor.req != 0) { + EXPECT_TRUE(!receptor.req->IsError()); + receptor.req->SubRef(); + } + EXPECT_TRUE(receptor.req != 0); +}; + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/frt/rpc/invoke.cpp b/fnet/src/tests/frt/rpc/invoke.cpp new file mode 100644 index 00000000000..7983d2eb9d8 --- /dev/null +++ b/fnet/src/tests/frt/rpc/invoke.cpp @@ -0,0 +1,938 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + +//------------------------------------------------------------- + +FNET_Mutex _delayedReturnCntLock; +uint32_t _delayedReturnCnt = 0; + +uint32_t _phase_simple_cnt = 0; +uint32_t _phase_void_cnt = 0; +uint32_t _phase_speed_cnt = 0; +uint32_t _phase_advanced_cnt = 0; +uint32_t _phase_error_cnt = 0; +uint32_t _phase_timeout_cnt = 0; +uint32_t _phase_abort_cnt = 0; +uint32_t _phase_echo_cnt = 0; + +//------------------------------------------------------------- + +struct LockedReqWait : public FRT_IRequestWait +{ + FNET_Cond _cond; // cond used to signal req done + bool _done; // flag indicating req done + + FNET_Mutex _lockLock; // lock protecting virtual lock + bool _lock; // virtual lock + bool _wasLocked; // was 'locked' when req done + + LockedReqWait() : _cond(), _done(false), _lockLock(), _lock(false), _wasLocked(false) {} + + void lock() { + _lockLock.Lock(); + _lock = true; + _lockLock.Unlock(); + } + + void unlock() { + _lockLock.Lock(); + _lock = false; + _lockLock.Unlock(); + } + + bool isLocked() { + _lockLock.Lock(); + bool ret = _lock; + _lockLock.Unlock(); + return ret; + } + + virtual void RequestDone(FRT_RPCRequest *) + { + _wasLocked = isLocked(); + _cond.Lock(); + _done = true; + _cond.Signal(); + _cond.Unlock(); + } + + void waitReq() + { + _cond.Lock(); + while(!_done) { + _cond.Wait(); + } + _cond.Unlock(); + } +}; + +//------------------------------------------------------------- + +class DelayedReturn : public FNET_Task +{ +private: + FRT_RPCRequest *_req; + + DelayedReturn(const DelayedReturn &); + DelayedReturn &operator=(const DelayedReturn &); + +public: + DelayedReturn(FNET_Scheduler *sched, + FRT_RPCRequest *req, + double delay) + : FNET_Task(sched), + _req(req) + { + _delayedReturnCntLock.Lock(); + _delayedReturnCnt++; + _delayedReturnCntLock.Unlock(); + Schedule(delay); + } + + void PerformTask() + { + _req->Return(); + _delayedReturnCntLock.Lock(); + _delayedReturnCnt--; + _delayedReturnCntLock.Unlock(); + } +}; + +//------------------------------------------------------------- + +class EchoTest : public FRT_Invokable +{ +private: + FRT_MemoryTub *_echo_tub; + FRT_Values *_echo_args; + + EchoTest(const EchoTest &); + EchoTest &operator=(const EchoTest &); + +public: + EchoTest() : _echo_tub(NULL), _echo_args(NULL) {} + ~EchoTest() + { + delete _echo_args; + delete _echo_tub; + } + + void Init(FRT_Supervisor *supervisor) + { + _echo_tub = new FRT_MemoryTub(); + _echo_args = new FRT_Values(_echo_tub); + assert(_echo_tub != NULL && _echo_args != NULL); + + FRT_ReflectionBuilder rb(supervisor); + rb.DefineMethod("echo", "*", "*", true, + FRT_METHOD(EchoTest::RPC_Echo), this); + + FRT_Values *args = _echo_args; + args->EnsureFree(16); + + args->AddInt8(8); + uint8_t *pt_int8 = args->AddInt8Array(3); + pt_int8[0] = 1; + pt_int8[1] = 2; + pt_int8[2] = 3; + + args->AddInt16(16); + uint16_t *pt_int16 = args->AddInt16Array(3); + pt_int16[0] = 2; + pt_int16[1] = 4; + pt_int16[2] = 6; + + args->AddInt32(32); + uint32_t *pt_int32 = args->AddInt32Array(3); + pt_int32[0] = 4; + pt_int32[1] = 8; + pt_int32[2] = 12; + + args->AddInt64(64); + uint64_t *pt_int64 = args->AddInt64Array(3); + pt_int64[0] = 8; + pt_int64[1] = 16; + pt_int64[2] = 24; + + args->AddFloat(32.5); + float *pt_float = args->AddFloatArray(3); + pt_float[0] = 0.25; + pt_float[1] = 0.5; + pt_float[2] = 0.75; + + args->AddDouble(64.5); + double *pt_double = args->AddDoubleArray(3); + pt_double[0] = 0.1; + pt_double[1] = 0.2; + pt_double[2] = 0.3; + + args->AddString("string"); + FRT_StringValue *pt_string = args->AddStringArray(3); + args->SetString(&pt_string[0], "str1"); + args->SetString(&pt_string[1], "str2"); + args->SetString(&pt_string[2], "str3"); + + args->AddData("data", 4); + FRT_DataValue *pt_data = args->AddDataArray(3); + args->SetData(&pt_data[0], "dat1", 4); + args->SetData(&pt_data[1], "dat2", 4); + args->SetData(&pt_data[2], "dat3", 4); + } + + bool PrepareEchoReq(FRT_RPCRequest *req) + { + FNET_DataBuffer buf; + + req->SetMethodName("echo"); + _echo_args->EncodeCopy(&buf); + req->GetParams()->DecodeCopy(&buf, buf.GetDataLen()); + return (req->GetParams()->Equals(_echo_args) && + _echo_args->Equals(req->GetParams())); + } + + void RPC_Echo(FRT_RPCRequest *req) + { + FNET_DataBuffer buf; + + req->GetParams()->EncodeCopy(&buf); + req->GetReturn()->DecodeCopy(&buf, buf.GetDataLen()); + if (!req->GetReturn()->Equals(_echo_args) || + !req->GetReturn()->Equals(req->GetParams())) + { + req->SetError(10000, "Streaming error"); + } + } +}; + +//------------------------------------------------------------- + +class TestRPC : public FRT_Invokable +{ +private: + FRT_Supervisor *_supervisor; + FNET_Scheduler *_scheduler; + uint32_t _intValue; + + TestRPC(const TestRPC &); + TestRPC &operator=(const TestRPC &); + +public: + TestRPC(FRT_Supervisor *supervisor, // server supervisor + FNET_Scheduler *scheduler) // client scheduler + : _supervisor(supervisor), + _scheduler(scheduler), + _intValue(0) + { + FRT_ReflectionBuilder rb(supervisor); + + rb.DefineMethod("inc", "i", "i", true, + FRT_METHOD(TestRPC::RPC_Inc), this); + rb.DefineMethod("setValue", "i", "", true, + FRT_METHOD(TestRPC::RPC_SetValue), this); + rb.DefineMethod("incValue", "", "", true, + FRT_METHOD(TestRPC::RPC_IncValue), this); + rb.DefineMethod("getValue", "", "i", true, + FRT_METHOD(TestRPC::RPC_GetValue), this); + rb.DefineMethod("testFast", "iiibb", "i", true, + FRT_METHOD(TestRPC::RPC_Test), this); + rb.DefineMethod("testSlow", "iiibb", "i", false, + FRT_METHOD(TestRPC::RPC_Test), this); + } + + void RPC_Test(FRT_RPCRequest *req) + { + FRT_Values ¶m = *req->GetParams(); + uint32_t value = param[0]._intval32; + uint32_t delay = param[1]._intval32; + uint32_t error = param[2]._intval32; + uint8_t extra = param[3]._intval8; + uint8_t async = param[4]._intval8; + + req->GetReturn()->AddInt32(value); + if (extra != 0) { + req->GetReturn()->AddInt32(value); + } + if (error != 0) { + req->SetError(error); + } + if (async != 0) { + req->Detach(); + if (delay == 0) { + req->Return(); + } else { + new (req->GetMemoryTub()) DelayedReturn(_scheduler, + req, + ((double)delay) / 1000.0); + } + } else { + + if (delay > 0) { + + const char *suffix = "testFast"; + uint32_t suffix_len = strlen(suffix); + uint32_t name_len = req->GetMethodNameLen(); + bool remote = req->GetContext()._value.VOIDP != NULL; + bool instant = name_len > suffix_len && + strcmp(req->GetMethodName() + name_len - suffix_len, suffix) == 0; + + if (remote && instant) { + + // block, but don't cripple server scheduler... + // (NB: in 'real life', instant methods should never block) + + FastOS_Time *now = _supervisor->GetTransport()->GetTimeSampler(); + FNET_Scheduler *scheduler = _supervisor->GetScheduler(); + assert(scheduler->GetTimeSampler() == now); + + while (delay > 0) { + if (delay > 20) { + FastOS_Thread::Sleep(20); + delay -= 20; + } else { + FastOS_Thread::Sleep(delay); + delay = 0; + } + now->SetNow(); + scheduler->CheckTasks(); + } + + } else { + + FastOS_Thread::Sleep(delay); + } + } + } + } + + void RPC_Inc(FRT_RPCRequest *req) + { + req->GetReturn()->AddInt32(req->GetParams()->GetValue(0)._intval32 + 1); + } + + void RPC_SetValue(FRT_RPCRequest *req) + { + _intValue = req->GetParams()->GetValue(0)._intval32; + } + + void RPC_IncValue(FRT_RPCRequest *req) + { + (void) req; + _intValue++; + } + + void RPC_GetValue(FRT_RPCRequest *req) + { + req->GetReturn()->AddInt32(_intValue); + } +}; + +//------------------------------------------------------------- + +enum { + OK_RET = 0, + BOGUS_RET = 1 +}; + +enum { + PHASE_NULL = 0, + PHASE_SETUP, + PHASE_SIMPLE, + PHASE_VOID, + PHASE_SPEED, + PHASE_ADVANCED, + PHASE_ERROR, + PHASE_TIMEOUT, + PHASE_ABORT, + PHASE_ECHO, + PHASE_SHUTDOWN, + PHASE_ZZZ +}; + +const char phase_names[PHASE_ZZZ][32] = +{ + "NULL", + "SETUP", + "SIMPLE", + "VOID", + "SPEED", + "ADVANCED", + "ERROR", + "TIMEOUT", + "ABORT", + "ECHO", + "SHUTDOWN" +}; + +enum { + TIMING_NULL = 0, + TIMING_INSTANT, + TIMING_NON_INSTANT, + TIMING_ZZZ +}; + +const char timing_names[TIMING_ZZZ][32] = +{ + "NULL", + "INSTANT", + "NON-INSTANT" +}; + +enum { + HANDLING_NULL = 0, + HANDLING_SYNC, + HANDLING_ASYNC, + HANDLING_ZZZ +}; + +const char handling_names[HANDLING_ZZZ][32] = +{ + "NULL", + "SYNC", + "ASYNC" +}; + +//------------------------------------------------------------- + +struct State { + FRT_Supervisor _client; + FRT_Supervisor _server; + TestRPC _rpc; + EchoTest _echo; + std::string _peerSpec; + uint32_t _testPhase; + uint32_t _timing; + uint32_t _handling; + double _timeout; + FRT_Target *_target; + FRT_RPCRequest *_req; + + State() + : _client(), + _server(), + _rpc(&_server, _client.GetScheduler()), + _echo(), + _peerSpec(), + _testPhase(PHASE_NULL), + _timing(TIMING_NULL), + _handling(HANDLING_NULL), + _timeout(5.0), + _target(NULL), + _req(NULL) + { + _client.GetTransport()->SetTCPNoDelay(true); + _server.GetTransport()->SetTCPNoDelay(true); + _echo.Init(&_server); + } + + void SetTimeout(double timeout) + { + _timeout = timeout; + } + + void NewReq() + { + if (_req != NULL) { + _req->SubRef(); + } + _req = new FRT_RPCRequest(); + } + + void FreeReq() + { + if (_req != NULL) { + _req->SubRef(); + } + _req = NULL; + } + + void LostReq() + { + _req = NULL; + } + + void PrepareTestMethod() + { + NewReq(); + bool instant = (_timing == TIMING_INSTANT); + if (_timing != TIMING_INSTANT && + _timing != TIMING_NON_INSTANT) + { + ASSERT_TRUE(false); // consult your dealer... + } + if (instant) { + _req->SetMethodName("testFast"); + } else { + _req->SetMethodName("testSlow"); + } + } + + void SetTestParams(uint32_t value, uint32_t delay, + uint32_t error = FRTE_NO_ERROR, + uint8_t extra = 0) + { + _req->GetParams()->AddInt32(value); + _req->GetParams()->AddInt32(delay); + _req->GetParams()->AddInt32(error); + _req->GetParams()->AddInt8(extra); + bool async = (_handling == HANDLING_ASYNC); + if (_handling != HANDLING_SYNC && + _handling != HANDLING_ASYNC) + { + ASSERT_TRUE(false); // consult your dealer... + } + _req->GetParams()->AddInt8((async) ? 1 : 0); + } + + void InvokeSync(); + void InvokeVoid(); + void InvokeAsync(FRT_IRequestWait *w); + void InvokeTest(uint32_t value, + uint32_t delay = 0, + uint32_t error = FRTE_NO_ERROR, + uint8_t extra = 0); + void InvokeTestAndAbort(uint32_t value, + uint32_t delay = 0, + uint32_t error = FRTE_NO_ERROR, + uint8_t extra = 0); + bool WaitForDelayedReturnCount(uint32_t wantedCount, double timeout); + +private: + State(const State &); + State &operator=(const State &); +}; + + +void +State::InvokeSync() +{ + _target->InvokeSync(_req, _timeout); +} + + +void +State::InvokeVoid() +{ + _target->InvokeVoid(_req); +} + + +void +State::InvokeAsync(FRT_IRequestWait *w) +{ + _target->InvokeAsync(_req, _timeout, w); +} + + +void +State::InvokeTest(uint32_t value, uint32_t delay, + uint32_t error, uint8_t extra) +{ + PrepareTestMethod(); + SetTestParams(value, delay, error, extra); + InvokeSync(); +} + + +void +State::InvokeTestAndAbort(uint32_t value, uint32_t delay, + uint32_t error, uint8_t extra) +{ + PrepareTestMethod(); + SetTestParams(value, delay, error, extra); + FRT_SingleReqWait w; + InvokeAsync(&w); + _req->Abort(); + w.WaitReq(); +} + +bool +State::WaitForDelayedReturnCount(uint32_t wantedCount, double timeout) +{ + FastOS_Time timer; + timer.SetNow(); + for (;;) { + _delayedReturnCntLock.Lock(); + uint32_t delayedReturnCnt = _delayedReturnCnt; + _delayedReturnCntLock.Unlock(); + if (delayedReturnCnt == wantedCount) { + return true; + } + if ((timer.MilliSecsToNow() / 1000.0) > timeout) { + return false; + } + FastOS_Thread::Sleep(10); + } +} + +//------------------------------------------------------------- + +bool CheckTypes(FRT_RPCRequest *req, const char *spec) { + return FRT_Values::CheckTypes(spec, req->GetReturnSpec()); +} + +FRT_Value &Get(FRT_RPCRequest *req, uint32_t idx) { + return req->GetReturn()->GetValue(idx); +} + +//------------------------------------------------------------- + +void TestSetup(State *_state) { + ASSERT_TRUE(_state->_testPhase == PHASE_SETUP); + + bool listenOK = _state->_server.Listen("tcp/0"); + + char spec[64]; + sprintf(spec, "tcp/localhost:%d", _state->_server.GetListenPort()); + _state->_peerSpec = spec; + + bool serverStartOK = _state->_server.Start(); + bool clientStartOK = _state->_client.Start(); + + ASSERT_TRUE(listenOK); + ASSERT_TRUE(serverStartOK); + ASSERT_TRUE(clientStartOK); + + _state->_target = _state->_client.GetTarget(_state->_peerSpec.c_str()); + _state->NewReq(); + _state->_req->SetMethodName("frt.rpc.ping"); + _state->_target->InvokeSync(_state->_req, 5.0); + ASSERT_TRUE(!_state->_req->IsError()); +} + + +void TestSimple(State *_state) { + ASSERT_TRUE(_state->_testPhase == PHASE_SIMPLE); + _phase_simple_cnt++; + _state->NewReq(); + _state->_req->SetMethodName("inc"); + _state->_req->GetParams()->AddInt32(502); + _state->InvokeSync(); + EXPECT_TRUE(!_state->_req->IsError() && + CheckTypes(_state->_req, "i") && + Get(_state->_req, 0)._intval32 == 503); +} + + +void TestVoid(State *_state) { + ASSERT_TRUE(_state->_testPhase == PHASE_VOID); + _phase_void_cnt++; + + _state->NewReq(); + _state->_req->SetMethodName("setValue"); + _state->_req->GetParams()->AddInt32(40); + _state->InvokeSync(); + EXPECT_TRUE(!_state->_req->IsError() && + CheckTypes(_state->_req, "")); + + _state->NewReq(); + _state->_req->SetMethodName("incValue"); + _state->InvokeVoid(); + _state->LostReq(); + + _state->NewReq(); + _state->_req->SetMethodName("incValue"); + _state->InvokeVoid(); + _state->LostReq(); + + _state->NewReq(); + _state->_req->SetMethodName("getValue"); + _state->InvokeSync(); + EXPECT_TRUE(!_state->_req->IsError() && + CheckTypes(_state->_req, "i") && + Get(_state->_req, 0)._intval32 == 42); +} + + +void TestSpeed(State *_state) { + ASSERT_TRUE(_state->_testPhase == PHASE_SPEED); + _phase_speed_cnt++; + + FastOS_Time start; + FastOS_Time stop; + uint32_t val = 0; + uint32_t cnt = 0; + + _state->NewReq(); + FRT_RPCRequest *req = _state->_req; + FRT_Target *target = _state->_target; + + // calibrate cnt to be used + start.SetNow(); + for (cnt = 0; cnt < 1000000; cnt++) { + req->SetMethodName("inc"); + req->GetParams()->AddInt32(0); + target->InvokeSync(req, 5.0); + if (req->IsError()) { + break; + } + req->Reset(); // ok if no error + if (start.MilliSecsToNow() > 20.0) { + break; + } + } + cnt = (cnt == 0) ? 1 : cnt * 10; + + fprintf(stderr, "checking invocation latency... (cnt = %d)\n", cnt); + + _state->NewReq(); + req = _state->_req; + + // actual benchmark + start.SetNow(); + for (uint32_t i = 0; i < cnt; i++) { + req->SetMethodName("inc"); + req->GetParams()->AddInt32(val); + target->InvokeSync(req, 60.0); + if (req->IsError()) { + fprintf(stderr, "... rpc error(%d): %s\n", + req->GetErrorCode(), + req->GetErrorMessage()); + break; + } + val = req->GetReturn()->GetValue(0)._intval32; + req->Reset(); // ok if no error + } + stop.SetNow(); + stop -= start; + double latency = stop.MilliSecs() / (double) cnt; + + EXPECT_EQUAL(val, cnt); + fprintf(stderr, "latency of invocation: %1.3f ms\n", latency); +} + + +void TestAdvanced(State *_state) { + ASSERT_TRUE(_state->_testPhase == PHASE_ADVANCED); + _phase_advanced_cnt++; + + // Test invocation + //---------------- + _state->InvokeTest(42); + EXPECT_TRUE(!_state->_req->IsError() && + CheckTypes(_state->_req, "i") && + Get(_state->_req, 0)._intval32 == 42); + + // Abort has no effect after request is done + //------------------------------------------ + _state->_req->Abort(); + EXPECT_TRUE(!_state->_req->IsError() && + CheckTypes(_state->_req, "i") && + Get(_state->_req, 0)._intval32 == 42); + + // Test invocation with delay + //--------------------------- + _state->InvokeTest(58, 100); + EXPECT_TRUE(!_state->_req->IsError() && + CheckTypes(_state->_req, "i") && + Get(_state->_req, 0)._intval32 == 58); +} + + +void TestError(State *_state) { + ASSERT_TRUE(_state->_testPhase == PHASE_ERROR); + _phase_error_cnt++; + + // bad target -> sync error -> avoid deadlock + //------------------------------------------- + if (_state->_handling == HANDLING_ASYNC) + { + // stash away valid target + FRT_Target *stateTarget = _state->_target; // backup of valid target + + _state->_target = _state->_client.GetTarget("bogus address"); + _state->NewReq(); + _state->_req->SetMethodName("frt.rpc.ping"); + LockedReqWait lw; + lw.lock(); + _state->InvokeAsync(&lw); + lw.unlock(); + lw.waitReq(); + EXPECT_TRUE(!lw._wasLocked); + EXPECT_TRUE(_state->_req->GetErrorCode() == FRTE_RPC_CONNECTION); + + // restore valid target + _state->_target->SubRef(); + _state->_target = stateTarget; + } + + // no such method + //--------------- + if (_state->_timing == TIMING_INSTANT && + _state->_handling == HANDLING_SYNC) + { + _state->NewReq(); + _state->_req->SetMethodName("bogus"); + _state->InvokeSync(); + EXPECT_TRUE(_state->_req->GetErrorCode() == FRTE_RPC_NO_SUCH_METHOD); + } + + // wrong params + //------------- + if (_state->_handling == HANDLING_SYNC) { + + _state->PrepareTestMethod(); + _state->InvokeSync(); + EXPECT_TRUE(_state->_req->GetErrorCode() == FRTE_RPC_WRONG_PARAMS); + + _state->PrepareTestMethod(); + _state->_req->GetParams()->AddInt32(42); + _state->_req->GetParams()->AddInt32(0); + _state->_req->GetParams()->AddInt8(0); + _state->_req->GetParams()->AddInt8(0); + _state->_req->GetParams()->AddInt8(0); + _state->InvokeSync(); + EXPECT_TRUE(_state->_req->GetErrorCode() == FRTE_RPC_WRONG_PARAMS); + + _state->PrepareTestMethod(); + _state->_req->GetParams()->AddInt32(42); + _state->_req->GetParams()->AddInt32(0); + _state->_req->GetParams()->AddInt32(0); + _state->_req->GetParams()->AddInt8(0); + _state->_req->GetParams()->AddInt8(0); + _state->_req->GetParams()->AddInt8(0); + _state->InvokeSync(); + EXPECT_TRUE(_state->_req->GetErrorCode() == FRTE_RPC_WRONG_PARAMS); + } + + // wrong return + //------------- + _state->InvokeTest(42, 0, 0, BOGUS_RET); + EXPECT_TRUE(_state->_req->GetErrorCode() == FRTE_RPC_WRONG_RETURN); + + // method failed + //-------------- + _state->InvokeTest(42, 0, 5000, BOGUS_RET); + EXPECT_TRUE(_state->_req->GetErrorCode() == 5000); +} + + +void TestTimeout(State *_state) { + ASSERT_TRUE(_state->_testPhase == PHASE_TIMEOUT); + _phase_timeout_cnt++; + + _state->SetTimeout(0.1); + + // Test timeout + //------------- + _state->InvokeTest(123, 5000); + EXPECT_TRUE(_state->_req->GetErrorCode() == FRTE_RPC_TIMEOUT); + FastOS_Thread::Sleep(5500); // settle + + _state->SetTimeout(5.0); +} + + +void TestAbort(State *_state) { + ASSERT_TRUE(_state->_testPhase == PHASE_ABORT); + _phase_abort_cnt++; + + // Test abort + //----------- + _state->InvokeTestAndAbort(456, 1000); + EXPECT_TRUE(_state->_req->GetErrorCode() == FRTE_RPC_ABORT); + FastOS_Thread::Sleep(1500); // settle +} + + +void TestEcho(State *_state) { + ASSERT_TRUE(_state->_testPhase == PHASE_ECHO); + _phase_echo_cnt++; + + // Test echo + //---------- + _state->NewReq(); + EXPECT_TRUE(_state->_echo.PrepareEchoReq(_state->_req)); + _state->InvokeSync(); + EXPECT_TRUE(!_state->_req->IsError()); + EXPECT_TRUE(_state->_req->GetReturn()->Equals(_state->_req->GetParams())); +} + + +TEST_F("invoke test", State()) { + State *_state = &f1; + + _state->_testPhase = PHASE_SETUP; + TestSetup(_state); + + for (_state->_testPhase = PHASE_SIMPLE; + _state->_testPhase < PHASE_SHUTDOWN; + _state->_testPhase++) { + + { + for (_state->_timing = TIMING_INSTANT; + _state->_timing < TIMING_ZZZ; + _state->_timing++) { + + for (_state->_handling = HANDLING_SYNC; + _state->_handling < HANDLING_ZZZ; + _state->_handling++) { + + switch (_state->_testPhase) { + case PHASE_SIMPLE: + if (_state->_timing == TIMING_INSTANT && + _state->_handling == HANDLING_SYNC) + { + TestSimple(_state); + } + break; + case PHASE_VOID: + if (_state->_timing == TIMING_INSTANT && + _state->_handling == HANDLING_SYNC) + { + TestVoid(_state); + } + break; + case PHASE_SPEED: + if (_state->_timing == TIMING_INSTANT && + _state->_handling == HANDLING_SYNC) + { + TestSpeed(_state); + } + break; + case PHASE_ADVANCED: + TestAdvanced(_state); + break; + case PHASE_ERROR: + TestError(_state); + break; + case PHASE_TIMEOUT: + TestTimeout(_state); + break; + case PHASE_ABORT: + TestAbort(_state); + break; + case PHASE_ECHO: + if (_state->_timing == TIMING_INSTANT && + _state->_handling == HANDLING_SYNC) + { + TestEcho(_state); + } + break; + default: + ASSERT_TRUE(false); // consult your dealer... + } + } + } + } + } + _state->_testPhase = PHASE_SHUTDOWN; + _state->_timing = TIMING_NULL; + _state->_handling = HANDLING_NULL; + EXPECT_TRUE(_state->WaitForDelayedReturnCount(0, 120.0)); + _state->FreeReq(); + _state->_client.ShutDown(true); + _state->_server.ShutDown(true); + _state->_target->SubRef(); + _state->_target = NULL; + EXPECT_TRUE(_delayedReturnCnt == 0); + EXPECT_TRUE(_phase_simple_cnt == 1); + EXPECT_TRUE(_phase_void_cnt == 1); + EXPECT_TRUE(_phase_speed_cnt == 1); + EXPECT_TRUE(_phase_advanced_cnt == 4); + EXPECT_TRUE(_phase_error_cnt == 4); + EXPECT_TRUE(_phase_abort_cnt == 4); + EXPECT_TRUE(_phase_echo_cnt == 1); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/frt/rpc/session.cpp b/fnet/src/tests/frt/rpc/session.cpp new file mode 100644 index 00000000000..c39fe8cba05 --- /dev/null +++ b/fnet/src/tests/frt/rpc/session.cpp @@ -0,0 +1,124 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + + +class Session +{ +private: + static FNET_Mutex _lock; + static int _cnt; + int _val; + +public: + Session() : _val(0) + { + _lock.Lock(); + ++_cnt; + _lock.Unlock(); + } + + ~Session() + { + _lock.Lock(); + --_cnt; + _lock.Unlock(); + } + + void SetValue(int val) { _val = val; } + int GetValue() const { return _val; } + static int GetCnt() { return _cnt; } +}; + +FNET_Mutex Session::_lock; +int Session::_cnt(0); + + +struct RPC : public FRT_Invokable +{ + bool bogusFini; + + RPC() : bogusFini(false) {} + + void InitSession(FRT_RPCRequest *req) + { + Session *session = new Session(); + req->GetConnection()->SetContext(FNET_Context((void *) session)); + } + + void FiniSession(FRT_RPCRequest *req) + { + Session *session = + (Session *)req->GetConnection()->GetContext()._value.VOIDP; + bogusFini |= (session == NULL); + delete session; + } + + void GetValue(FRT_RPCRequest *req) + { + Session *session = + (Session *)req->GetConnection()->GetContext()._value.VOIDP; + req->GetReturn()->AddInt32(session->GetValue()); + } + + void SetValue(FRT_RPCRequest *req) + { + Session *session = + (Session *)req->GetConnection()->GetContext()._value.VOIDP; + session->SetValue(req->GetParams()->GetValue(0)._intval32); + } + + void Init(FRT_Supervisor *s) + { + FRT_ReflectionBuilder rb(s); + rb.DefineMethod("getValue", "", "i", true, + FRT_METHOD(RPC::GetValue), this); + rb.DefineMethod("setValue", "i", "", true, + FRT_METHOD(RPC::SetValue), this); + s->SetSessionInitHook(FRT_METHOD(RPC::InitSession), this); + s->SetSessionFiniHook(FRT_METHOD(RPC::FiniSession), this); + } +}; + +TEST("session") { + RPC rpc; + FRT_Supervisor orb; + char spec[64]; + rpc.Init(&orb); + ASSERT_TRUE(orb.Listen("tcp/0")); + sprintf(spec, "tcp/localhost:%d", orb.GetListenPort()); + ASSERT_TRUE(orb.Start()); + + FRT_Target *target = orb.GetTarget(spec); + FRT_RPCRequest *req = orb.AllocRPCRequest(); + + req->SetMethodName("getValue"); + target->InvokeSync(req, 5.0); + ASSERT_TRUE(!req->IsError() && + strcmp(req->GetReturnSpec(), "i") == 0 && + req->GetReturn()->GetValue(0)._intval32 == 0); + + req = orb.AllocRPCRequest(req); + req->SetMethodName("setValue"); + req->GetParams()->AddInt32(42); + target->InvokeSync(req, 5.0); + ASSERT_TRUE(!req->IsError() && + strcmp(req->GetReturnSpec(), "") == 0); + + req = orb.AllocRPCRequest(req); + req->SetMethodName("getValue"); + target->InvokeSync(req, 5.0); + ASSERT_TRUE(!req->IsError() && + strcmp(req->GetReturnSpec(), "i") == 0 && + req->GetReturn()->GetValue(0)._intval32 == 42); + + EXPECT_TRUE(Session::GetCnt() == 1); + + req->SubRef(); + target->SubRef(); + orb.ShutDown(true); + EXPECT_TRUE(Session::GetCnt() == 0); + EXPECT_TRUE(!rpc.bogusFini); +}; + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/frt/rpc/sharedblob.cpp b/fnet/src/tests/frt/rpc/sharedblob.cpp new file mode 100644 index 00000000000..bb115f1c65f --- /dev/null +++ b/fnet/src/tests/frt/rpc/sharedblob.cpp @@ -0,0 +1,256 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include + +struct MyBlob : FRT_ISharedBlob +{ + int refcnt; + MyBlob() : refcnt(1) {} + virtual uint32_t getLen() { return (strlen("blob_test") + 1); } + virtual const char *getData() { return "blob_test"; } + virtual void addRef() { ++refcnt; } + virtual void subRef() { --refcnt; } +}; + +struct Data +{ + enum { + SMALL = (FRT_MemoryTub::ALLOC_LIMIT / 2), + LARGE = (FRT_MemoryTub::ALLOC_LIMIT * 2) + }; + + char *buf; + uint32_t len; + + Data(const char *pt, uint32_t l) : buf(new char[l]), len(l) { + memcpy(buf, pt, len); + } + Data(uint32_t l, char c) : buf(new char[l]), len(l) { + memset(buf, c, len); + } + Data(const Data &rhs) : buf(new char[rhs.len]), len(rhs.len) { + memcpy(buf, rhs.buf, len); + } + Data &operator=(const Data &rhs) { + if (this != &rhs) { + delete [] buf; + buf = new char[rhs.len]; + len = rhs.len; + memcpy(buf, rhs.buf, len); + } + return *this; + } + bool check(uint32_t l, char c) { + if (l != len) { + fprintf(stderr, "blob length was %u, expected %u\n", len, l); + return false; + } + for (uint32_t i = 0; i < l; ++i) { + if (buf[i] != c) { + fprintf(stderr, "byte at offset %u was %c, expected %c\n", i, buf[i], c); + return false; + } + } + return true; + } + ~Data() { + delete [] buf; + } +}; + +struct DataSet +{ + std::vector blobs; + + void sample(FRT_Values &v) { + blobs.push_back(Data(v.GetNumValues(), 'V')); + for (uint32_t i = 0; i < v.GetNumValues(); ++i) { + if (v.GetType(i) == FRT_VALUE_DATA) { + blobs.push_back(Data(1, 'x')); + blobs.push_back(Data(v[i]._data._buf, v[i]._data._len)); + } else if (v.GetType(i) == FRT_VALUE_DATA_ARRAY) { + blobs.push_back(Data(v[i]._data_array._len, 'X')); + for (uint32_t j = 0; j < v[i]._data_array._len; ++j) { + blobs.push_back(Data(v[i]._data_array._pt[j]._buf, + v[i]._data_array._pt[j]._len)); + } + } + } + } +}; + +struct ServerSampler : public FRT_Invokable +{ + DataSet &dataSet; + FRT_RPCRequest *clientReq; + FRT_RPCRequest *serverReq; + + ServerSampler(DataSet &ds, FRT_RPCRequest *cr) : dataSet(ds), clientReq(cr), serverReq(0) {} + + void RPC_test(FRT_RPCRequest *req) + { + if (clientReq != 0) { + dataSet.sample(*clientReq->GetParams()); // client params after drop + } + + // store away parameters + FNET_DataBuffer buf; + buf.EnsureFree(req->GetParams()->GetLength()); + req->GetParams()->EncodeCopy(&buf); + + dataSet.sample(*req->GetParams()); // server params before drop + req->DiscardBlobs(); + dataSet.sample(*req->GetParams()); // server params after drop + + // restore parameters into return values + req->GetReturn()->DecodeCopy(&buf, buf.GetDataLen()); + + dataSet.sample(*req->GetReturn()); // server return before drop + + // keep request to sample return after drop + req->AddRef(); + serverReq = req; + } +}; + +TEST("testExplicitShared") { + FRT_Supervisor orb; + MyBlob blob; + + FRT_RPCRequest *req = orb.AllocRPCRequest(); + EXPECT_TRUE(blob.refcnt == 1); + + req->GetParams()->AddSharedData(&blob); + req->GetParams()->AddInt32(42); + req->GetParams()->AddSharedData(&blob); + req->GetParams()->AddInt32(84); + req->GetParams()->AddSharedData(&blob); + + EXPECT_TRUE(blob.refcnt == 4); + EXPECT_TRUE(strcmp(req->GetParamSpec(), "xixix") == 0); + EXPECT_TRUE(req->GetParams()->GetValue(0)._data._len == blob.getLen()); + EXPECT_TRUE(req->GetParams()->GetValue(0)._data._buf == blob.getData()); + EXPECT_TRUE(req->GetParams()->GetValue(1)._intval32 == 42); + EXPECT_TRUE(req->GetParams()->GetValue(2)._data._len == blob.getLen()); + EXPECT_TRUE(req->GetParams()->GetValue(2)._data._buf == blob.getData()); + EXPECT_TRUE(req->GetParams()->GetValue(3)._intval32 == 84); + EXPECT_TRUE(req->GetParams()->GetValue(4)._data._len == blob.getLen()); + EXPECT_TRUE(req->GetParams()->GetValue(4)._data._buf == blob.getData()); + + req->CreateRequestPacket(true)->Free(); // fake request send. + + EXPECT_TRUE(blob.refcnt == 1); + EXPECT_TRUE(strcmp(req->GetParamSpec(), "xixix") == 0); + EXPECT_TRUE(req->GetParams()->GetValue(0)._data._len == 0); + EXPECT_TRUE(req->GetParams()->GetValue(0)._data._buf == NULL); + EXPECT_TRUE(req->GetParams()->GetValue(1)._intval32 == 42); + EXPECT_TRUE(req->GetParams()->GetValue(2)._data._len == 0); + EXPECT_TRUE(req->GetParams()->GetValue(2)._data._buf == NULL); + EXPECT_TRUE(req->GetParams()->GetValue(3)._intval32 == 84); + EXPECT_TRUE(req->GetParams()->GetValue(4)._data._len == 0); + EXPECT_TRUE(req->GetParams()->GetValue(4)._data._buf == NULL); + + req = orb.AllocRPCRequest(req); + + req->GetParams()->AddSharedData(&blob); + req->GetParams()->AddInt32(42); + req->GetParams()->AddSharedData(&blob); + req->GetParams()->AddInt32(84); + req->GetParams()->AddSharedData(&blob); + + EXPECT_TRUE(blob.refcnt == 4); + req->SubRef(); + EXPECT_TRUE(blob.refcnt == 1); +} + +TEST("testImplicitShared") { + DataSet dataSet; + FRT_Supervisor orb; + FRT_RPCRequest *req = orb.AllocRPCRequest(); + ServerSampler serverSampler(dataSet, req); + { + FRT_ReflectionBuilder rb(&orb); + rb.DefineMethod("test", "*", "*", true, + FRT_METHOD(ServerSampler::RPC_test), &serverSampler); + } + orb.Listen(0); + int port = orb.GetListenPort(); + ASSERT_TRUE(port != 0); + orb.Start(); + + char tmp[64]; + snprintf(tmp, sizeof(tmp), "tcp/localhost:%d", port); + FRT_Target *target = orb.GetTarget(tmp); + req->SetMethodName("test"); + { + Data data(Data::SMALL, 'a'); + req->GetParams()->AddData(data.buf, data.len); + } + { + Data data(Data::LARGE, 'b'); + req->GetParams()->AddData(data.buf, data.len); + } + { + char *data = req->GetParams()->AddData(Data::LARGE); + memset(data, 'c', Data::LARGE); + } + { + Data data1(Data::SMALL, 'd'); + Data data2(Data::LARGE, 'e'); + FRT_DataValue *arr = req->GetParams()->AddDataArray(2); + req->GetParams()->SetData(&arr[0], data1.buf, data1.len); + req->GetParams()->SetData(&arr[1], data2.buf, data2.len); + } + + dataSet.sample(*req->GetParams()); // client params before drop + + target->InvokeSync(req, 30.0); + + if (serverSampler.serverReq != 0) { + dataSet.sample(*serverSampler.serverReq->GetReturn()); // server return after drop + } + dataSet.sample(*req->GetReturn()); // client return before drop + + req->DiscardBlobs(); + + dataSet.sample(*req->GetReturn()); // client return after drop + + // verify blob samples + EXPECT_EQUAL(dataSet.blobs.size(), 80u); + + for (int i = 0; i < 80; i += 20) { + // before discard (client params, server params, server return, client return) + EXPECT_TRUE(dataSet.blobs[i + 0].check(4, 'V')); + EXPECT_TRUE(dataSet.blobs[i + 1].check(1, 'x')); + EXPECT_TRUE(dataSet.blobs[i + 2].check(Data::SMALL, 'a')); + EXPECT_TRUE(dataSet.blobs[i + 3].check(1, 'x')); + EXPECT_TRUE(dataSet.blobs[i + 4].check(Data::LARGE, 'b')); + EXPECT_TRUE(dataSet.blobs[i + 5].check(1, 'x')); + EXPECT_TRUE(dataSet.blobs[i + 6].check(Data::LARGE, 'c')); + EXPECT_TRUE(dataSet.blobs[i + 7].check(2, 'X')); + EXPECT_TRUE(dataSet.blobs[i + 8].check(Data::SMALL, 'd')); + EXPECT_TRUE(dataSet.blobs[i + 9].check(Data::LARGE, 'e')); + + // after discard (client params, server params, server return, client return) + EXPECT_TRUE(dataSet.blobs[i + 10].check(4, 'V')); + EXPECT_TRUE(dataSet.blobs[i + 11].check(1, 'x')); + EXPECT_TRUE(dataSet.blobs[i + 12].check(Data::SMALL, 'a')); + EXPECT_TRUE(dataSet.blobs[i + 13].check(1, 'x')); + EXPECT_TRUE(dataSet.blobs[i + 14].check(0, 0)); + EXPECT_TRUE(dataSet.blobs[i + 15].check(1, 'x')); + EXPECT_TRUE(dataSet.blobs[i + 16].check(0, 0)); + EXPECT_TRUE(dataSet.blobs[i + 17].check(2, 'X')); + EXPECT_TRUE(dataSet.blobs[i + 18].check(Data::SMALL, 'd')); + EXPECT_TRUE(dataSet.blobs[i + 19].check(0, 0)); + } + + if (serverSampler.serverReq != 0) { + serverSampler.serverReq->SubRef(); + } + req->SubRef(); + target->SubRef(); + orb.ShutDown(true); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/frt/values/.gitignore b/fnet/src/tests/frt/values/.gitignore new file mode 100644 index 00000000000..509dbc76bff --- /dev/null +++ b/fnet/src/tests/frt/values/.gitignore @@ -0,0 +1 @@ +fnet_values_test_app diff --git a/fnet/src/tests/frt/values/CMakeLists.txt b/fnet/src/tests/frt/values/CMakeLists.txt new file mode 100644 index 00000000000..f1a851a09b1 --- /dev/null +++ b/fnet/src/tests/frt/values/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_values_test_app + SOURCES + values_test.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_values_test_app COMMAND fnet_values_test_app) diff --git a/fnet/src/tests/frt/values/FILES b/fnet/src/tests/frt/values/FILES new file mode 100644 index 00000000000..8450fbfdf7e --- /dev/null +++ b/fnet/src/tests/frt/values/FILES @@ -0,0 +1 @@ +values_test.cpp diff --git a/fnet/src/tests/frt/values/values_test.cpp b/fnet/src/tests/frt/values/values_test.cpp new file mode 100644 index 00000000000..01c43d8207d --- /dev/null +++ b/fnet/src/tests/frt/values/values_test.cpp @@ -0,0 +1,207 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + +uint8_t int8_arr[3] = { 1, 2, 3 }; +uint16_t int16_arr[3] = { 2, 4, 6 }; +uint32_t int32_arr[3] = { 4, 8, 12 }; +uint64_t int64_arr[3] = { 8, 16, 24 }; +float float_arr[3] = { 0.5, 1.0, 1.5 }; +double double_arr[3] = { 0.25, 0.50, 0.75 }; + +template +void arr_cpy(T *dst, const T* src, size_t len) { + for (size_t i = 0; i < len; ++i) { + dst[i] = src[i]; + } +} + +void fillValues(FRT_Values &values) { + { + values.AddInt8(int8_arr[0]); + arr_cpy(values.AddInt8Array(3), int8_arr, 3); + values.AddInt8Array(int8_arr, 3); + values.AddInt8ArrayRef(int8_arr, 3); + } + { + values.AddInt16(int16_arr[0]); + arr_cpy(values.AddInt16Array(3), int16_arr, 3); + values.AddInt16Array(int16_arr, 3); + values.AddInt16ArrayRef(int16_arr, 3); + } + { + values.AddInt32(int32_arr[0]); + arr_cpy(values.AddInt32Array(3), int32_arr, 3); + values.AddInt32Array(int32_arr, 3); + values.AddInt32ArrayRef(int32_arr, 3); + } + { + values.AddInt64(int64_arr[0]); + arr_cpy(values.AddInt64Array(3), int64_arr, 3); + values.AddInt64Array(int64_arr, 3); + values.AddInt64ArrayRef(int64_arr, 3); + } + { + values.AddFloat(float_arr[0]); + arr_cpy(values.AddFloatArray(3), float_arr, 3); + values.AddFloatArray(float_arr, 3); + values.AddFloatArrayRef(float_arr, 3); + } + { + values.AddDouble(double_arr[0]); + arr_cpy(values.AddDoubleArray(3), double_arr, 3); + values.AddDoubleArray(double_arr, 3); + values.AddDoubleArrayRef(double_arr, 3); + } + { + values.AddString("foo"); + values.AddString("bar", 3); + strcpy(values.AddString(3), "baz"); + FRT_StringValue *str_arr = values.AddStringArray(3); + values.SetString(str_arr, "foo"); + values.SetString(str_arr + 1, "bar"); + values.SetString(str_arr + 2, "baz", 3); + } + { + values.AddData("foo", 3); + strncpy(values.AddData(3), "bar", 3); + FRT_DataValue *data_arr = values.AddDataArray(3); + values.SetData(data_arr, "foo", 3); + values.SetData(data_arr + 1, "bar", 3); + values.SetData(data_arr + 2, "baz", 3); + } +} + +void checkValues(FRT_Values &values) { + ASSERT_EQUAL(31u, values.GetNumValues()); + ASSERT_EQUAL(std::string("bBBBhHHHiIIIlLLLfFFFdDDDsssSxxX"), values.GetTypeString()); + size_t idx = 0; + EXPECT_EQUAL(int8_arr[0], values[idx++]._intval8); + for (size_t i = 0; i < 3; ++i, ++idx) { + ASSERT_EQUAL(3u, values[idx]._int8_array._len); + for (size_t j = 0; j < 3; ++j) { + EXPECT_EQUAL(int8_arr[j], values[idx]._int8_array._pt[j]); + } + } + EXPECT_EQUAL(int16_arr[0], values[idx++]._intval16); + for (size_t i = 0; i < 3; ++i, ++idx) { + ASSERT_EQUAL(3u, values[idx]._int16_array._len); + for (size_t j = 0; j < 3; ++j) { + EXPECT_EQUAL(int16_arr[j], values[idx]._int16_array._pt[j]); + } + } + EXPECT_EQUAL(int32_arr[0], values[idx++]._intval32); + for (size_t i = 0; i < 3; ++i, ++idx) { + ASSERT_EQUAL(3u, values[idx]._int32_array._len); + for (size_t j = 0; j < 3; ++j) { + EXPECT_EQUAL(int32_arr[j], values[idx]._int32_array._pt[j]); + } + } + EXPECT_EQUAL(int64_arr[0], values[idx++]._intval64); + for (size_t i = 0; i < 3; ++i, ++idx) { + ASSERT_EQUAL(3u, values[idx]._int64_array._len); + for (size_t j = 0; j < 3; ++j) { + EXPECT_EQUAL(int64_arr[j], values[idx]._int64_array._pt[j]); + } + } + EXPECT_EQUAL(float_arr[0], values[idx++]._float); + for (size_t i = 0; i < 3; ++i, ++idx) { + ASSERT_EQUAL(3u, values[idx]._float_array._len); + for (size_t j = 0; j < 3; ++j) { + EXPECT_EQUAL(float_arr[j], values[idx]._float_array._pt[j]); + } + } + EXPECT_EQUAL(double_arr[0], values[idx++]._double); + for (size_t i = 0; i < 3; ++i, ++idx) { + ASSERT_EQUAL(3u, values[idx]._double_array._len); + for (size_t j = 0; j < 3; ++j) { + EXPECT_EQUAL(double_arr[j], values[idx]._double_array._pt[j]); + } + } + EXPECT_EQUAL(std::string("foo"), std::string(values[idx]._string._str, + values[idx]._string._len)); + ++idx; + EXPECT_EQUAL(std::string("bar"), std::string(values[idx]._string._str, + values[idx]._string._len)); + ++idx; + EXPECT_EQUAL(std::string("baz"), std::string(values[idx]._string._str, + values[idx]._string._len)); + ++idx; + ASSERT_EQUAL(3u, values[idx]._string_array._len); + EXPECT_EQUAL(std::string("foo"), std::string(values[idx]._string_array._pt[0]._str, + values[idx]._string_array._pt[0]._len)); + EXPECT_EQUAL(std::string("bar"), std::string(values[idx]._string_array._pt[1]._str, + values[idx]._string_array._pt[1]._len)); + EXPECT_EQUAL(std::string("baz"), std::string(values[idx]._string_array._pt[2]._str, + values[idx]._string_array._pt[2]._len)); + ++idx; + EXPECT_EQUAL(std::string("foo"), std::string(values[idx]._data._buf, + values[idx]._data._len)); + ++idx; + EXPECT_EQUAL(std::string("bar"), std::string(values[idx]._data._buf, + values[idx]._data._len)); + ++idx; + ASSERT_EQUAL(3u, values[idx]._data_array._len); + EXPECT_EQUAL(std::string("foo"), std::string(values[idx]._data_array._pt[0]._buf, + values[idx]._data_array._pt[0]._len)); + EXPECT_EQUAL(std::string("bar"), std::string(values[idx]._data_array._pt[1]._buf, + values[idx]._data_array._pt[1]._len)); + EXPECT_EQUAL(std::string("baz"), std::string(values[idx]._data_array._pt[2]._buf, + values[idx]._data_array._pt[2]._len)); + ++idx; + EXPECT_EQUAL(31u, idx); +} + +void checkValues(FRT_Values &v1, FRT_Values &v2) { + checkValues(v1); + checkValues(v2); + EXPECT_TRUE(v1.Equals(&v2)); + EXPECT_TRUE(v2.Equals(&v1)); +} + +TEST_FF("set and get", FRT_MemoryTub(), FRT_Values(&f1)) { + fillValues(f2); + checkValues(f2); +} + +TEST_FFFF("encode/decode big endian", FRT_MemoryTub(), FRT_Values(&f1), + FNET_DataBuffer(), FRT_Values(&f1)) +{ + fillValues(f2); + f2.EncodeBig(&f3); + EXPECT_EQUAL(f2.GetLength(), f3.GetDataLen()); + EXPECT_TRUE(f4.DecodeBig(&f3, f3.GetDataLen())); + checkValues(f2, f4); +} + +TEST_FFFF("encode/decode host endian", FRT_MemoryTub(), FRT_Values(&f1), + FNET_DataBuffer(), FRT_Values(&f1)) +{ + fillValues(f2); + f2.EncodeCopy(&f3); + EXPECT_EQUAL(f2.GetLength(), f3.GetDataLen()); + EXPECT_TRUE(f4.DecodeCopy(&f3, f3.GetDataLen())); + checkValues(f2, f4); +} + +TEST_FFFF("decode little if host is little", FRT_MemoryTub(), FRT_Values(&f1), + FNET_DataBuffer(), FRT_Values(&f1)) +{ + if (FNET_Info::GetEndian() == FNET_Info::ENDIAN_LITTLE) { + fprintf(stderr, "little endian detected...\n"); + fillValues(f2); + f2.EncodeCopy(&f3); + EXPECT_EQUAL(f2.GetLength(), f3.GetDataLen()); + EXPECT_TRUE(f4.DecodeLittle(&f3, f3.GetDataLen())); + checkValues(f2, f4); + } else { + fprintf(stderr, "host is not little endian, coverage will suffer...\n"); + } +} + +TEST_FF("print values", FRT_MemoryTub(), FRT_Values(&f1)) { + fillValues(f2); + f2.Print(); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/info/.gitignore b/fnet/src/tests/info/.gitignore new file mode 100644 index 00000000000..bd20557beb5 --- /dev/null +++ b/fnet/src/tests/info/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +info_test +fnet_info_test_app diff --git a/fnet/src/tests/info/CMakeLists.txt b/fnet/src/tests/info/CMakeLists.txt new file mode 100644 index 00000000000..fb6069e5f8a --- /dev/null +++ b/fnet/src/tests/info/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_info_test_app + SOURCES + info.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_info_test_app COMMAND fnet_info_test_app) diff --git a/fnet/src/tests/info/DESC b/fnet/src/tests/info/DESC new file mode 100644 index 00000000000..4e7cd423af6 --- /dev/null +++ b/fnet/src/tests/info/DESC @@ -0,0 +1 @@ +Dummy test used to print out some general info about FNET. diff --git a/fnet/src/tests/info/FILES b/fnet/src/tests/info/FILES new file mode 100644 index 00000000000..62e5403ecb5 --- /dev/null +++ b/fnet/src/tests/info/FILES @@ -0,0 +1 @@ +info.cpp diff --git a/fnet/src/tests/info/info.cpp b/fnet/src/tests/info/info.cpp new file mode 100644 index 00000000000..284be22db63 --- /dev/null +++ b/fnet/src/tests/info/info.cpp @@ -0,0 +1,89 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include +#include + +struct RPC : public FRT_Invokable +{ + void GetInfo(FRT_RPCRequest *req) + { + req->GetReturn()->AddString("fastos X current"); + req->GetReturn()->AddString(FNET_Info::GetFNETVersion()); + const char *endian_str = "UNKNOWN"; + if (FNET_Info::GetEndian() == FNET_Info::ENDIAN_LITTLE) + endian_str = "LITTLE"; + if (FNET_Info::GetEndian() == FNET_Info::ENDIAN_BIG) + endian_str = "BIG"; + req->GetReturn()->AddString(endian_str); + req->GetReturn()->AddInt32(FD_SETSIZE); + req->GetReturn()->AddInt32(sizeof(FRT_RPCRequest)); + } + + void Init(FRT_Supervisor *s) + { + FRT_ReflectionBuilder rb(s); + //------------------------------------------------------------------- + rb.DefineMethod("getInfo", "", "sssii", true, + FRT_METHOD(RPC::GetInfo), this); + // FastOS version + // FNET version + // endian + // FD_SETSIZE + // req object size + //------------------------------------------------------------------- + } +}; + +TEST("info") { + RPC rpc; + FRT_Supervisor orb; + char spec[64]; + rpc.Init(&orb); + ASSERT_TRUE(orb.Listen("tcp/0")); + sprintf(spec, "tcp/localhost:%d", orb.GetListenPort()); + ASSERT_TRUE(orb.Start()); + + FRT_Target *target = orb.GetTarget(spec); + FRT_RPCRequest *local_info = orb.AllocRPCRequest(); + FRT_RPCRequest *remote_info = orb.AllocRPCRequest(); + + rpc.GetInfo(local_info); + remote_info->SetMethodName("getInfo"); + target->InvokeSync(remote_info, 10.0); + EXPECT_FALSE(remote_info->IsError()); + + FRT_Values &l = *local_info->GetReturn(); + // FRT_Values &r = *remote_info->GetReturn(); + + fprintf(stderr, "FastOS Version: %s\n", l[0]._string._str); + fprintf(stderr, "FNET Version: %s\n", l[1]._string._str); + fprintf(stderr, "Endian: %s\n", l[2]._string._str); + fprintf(stderr, "FD_SETSIZE: %d\n", l[3]._intval32); + fprintf(stderr, "sizeof(FRT_RPCRequest): %d\n", l[4]._intval32); + + target->SubRef(); + local_info->SubRef(); + remote_info->SubRef(); + orb.ShutDown(true); +}; + +TEST("size of important objects") +{ + EXPECT_EQUAL(184u, sizeof(FNET_IOComponent)); + EXPECT_EQUAL(32u, sizeof(FNET_Channel)); + EXPECT_EQUAL(40u, sizeof(FNET_PacketQueue_NoLock)); + EXPECT_EQUAL(512u, sizeof(FNET_Connection)); + EXPECT_EQUAL(96u, sizeof(FNET_Cond)); + EXPECT_EQUAL(48u, sizeof(FNET_DataBuffer)); + EXPECT_EQUAL(24u, sizeof(FastOS_Time)); + EXPECT_EQUAL(8u, sizeof(FNET_Context)); + EXPECT_EQUAL(8u, sizeof(fastos::TimeStamp)); + EXPECT_EQUAL(48u, sizeof(FastOS_Mutex)); + EXPECT_EQUAL(40u, sizeof(pthread_mutex_t)); + EXPECT_EQUAL(48u, sizeof(pthread_cond_t)); + EXPECT_EQUAL(40u, sizeof(std::mutex)); + EXPECT_EQUAL(48u, sizeof(std::condition_variable)); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/locking/.gitignore b/fnet/src/tests/locking/.gitignore new file mode 100644 index 00000000000..dc9c395ba5b --- /dev/null +++ b/fnet/src/tests/locking/.gitignore @@ -0,0 +1,8 @@ +.depend +Makefile +castspeed_test +drainpackets_test +lockspeed_test +fnet_castspeed_test_app +fnet_drainpackets_test_app +fnet_lockspeed_test_app diff --git a/fnet/src/tests/locking/CMakeLists.txt b/fnet/src/tests/locking/CMakeLists.txt new file mode 100644 index 00000000000..7a0187717e6 --- /dev/null +++ b/fnet/src/tests/locking/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_drainpackets_test_app + SOURCES + drainpackets.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_drainpackets_test_app NO_VALGRIND COMMAND fnet_drainpackets_test_app) +vespa_add_executable(fnet_lockspeed_test_app + SOURCES + lockspeed.cpp + dummy.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_lockspeed_test_app NO_VALGRIND COMMAND fnet_lockspeed_test_app) +vespa_add_executable(fnet_castspeed_test_app + SOURCES + castspeed.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_castspeed_test_app NO_VALGRIND COMMAND fnet_castspeed_test_app) diff --git a/fnet/src/tests/locking/DESC b/fnet/src/tests/locking/DESC new file mode 100644 index 00000000000..86c035e5a09 --- /dev/null +++ b/fnet/src/tests/locking/DESC @@ -0,0 +1 @@ +Benchmark locking and some queue locking strategies. diff --git a/fnet/src/tests/locking/FILES b/fnet/src/tests/locking/FILES new file mode 100644 index 00000000000..dfa689e7256 --- /dev/null +++ b/fnet/src/tests/locking/FILES @@ -0,0 +1,2 @@ +lockspeed.cpp +drainpackets.cpp diff --git a/fnet/src/tests/locking/castspeed.cpp b/fnet/src/tests/locking/castspeed.cpp new file mode 100644 index 00000000000..874769a7579 --- /dev/null +++ b/fnet/src/tests/locking/castspeed.cpp @@ -0,0 +1,219 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + +class B; + +static int taken = 0; +extern void takeB(B* foo) __attribute__((noinline)); + +class A +{ +public: + virtual B* asB() { return 0; } + virtual ~A() {} +}; + +class C: public A +{ +public: + B *otherB; + virtual B* asB() { return otherB; } + C() : otherB(NULL) {} +}; + +class B: public C +{ +public: + virtual B* asB() { return this; } +}; + + +class CastTest +{ + A* myB; + B* realB; +public: + B* DummyCast() { + return realB; + } + B* DynamicCast() { + return dynamic_cast(myB); + } + B* TypesafeCast() { + return myB->asB(); + } + B* UnsafeCast() { + return reinterpret_cast(myB); + } + B* StaticCast() { + return static_cast(myB); + } + + CastTest() { + myB = realB = new B; + } + + ~CastTest() { + delete myB; + } +}; + +#define LOOPCNT 30000000 + +TEST("cast speed") { + FastOS_Time start; + FastOS_Time stop; + + CastTest casttest; + + double actualTime; + uint32_t i; + + taken = 0; + start.SetNow(); + for (i = 0; i < LOOPCNT; i++) { + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + } + stop.SetNow(); + stop -= start; + actualTime = stop.MilliSecs(); + fprintf(stderr, + "%d dummy cast calls: %f ms (%1.2f/us) [%f]\n", + taken, stop.MilliSecs(), + 0.001 * taken / stop.MilliSecs(), + actualTime); + + taken = 0; + start.SetNow(); + for (i = 0; i < LOOPCNT; i++) { + takeB(casttest.DynamicCast()); + takeB(casttest.DynamicCast()); + takeB(casttest.DynamicCast()); + takeB(casttest.DynamicCast()); + takeB(casttest.DynamicCast()); + takeB(casttest.DynamicCast()); + takeB(casttest.DynamicCast()); + takeB(casttest.DynamicCast()); + takeB(casttest.DynamicCast()); + takeB(casttest.DynamicCast()); + } + stop.SetNow(); + stop -= start; + actualTime = stop.MilliSecs(); + fprintf(stderr, + "%d dynamic cast calls: %f ms (%1.2f/us) [%f]\n", + taken, stop.MilliSecs(), + 0.001 * taken / stop.MilliSecs(), + actualTime); + + taken = 0; + start.SetNow(); + for (i = 0; i < LOOPCNT; i++) { + takeB(casttest.TypesafeCast()); + takeB(casttest.TypesafeCast()); + takeB(casttest.TypesafeCast()); + takeB(casttest.TypesafeCast()); + takeB(casttest.TypesafeCast()); + takeB(casttest.TypesafeCast()); + takeB(casttest.TypesafeCast()); + takeB(casttest.TypesafeCast()); + takeB(casttest.TypesafeCast()); + takeB(casttest.TypesafeCast()); + } + stop.SetNow(); + stop -= start; + actualTime = stop.MilliSecs(); + fprintf(stderr, + "%d typesafe cast calls: %f ms (%1.2f/us) [%f]\n", + taken, stop.MilliSecs(), + 0.001 * taken / stop.MilliSecs(), + actualTime); + + taken = 0; + start.SetNow(); + for (i = 0; i < LOOPCNT; i++) { + takeB(casttest.StaticCast()); + takeB(casttest.StaticCast()); + takeB(casttest.StaticCast()); + takeB(casttest.StaticCast()); + takeB(casttest.StaticCast()); + takeB(casttest.StaticCast()); + takeB(casttest.StaticCast()); + takeB(casttest.StaticCast()); + takeB(casttest.StaticCast()); + takeB(casttest.StaticCast()); + } + stop.SetNow(); + stop -= start; + actualTime = stop.MilliSecs(); + fprintf(stderr, + "%d static cast calls: %f ms (%1.2f/us) [%f]\n", + taken, stop.MilliSecs(), + 0.001 * taken / stop.MilliSecs(), + actualTime); + + taken = 0; + start.SetNow(); + for (i = 0; i < LOOPCNT; i++) { + takeB(casttest.UnsafeCast()); + takeB(casttest.UnsafeCast()); + takeB(casttest.UnsafeCast()); + takeB(casttest.UnsafeCast()); + takeB(casttest.UnsafeCast()); + takeB(casttest.UnsafeCast()); + takeB(casttest.UnsafeCast()); + takeB(casttest.UnsafeCast()); + takeB(casttest.UnsafeCast()); + takeB(casttest.UnsafeCast()); + } + stop.SetNow(); + stop -= start; + actualTime = stop.MilliSecs(); + fprintf(stderr, + "%d reinterpret_cast calls: %f ms (%1.2f/us) [%f]\n", + taken, stop.MilliSecs(), + 0.001 * taken / stop.MilliSecs(), + actualTime); + + taken = 0; + start.SetNow(); + for (i = 0; i < LOOPCNT; i++) { + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + takeB(casttest.DummyCast()); + } + stop.SetNow(); + stop -= start; + actualTime = stop.MilliSecs(); + fprintf(stderr, + "%d dummy cast calls: %f ms (%1.2f/us) [%f]\n", + taken, stop.MilliSecs(), + 0.001 * taken / stop.MilliSecs(), + actualTime); +} + +void takeB(B* foo) +{ + if (foo != 0) { + taken++; + } +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/locking/drainpackets.cpp b/fnet/src/tests/locking/drainpackets.cpp new file mode 100644 index 00000000000..066923f0c70 --- /dev/null +++ b/fnet/src/tests/locking/drainpackets.cpp @@ -0,0 +1,134 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + + +class MyPacket : public FNET_Packet +{ +public: + uint32_t GetPCODE() { return 0; } + uint32_t GetLength() { return 0; } + void Encode(FNET_DataBuffer *) {} + bool Decode(FNET_DataBuffer *, uint32_t) + { return true; } +}; + + +TEST("drain packets") { + FastOS_Time start; + FastOS_Time stop; + + FNET_Mutex lock; + + FNET_PacketQueue q1(512); + FNET_PacketQueue q2(512); + FNET_PacketQueue q3(512); + + int i; + + // create dummy packets + + for (i = 0; i < 500; i++) { + q1.QueuePacket_NoLock(new MyPacket(), FNET_Context()); + } + + // drain packets directly with single lock interval + + start.SetNow(); + + for (i = 0; i < 10000; i++) { + + FNET_Packet *packet; + FNET_Context context; + + lock.Lock(); + + while (!q1.IsEmpty_NoLock()) { + packet = q1.DequeuePacket_NoLock(&context); + q3.QueuePacket_NoLock(packet, context); + } + + lock.Unlock(); + + //------------------------ + + lock.Lock(); + + while (!q3.IsEmpty_NoLock()) { + packet = q3.DequeuePacket_NoLock(&context); + q1.QueuePacket_NoLock(packet, context); + } + + lock.Unlock(); + } + + stop.SetNow(); + stop -= start; + fprintf(stderr, "direct, single lock interval (10M packets): %1.2f ms\n", + stop.MilliSecs()); + + // flush packets, then move without lock + + start.SetNow(); + + for (i = 0; i < 10000; i++) { + + FNET_Packet *packet; + FNET_Context context; + + lock.Lock(); + q1.FlushPackets_NoLock(&q2); + lock.Unlock(); + + while (!q2.IsEmpty_NoLock()) { + packet = q2.DequeuePacket_NoLock(&context); + q3.QueuePacket_NoLock(packet, context); + } + + //------------------------ + + lock.Lock(); + q3.FlushPackets_NoLock(&q2); + lock.Unlock(); + + while (!q2.IsEmpty_NoLock()) { + packet = q2.DequeuePacket_NoLock(&context); + q1.QueuePacket_NoLock(packet, context); + } + } + + stop.SetNow(); + stop -= start; + fprintf(stderr, "indirect (10M packets): %1.2f ms\n", stop.MilliSecs()); + + // drain packets directly with multiple lock intervals + + start.SetNow(); + + for (i = 0; i < 10000; i++) { + + FNET_Packet *packet; + FNET_Context context; + + while ((packet = q1.DequeuePacket(0, &context)) != NULL) { + q3.QueuePacket_NoLock(packet, context); + } + + //------------------------ + + while ((packet = q3.DequeuePacket(0, &context)) != NULL) { + q1.QueuePacket_NoLock(packet, context); + } + } + + stop.SetNow(); + stop -= start; + fprintf(stderr, "direct, multiple lock intervals (10M packets): %1.2f ms\n", + stop.MilliSecs()); + + EXPECT_TRUE(q1.GetPacketCnt_NoLock() == 500 && + q2.GetPacketCnt_NoLock() == 0 && + q3.GetPacketCnt_NoLock() == 0); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/locking/dummy.cpp b/fnet/src/tests/locking/dummy.cpp new file mode 100644 index 00000000000..bab18ef3db9 --- /dev/null +++ b/fnet/src/tests/locking/dummy.cpp @@ -0,0 +1,9 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include "dummy.h" + +DummyObj::DummyObj() {} +DummyObj::~DummyObj() {} + +void DummyLock::Lock() {} +void DummyLock::Unlock() {} diff --git a/fnet/src/tests/locking/dummy.h b/fnet/src/tests/locking/dummy.h new file mode 100644 index 00000000000..9a3578b43e5 --- /dev/null +++ b/fnet/src/tests/locking/dummy.h @@ -0,0 +1,17 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +class DummyObj +{ +public: + DummyObj(); + ~DummyObj(); +}; + +class DummyLock +{ +public: + void Lock(); + void Unlock(); +}; + diff --git a/fnet/src/tests/locking/lockspeed.cpp b/fnet/src/tests/locking/lockspeed.cpp new file mode 100644 index 00000000000..bccedc61b8e --- /dev/null +++ b/fnet/src/tests/locking/lockspeed.cpp @@ -0,0 +1,177 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include "dummy.h" + +TEST("lock speed") { + FastOS_Time start; + FastOS_Time stop; + DummyLock dummy; + FNET_Mutex lock; + double dummyTime; + double actualTime; + double overhead; + uint32_t i; + + start.SetNow(); + for (i = 0; i < 1000000; i++) { + dummy.Lock(); + dummy.Unlock(); + dummy.Lock(); + dummy.Unlock(); + dummy.Lock(); + dummy.Unlock(); + dummy.Lock(); + dummy.Unlock(); + dummy.Lock(); + dummy.Unlock(); + dummy.Lock(); + dummy.Unlock(); + dummy.Lock(); + dummy.Unlock(); + dummy.Lock(); + dummy.Unlock(); + dummy.Lock(); + dummy.Unlock(); + dummy.Lock(); + dummy.Unlock(); + } + stop.SetNow(); + stop -= start; + dummyTime = stop.MilliSecs(); + + fprintf(stderr, + "10M dummy lock/unlock: %f ms (%1.2f/ms)\n", + dummyTime, 10000000.0 / dummyTime); + + start.SetNow(); + for (i = 0; i < 1000000; i++) { + lock.Lock(); + lock.Unlock(); + lock.Lock(); + lock.Unlock(); + lock.Lock(); + lock.Unlock(); + lock.Lock(); + lock.Unlock(); + lock.Lock(); + lock.Unlock(); + lock.Lock(); + lock.Unlock(); + lock.Lock(); + lock.Unlock(); + lock.Lock(); + lock.Unlock(); + lock.Lock(); + lock.Unlock(); + lock.Lock(); + lock.Unlock(); + } + stop.SetNow(); + stop -= start; + actualTime = stop.MilliSecs(); + + fprintf(stderr, + "10M actual lock/unlock: %f ms (%1.2f/ms)\n", + stop.MilliSecs(), 10000000.0 / stop.MilliSecs()); + + overhead = (actualTime - dummyTime) / 10000.0; + + fprintf(stderr, + "approx overhead per lock/unlock: %f microseconds\n", + overhead); + + //--------------------------------------------------------------------------- + + start.SetNow(); + for (i = 0; i < 1000000; i++) { + FNET_Mutex lock0; + FNET_Mutex lock1; + FNET_Mutex lock2; + FNET_Mutex lock3; + FNET_Mutex lock4; + FNET_Mutex lock5; + FNET_Mutex lock6; + FNET_Mutex lock7; + FNET_Mutex lock8; + FNET_Mutex lock9; + } + stop.SetNow(); + stop -= start; + fprintf(stderr, "10M mutex create/destroy %f ms (%1.2f/ms)\n", + stop.MilliSecs(), 10000000.0 / stop.MilliSecs()); + + //--------------------------------------------------------------------------- + + start.SetNow(); + for (i = 0; i < 1000000; i++) { + FNET_Cond cond0; + FNET_Cond cond1; + FNET_Cond cond2; + FNET_Cond cond3; + FNET_Cond cond4; + FNET_Cond cond5; + FNET_Cond cond6; + FNET_Cond cond7; + FNET_Cond cond8; + FNET_Cond cond9; + } + stop.SetNow(); + stop -= start; + fprintf(stderr, "10M cond create/destroy %f ms (%1.2f/ms)\n", + stop.MilliSecs(), 10000000.0 / stop.MilliSecs()); + + //--------------------------------------------------------------------------- + + start.SetNow(); + for (i = 0; i < 1000000; i++) { + DummyObj dummy0; + DummyObj dummy1; + DummyObj dummy2; + DummyObj dummy3; + DummyObj dummy4; + DummyObj dummy5; + DummyObj dummy6; + DummyObj dummy7; + DummyObj dummy8; + DummyObj dummy9; + } + stop.SetNow(); + stop -= start; + fprintf(stderr, "10M dummy create/destroy %f ms (%1.2f/ms)\n", + stop.MilliSecs(), 10000000.0 / stop.MilliSecs()); + + //--------------------------------------------------------------------------- + + start.SetNow(); + for (i = 0; i < 1000000; i++) { + DummyObj *dummy0 = new DummyObj(); + DummyObj *dummy1 = new DummyObj(); + DummyObj *dummy2 = new DummyObj(); + DummyObj *dummy3 = new DummyObj(); + DummyObj *dummy4 = new DummyObj(); + DummyObj *dummy5 = new DummyObj(); + DummyObj *dummy6 = new DummyObj(); + DummyObj *dummy7 = new DummyObj(); + DummyObj *dummy8 = new DummyObj(); + DummyObj *dummy9 = new DummyObj(); + delete dummy9; + delete dummy8; + delete dummy7; + delete dummy6; + delete dummy5; + delete dummy4; + delete dummy3; + delete dummy2; + delete dummy1; + delete dummy0; + } + stop.SetNow(); + stop -= start; + fprintf(stderr, "10M dummy new/delete %f ms (%1.2f/ms)\n", + stop.MilliSecs(), 10000000.0 / stop.MilliSecs()); + + //--------------------------------------------------------------------------- +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/printstuff/.gitignore b/fnet/src/tests/printstuff/.gitignore new file mode 100644 index 00000000000..bfb8f2d1754 --- /dev/null +++ b/fnet/src/tests/printstuff/.gitignore @@ -0,0 +1 @@ +fnet_printstuff_test_app diff --git a/fnet/src/tests/printstuff/CMakeLists.txt b/fnet/src/tests/printstuff/CMakeLists.txt new file mode 100644 index 00000000000..7180d2866f0 --- /dev/null +++ b/fnet/src/tests/printstuff/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_printstuff_test_app + SOURCES + printstuff_test.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_printstuff_test_app COMMAND fnet_printstuff_test_app) diff --git a/fnet/src/tests/printstuff/FILES b/fnet/src/tests/printstuff/FILES new file mode 100644 index 00000000000..95a889d6494 --- /dev/null +++ b/fnet/src/tests/printstuff/FILES @@ -0,0 +1 @@ +printstuff_test.cpp diff --git a/fnet/src/tests/printstuff/printstuff_test.cpp b/fnet/src/tests/printstuff/printstuff_test.cpp new file mode 100644 index 00000000000..3778cef9c8c --- /dev/null +++ b/fnet/src/tests/printstuff/printstuff_test.cpp @@ -0,0 +1,45 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + +void printError(uint32_t ecode) { + fprintf(stderr, "error(%u): %s: %s\n", + ecode, FRT_GetErrorCodeName(ecode), FRT_GetDefaultErrorMessage(ecode)); +} + +TEST("frt error code names and default messages") { + printError(0); + printError(99); + for (uint32_t i = 100; i < 112; ++i) { + printError(i); + } + printError(198); + printError(199); + printError(200); + printError(70000); +} + +TEST("rpc packets in a queue") { + FRT_RPCRequest *req = new FRT_RPCRequest(); + { + req->SetMethodName("foo"); + FNET_PacketQueue_NoLock q1(1, FNET_IPacketHandler::FNET_KEEP_CHANNEL); + q1.QueuePacket_NoLock(new (req->GetMemoryTub()) FRT_RPCRequestPacket(req, 0, false), FNET_Context()); + q1.QueuePacket_NoLock(new (req->GetMemoryTub()) FRT_RPCReplyPacket(req, 0, false), FNET_Context()); + q1.QueuePacket_NoLock(new (req->GetMemoryTub()) FRT_RPCErrorPacket(req, 0, false), FNET_Context()); + q1.Print(); + FNET_PacketQueue q2(2, FNET_IPacketHandler::FNET_KEEP_CHANNEL); + q2.QueuePacket(new (req->GetMemoryTub()) FRT_RPCRequestPacket(req, 0, false), FNET_Context()); + q2.QueuePacket(new (req->GetMemoryTub()) FRT_RPCReplyPacket(req, 0, false), FNET_Context()); + q2.QueuePacket(new (req->GetMemoryTub()) FRT_RPCErrorPacket(req, 0, false), FNET_Context()); + q2.Print(); + } + req->SubRef(); +} + +TEST("info") { + FNET_Info::PrintInfo(); + FNET_Info::LogInfo(); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/regress/databuffer/.gitignore b/fnet/src/tests/regress/databuffer/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/fdselector/.gitignore b/fnet/src/tests/regress/fdselector/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/frt/memorytub/.gitignore b/fnet/src/tests/regress/frt/memorytub/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/frt/method_pt/.gitignore b/fnet/src/tests/regress/frt/method_pt/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/frt/rpc/.gitignore b/fnet/src/tests/regress/frt/rpc/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/frt/values/.gitignore b/fnet/src/tests/regress/frt/values/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/info/.gitignore b/fnet/src/tests/regress/info/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/locking/.gitignore b/fnet/src/tests/regress/locking/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/scheduling/.gitignore b/fnet/src/tests/regress/scheduling/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/spiral/.gitignore b/fnet/src/tests/regress/spiral/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/sync_execute/.gitignore b/fnet/src/tests/regress/sync_execute/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/thread_id/.gitignore b/fnet/src/tests/regress/thread_id/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/regress/time/.gitignore b/fnet/src/tests/regress/time/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fnet/src/tests/scheduling/.gitignore b/fnet/src/tests/scheduling/.gitignore new file mode 100644 index 00000000000..b6dccb18324 --- /dev/null +++ b/fnet/src/tests/scheduling/.gitignore @@ -0,0 +1,6 @@ +.depend +Makefile +schedule_test +sloweventloop_test +fnet_schedule_test_app +fnet_sloweventloop_test_app diff --git a/fnet/src/tests/scheduling/CMakeLists.txt b/fnet/src/tests/scheduling/CMakeLists.txt new file mode 100644 index 00000000000..244211a7cb6 --- /dev/null +++ b/fnet/src/tests/scheduling/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_schedule_test_app + SOURCES + schedule.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_schedule_test_app COMMAND fnet_schedule_test_app) +vespa_add_executable(fnet_sloweventloop_test_app + SOURCES + sloweventloop.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_sloweventloop_test_app COMMAND fnet_sloweventloop_test_app) diff --git a/fnet/src/tests/scheduling/DESC b/fnet/src/tests/scheduling/DESC new file mode 100644 index 00000000000..6e4ca2972b9 --- /dev/null +++ b/fnet/src/tests/scheduling/DESC @@ -0,0 +1 @@ +Scheduler test. diff --git a/fnet/src/tests/scheduling/FILES b/fnet/src/tests/scheduling/FILES new file mode 100644 index 00000000000..bd5dc7f2572 --- /dev/null +++ b/fnet/src/tests/scheduling/FILES @@ -0,0 +1 @@ +schedule.cpp diff --git a/fnet/src/tests/scheduling/schedule.cpp b/fnet/src/tests/scheduling/schedule.cpp new file mode 100644 index 00000000000..0625b83e121 --- /dev/null +++ b/fnet/src/tests/scheduling/schedule.cpp @@ -0,0 +1,153 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + +FastOS_Time _time; +FNET_Scheduler *_scheduler; + + +class MyTask : public FNET_Task +{ +public: + FastOS_Time _time; + int _target; + bool _done; + + MyTask(int target) + : FNET_Task(::_scheduler), + _time(), + _target(target), + _done(false) {} + + int GetTarget() const { return _target; } + + bool Check() const + { + int a = _target; + int b = (int)_time.MilliSecs(); + + if (!_done) + return false; + + if (b < a) + return false; + + if ((b - a) > (2 * FNET_Scheduler::SLOT_TICK)) + return false; + + return true; + } + + void PerformTask() + { + _time = ::_time; + _done = true; + } +}; + + +class RealTimeTask : public FNET_Task +{ +public: + uint32_t _cnt; + + RealTimeTask() : FNET_Task(::_scheduler), _cnt(0) + { + } + + uint32_t GetCnt() { return _cnt; } + + void PerformTask() + { + _cnt++; + ScheduleNow(); // re-schedule as fast as possible + } +}; + + +TEST("schedule") { + _time.SetMilliSecs(0); + _scheduler = new FNET_Scheduler(&_time, &_time); + + RealTimeTask rt_task1; + RealTimeTask rt_task2; + RealTimeTask rt_task3; + rt_task1.ScheduleNow(); + rt_task2.ScheduleNow(); + rt_task3.ScheduleNow(); + + uint32_t taskCnt = 1000000; + MyTask **tasks = new MyTask*[taskCnt]; + assert(tasks != NULL); + for (uint32_t i = 0; i < taskCnt; i++) { + tasks[i] = new MyTask(rand() & 131071); + assert(tasks[i] != NULL); + } + + FastOS_Time start; + FastOS_Time stop; + + start.SetNow(); + for (uint32_t j = 0; j < taskCnt; j++) { + tasks[j]->Schedule(tasks[j]->GetTarget() / 1000.0); + } + stop.SetNow(); + stop -= start; + double scheduleTime = stop.MilliSecs() / (double)taskCnt; + fprintf(stderr, "scheduling cost: %1.2f microseconds\n", scheduleTime * 1000.0); + + start.SetNow(); + uint32_t tickCnt = 0; + while (_time.MilliSecs() < 135000.0) { + _time.AddMilliSecs(FNET_Scheduler::SLOT_TICK); + _scheduler->CheckTasks(); + tickCnt++; + } + stop.SetNow(); + stop -= start; + double runTime = stop.MilliSecs(); + fprintf(stderr, "3 RT tasks + %d one-shot tasks over 135s\n", taskCnt); + fprintf(stderr, "%1.2f seconds actual run time\n", runTime / 1000.0); + fprintf(stderr, "%1.2f tasks per simulated second\n", (double)taskCnt / (double)135); + fprintf(stderr, "%d ticks\n", tickCnt); + fprintf(stderr, "%1.2f %% simulated CPU usage\n", 100 * (runTime / 135000.0)); + fprintf(stderr, "%1.2f microseconds per performed task\n", + 1000.0 * (runTime / (taskCnt + tickCnt * 3.0))); + + for (uint32_t k = 0; k < taskCnt; k++) { + EXPECT_TRUE(tasks[k]->Check()); + } + EXPECT_TRUE(rt_task1.GetCnt() == tickCnt); + EXPECT_TRUE(rt_task2.GetCnt() == tickCnt); + EXPECT_TRUE(rt_task3.GetCnt() == tickCnt); + + for (uint32_t l = 0; l < taskCnt; l++) { + delete tasks[l]; + } + rt_task1.Kill(); + rt_task2.Kill(); + rt_task3.Kill(); + delete [] tasks; + delete _scheduler; + + { // trigger warning from scheduler destructor + + FNET_Scheduler *s = new FNET_Scheduler(); + + FNET_Task t1(s); + FNET_Task t2(s); + FNET_Task t3(s); + FNET_Task t4(s); + FNET_Task t5(s); + + t1.ScheduleNow(); + t2.Schedule(5.0); + t3.Schedule(5.0); + t4.Schedule(10.0); + t5.Schedule(15.0); + + delete s; + } +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/scheduling/sloweventloop.cpp b/fnet/src/tests/scheduling/sloweventloop.cpp new file mode 100644 index 00000000000..3e27bfef131 --- /dev/null +++ b/fnet/src/tests/scheduling/sloweventloop.cpp @@ -0,0 +1,65 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + + +class MyTask : public FNET_Task +{ +public: + bool _done; + + MyTask(FNET_Scheduler &scheduler) + : FNET_Task(&scheduler), + _done(false) {} + + bool done() const { return _done; } + void PerformTask() { _done = true; } +}; + + +TEST("slow event loop") { + FastOS_Time t; + t.SetMilliSecs(0); + + FNET_Scheduler scheduler(&t, &t); + MyTask task(scheduler); + MyTask task2(scheduler); + + scheduler.CheckTasks(); + t.AddMilliSecs(10000); + task.Schedule(5.0); + + uint32_t cnt = 0; + for (;;) { + scheduler.CheckTasks(); + if (task.done()) { + break; + } + ++cnt; + t.AddMilliSecs(1); + } + + if (!EXPECT_TRUE(cnt > 4700 && cnt < 4800)) { + fprintf(stderr, "cnt=%d\n", cnt); + } + + scheduler.CheckTasks(); + t.AddMilliSecs(10000); + task2.Schedule(5.0); + + uint32_t cnt2 = 0; + for(;;) { + scheduler.CheckTasks(); + if (task2.done()) { + break; + } + ++cnt2; + t.AddMilliSecs(10000); + } + + if (!EXPECT_TRUE(cnt2 > 15 && cnt2 < 25)) { + fprintf(stderr, "cnt2=%d\n", cnt2); + } +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/sync_execute/.gitignore b/fnet/src/tests/sync_execute/.gitignore new file mode 100644 index 00000000000..90d51f31d97 --- /dev/null +++ b/fnet/src/tests/sync_execute/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +sync_execute_test +fnet_sync_execute_test_app diff --git a/fnet/src/tests/sync_execute/CMakeLists.txt b/fnet/src/tests/sync_execute/CMakeLists.txt new file mode 100644 index 00000000000..820cdb1af86 --- /dev/null +++ b/fnet/src/tests/sync_execute/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_sync_execute_test_app + SOURCES + sync_execute.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_sync_execute_test_app COMMAND fnet_sync_execute_test_app) diff --git a/fnet/src/tests/sync_execute/DESC b/fnet/src/tests/sync_execute/DESC new file mode 100644 index 00000000000..4f1b88599a8 --- /dev/null +++ b/fnet/src/tests/sync_execute/DESC @@ -0,0 +1 @@ +Test the sync and execute methods on the transport object. diff --git a/fnet/src/tests/sync_execute/FILES b/fnet/src/tests/sync_execute/FILES new file mode 100644 index 00000000000..2e03de91167 --- /dev/null +++ b/fnet/src/tests/sync_execute/FILES @@ -0,0 +1 @@ +sync_execute.cpp diff --git a/fnet/src/tests/sync_execute/sync_execute.cpp b/fnet/src/tests/sync_execute/sync_execute.cpp new file mode 100644 index 00000000000..e24d63effd5 --- /dev/null +++ b/fnet/src/tests/sync_execute/sync_execute.cpp @@ -0,0 +1,39 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include + +struct DoIt : public FNET_IExecutable { + vespalib::Gate gate; + virtual void execute() { + gate.countDown(); + } +}; + +TEST("sync execute") { + DoIt exe1; + DoIt exe2; + DoIt exe3; + DoIt exe4; + FastOS_ThreadPool pool(128 * 1024 * 1024); + FNET_Transport transport; + ASSERT_TRUE(transport.execute(&exe1)); + ASSERT_TRUE(transport.Start(&pool)); + exe1.gate.await(); + ASSERT_TRUE(transport.execute(&exe2)); + transport.sync(); + ASSERT_TRUE(exe2.gate.getCount() == 0u); + ASSERT_TRUE(transport.execute(&exe3)); + transport.ShutDown(false); + ASSERT_TRUE(!transport.execute(&exe4)); + transport.sync(); + transport.WaitFinished(); + transport.sync(); + pool.Close(); + ASSERT_TRUE(exe1.gate.getCount() == 0u); + ASSERT_TRUE(exe2.gate.getCount() == 0u); + ASSERT_TRUE(exe3.gate.getCount() == 0u); + ASSERT_TRUE(exe4.gate.getCount() == 1u); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/thread_selection/.gitignore b/fnet/src/tests/thread_selection/.gitignore new file mode 100644 index 00000000000..e0d5b3c18f7 --- /dev/null +++ b/fnet/src/tests/thread_selection/.gitignore @@ -0,0 +1 @@ +fnet_thread_selection_test_app diff --git a/fnet/src/tests/thread_selection/CMakeLists.txt b/fnet/src/tests/thread_selection/CMakeLists.txt new file mode 100644 index 00000000000..183781f3b21 --- /dev/null +++ b/fnet/src/tests/thread_selection/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_thread_selection_test_app + SOURCES + thread_selection_test.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_thread_selection_test_app COMMAND fnet_thread_selection_test_app) diff --git a/fnet/src/tests/thread_selection/thread_selection_test.cpp b/fnet/src/tests/thread_selection/thread_selection_test.cpp new file mode 100644 index 00000000000..5e5f0891ba4 --- /dev/null +++ b/fnet/src/tests/thread_selection/thread_selection_test.cpp @@ -0,0 +1,89 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include +#include +#include +#include +#include +#include + +struct Fixture { + std::mutex lock; + FNET_Transport transport; + std::map counts; + Fixture(size_t num_threads) : transport(num_threads) {} + void count_selected_thread(const void *key, size_t key_len) { + std::lock_guard guard(lock); + FNET_TransportThread *thread = transport.select_thread(key, key_len); + ++counts[thread]; + } + std::vector get_counts() { + std::vector result; + for (const auto &entry: counts) { + result.push_back(entry.second); + } + return result; + } + void dump_counts() { + std::vector list = get_counts(); + fprintf(stderr, "thread selection counts: ["); + for (size_t i = 0; i < list.size(); ++i) { + if (i > 0) { + fprintf(stderr, ", "); + } + fprintf(stderr, "%zu", list[i]); + } + fprintf(stderr, "]\n"); + } +}; + +TEST_F("require that selection is time sensistive", Fixture(8)) +{ + using namespace std::literals; + vespalib::string key("my random key"); + for (size_t i = 0; i < 256; ++i) { + f1.count_selected_thread(key.data(), key.size()); + std::this_thread::sleep_for(10ms); + } + EXPECT_EQUAL(f1.counts.size(), 8u); + f1.dump_counts(); +} + +TEST_F("require that selection is key sensistive", Fixture(8)) +{ + for (size_t i = 0; i < 256; ++i) { + vespalib::string key = vespalib::make_string("my random key %zu", i); + f1.count_selected_thread(key.data(), key.size()); + } + EXPECT_EQUAL(f1.counts.size(), 8u); + f1.dump_counts(); +} + +TEST_MT_F("require that selection is thread sensitive", 256, Fixture(8)) +{ + f1.count_selected_thread(nullptr, 0); + TEST_BARRIER(); + if (thread_id == 0) { + std::vector counts = f1.get_counts(); + EXPECT_EQUAL(f1.counts.size(), 8u); + f1.dump_counts(); + } +} + +void recursive_select(Fixture &f, size_t n) { + char dummy[32]; + if (n > 0) { + recursive_select(f, n - 1); + f.count_selected_thread(nullptr, 0); + (void) dummy; + } +} + +TEST_F("require that selection is stack location sensistive", Fixture(8)) +{ + recursive_select(f, 256); + EXPECT_EQUAL(f1.counts.size(), 8u); + f1.dump_counts(); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/fnet/src/tests/time/.gitignore b/fnet/src/tests/time/.gitignore new file mode 100644 index 00000000000..fddb94a8e8a --- /dev/null +++ b/fnet/src/tests/time/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +timespeed_test +fnet_timespeed_test_app diff --git a/fnet/src/tests/time/CMakeLists.txt b/fnet/src/tests/time/CMakeLists.txt new file mode 100644 index 00000000000..5f620af2b53 --- /dev/null +++ b/fnet/src/tests/time/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(fnet_timespeed_test_app + SOURCES + timespeed.cpp + DEPENDS + fnet +) +vespa_add_test(NAME fnet_timespeed_test_app NO_VALGRIND COMMAND fnet_timespeed_test_app) diff --git a/fnet/src/tests/time/DESC b/fnet/src/tests/time/DESC new file mode 100644 index 00000000000..27214fa9bcc --- /dev/null +++ b/fnet/src/tests/time/DESC @@ -0,0 +1 @@ +Check how fast we can determine what time it is. diff --git a/fnet/src/tests/time/FILES b/fnet/src/tests/time/FILES new file mode 100644 index 00000000000..bf3708f36fa --- /dev/null +++ b/fnet/src/tests/time/FILES @@ -0,0 +1 @@ +timespeed.cpp diff --git a/fnet/src/tests/time/timespeed.cpp b/fnet/src/tests/time/timespeed.cpp new file mode 100644 index 00000000000..cdbaaa27781 --- /dev/null +++ b/fnet/src/tests/time/timespeed.cpp @@ -0,0 +1,68 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include +#include + + +class DummyTime +{ +public: + void SetNow() {} +}; + + +TEST("time speed") { + FastOS_Time start; + FastOS_Time stop; + DummyTime dummy; + FastOS_Time now; + double dummyTime; + double actualTime; + double overhead; + uint32_t i; + + start.SetNow(); + for (i = 0; i < 100000; i++) { + dummy.SetNow(); + dummy.SetNow(); + dummy.SetNow(); + dummy.SetNow(); + dummy.SetNow(); + dummy.SetNow(); + dummy.SetNow(); + dummy.SetNow(); + dummy.SetNow(); + dummy.SetNow(); + } + stop.SetNow(); + stop -= start; + dummyTime = stop.MilliSecs(); + + fprintf(stderr, "1M dummy SetNow: %f ms (%1.2f/ms)\n", + dummyTime, 1000000.0 / dummyTime); + + start.SetNow(); + for (i = 0; i < 100000; i++) { + now.SetNow(); + now.SetNow(); + now.SetNow(); + now.SetNow(); + now.SetNow(); + now.SetNow(); + now.SetNow(); + now.SetNow(); + now.SetNow(); + now.SetNow(); + } + stop.SetNow(); + stop -= start; + actualTime = stop.MilliSecs(); + + fprintf(stderr, "1M actual SetNow: %f ms (%1.2f/ms)\n", + stop.MilliSecs(), 1000000.0 / stop.MilliSecs()); + + overhead = (actualTime - dummyTime) / 1000.0; + + fprintf(stderr, "approx overhead per SetNow: %f microseconds\n", overhead); +} + +TEST_MAIN() { TEST_RUN_ALL(); } -- cgit v1.2.3