diff options
Diffstat (limited to 'vespalib/src/tests')
69 files changed, 3815 insertions, 0 deletions
diff --git a/vespalib/src/tests/array/.gitignore b/vespalib/src/tests/array/.gitignore index 952bc0ecdf2..dd07c7d9777 100644 --- a/vespalib/src/tests/array/.gitignore +++ b/vespalib/src/tests/array/.gitignore @@ -1,3 +1,4 @@ /sort_benchmark /allocinarray_benchmark vespalib_array_test_app +vespalib_sort_benchmark_app diff --git a/vespalib/src/tests/array/CMakeLists.txt b/vespalib/src/tests/array/CMakeLists.txt index 18cb5c2b95e..fae7b32cd7e 100644 --- a/vespalib/src/tests/array/CMakeLists.txt +++ b/vespalib/src/tests/array/CMakeLists.txt @@ -6,3 +6,10 @@ vespa_add_executable(vespalib_array_test_app TEST vespalib ) vespa_add_test(NAME vespalib_array_test_app COMMAND vespalib_array_test_app) +vespa_add_executable(vespalib_sort_benchmark_app + SOURCES + sort_benchmark.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_sort_benchmark_app COMMAND vespalib_sort_benchmark_app BENCHMARK) diff --git a/vespalib/src/tests/array/sort_benchmark.cpp b/vespalib/src/tests/array/sort_benchmark.cpp new file mode 100644 index 00000000000..5d8a1efaa7a --- /dev/null +++ b/vespalib/src/tests/array/sort_benchmark.cpp @@ -0,0 +1,184 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/rusage.h> +#include <vespa/vespalib/util/array.hpp> +#include <csignal> +#include <algorithm> + +#include <vespa/log/log.h> +LOG_SETUP("sort_benchmark"); + +using namespace vespalib; + +class Test : public TestApp +{ +public: +private: + template<typename T> + vespalib::Array<T> create(size_t count); + template<typename T> + void sortDirect(size_t count); + template<typename T> + void sortInDirect(size_t count); + int Main() override; +}; + +template<size_t N> +class TT +{ +public: + TT(uint64_t v) : _v(v) { } + bool operator < (const TT & rhs) const { return _v < rhs._v; } +private: + uint64_t _v; + uint8_t _payLoad[N - sizeof(uint64_t)]; +}; + +template <typename T> +class I +{ +public: + I(const T * p) : _p(p) { } + bool operator < (const I & rhs) const { return *_p < *rhs._p; } +private: + const T * _p; +}; + +template<typename T> +vespalib::Array<T> +Test::create(size_t count) +{ + vespalib::Array<T> v; + v.reserve(count); + srand(0); + for (size_t i(0); i < count; i++) { + v.push_back(rand()); + } + return v; +} + +template<typename T> +void Test::sortDirect(size_t count) +{ + vespalib::Array<T> v(create<T>(count)); + LOG(info, "Running sortDirect with %ld count and payload of %ld", v.size(), sizeof(T)); + for (size_t j=0; j < 10; j++) { + vespalib::Array<T> t(v); + std::sort(t.begin(), t.end()); + } +} + +template<typename T> +void Test::sortInDirect(size_t count) +{ + vespalib::Array<T> k(create<T>(count)); + LOG(info, "Running sortInDirect with %ld count and payload of %ld", k.size(), sizeof(T)); + vespalib::Array< I<T> > v; + v.reserve(k.size()); + for (size_t i(0), m(k.size()); i < m; i++) { + v.push_back(&k[i]); + } + for (size_t j=0; j < 10; j++) { + vespalib::Array< I<T> > t(v); + std::sort(t.begin(), t.end()); + } +} + +int +Test::Main() +{ + std::string type("sortdirect"); + size_t count = 1000000; + size_t payLoad = 0; + if (_argc > 1) { + type = _argv[1]; + } + if (_argc > 2) { + count = strtol(_argv[2], NULL, 0); + } + if (_argc > 3) { + payLoad = strtol(_argv[3], NULL, 0); + } + TEST_INIT("sort_benchmark"); + steady_time start(steady_clock::now()); + if (payLoad < 8) { + typedef TT<8> T; + if (type == "sortdirect") { + sortDirect<T>(count); + } else if (type == "sortindirect") { + sortInDirect<T>(count); + } else { + LOG(warning, "type '%s' is unknown", type.c_str()); + } + } else if (payLoad < 16) { + typedef TT<16> T; + if (type == "sortdirect") { + sortDirect<T>(count); + } else if (type == "sortindirect") { + sortInDirect<T>(count); + } else { + LOG(warning, "type '%s' is unknown", type.c_str()); + } + } else if (payLoad < 32) { + typedef TT<32> T; + if (type == "sortdirect") { + sortDirect<T>(count); + } else if (type == "sortindirect") { + sortInDirect<T>(count); + } else { + LOG(warning, "type '%s' is unknown", type.c_str()); + } + } else if (payLoad < 64) { + typedef TT<64> T; + if (type == "sortdirect") { + sortDirect<T>(count); + } else if (type == "sortindirect") { + sortInDirect<T>(count); + } else { + LOG(warning, "type '%s' is unknown", type.c_str()); + } + } else if (payLoad < 128) { + typedef TT<128> T; + if (type == "sortdirect") { + sortDirect<T>(count); + } else if (type == "sortindirect") { + sortInDirect<T>(count); + } else { + LOG(warning, "type '%s' is unknown", type.c_str()); + } + } else if (payLoad < 256) { + typedef TT<256> T; + if (type == "sortdirect") { + sortDirect<T>(count); + } else if (type == "sortindirect") { + sortInDirect<T>(count); + } else { + LOG(warning, "type '%s' is unknown", type.c_str()); + } + } else if (payLoad < 512) { + typedef TT<512> T; + if (type == "sortdirect") { + sortDirect<T>(count); + } else if (type == "sortindirect") { + sortInDirect<T>(count); + } else { + LOG(warning, "type '%s' is unknown", type.c_str()); + } + } else { + typedef TT<1024> T; + LOG(info, "Payload %ld is too big to make any sense. Using %ld.", payLoad, sizeof(T)); + if (type == "sortdirect") { + sortDirect<T>(count); + } else if (type == "sortindirect") { + sortInDirect<T>(count); + } else { + LOG(warning, "type '%s' is unknown", type.c_str()); + } + } + LOG(info, "rusage = {\n%s\n}", vespalib::RUsage::createSelf(start).toString().c_str()); + ASSERT_EQUAL(0, kill(0, SIGPROF)); + TEST_DONE(); +} + +TEST_APPHOOK(Test); + diff --git a/vespalib/src/tests/benchmark/.gitignore b/vespalib/src/tests/benchmark/.gitignore new file mode 100644 index 00000000000..3280cb17888 --- /dev/null +++ b/vespalib/src/tests/benchmark/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +benchmark_test +vespalib_benchmark_test_app diff --git a/vespalib/src/tests/benchmark/CMakeLists.txt b/vespalib/src/tests/benchmark/CMakeLists.txt new file mode 100644 index 00000000000..7003a5c4183 --- /dev/null +++ b/vespalib/src/tests/benchmark/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_benchmark_test_app + SOURCES + benchmark.cpp + testbase.cpp + DEPENDS + vespalib + EXTERNAL_DEPENDS + ${VESPA_GLIBC_RT_LIB} +) +vespa_add_test(NAME vespalib_benchmark_test NO_VALGRIND COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_test.sh BENCHMARK + DEPENDS vespalib_benchmark_test_app) diff --git a/vespalib/src/tests/benchmark/benchmark.cpp b/vespalib/src/tests/benchmark/benchmark.cpp new file mode 100644 index 00000000000..f1e69758c8c --- /dev/null +++ b/vespalib/src/tests/benchmark/benchmark.cpp @@ -0,0 +1,30 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/vespalib/testkit/testapp.h> +#include "testbase.h" + +#include <vespa/log/log.h> +LOG_SETUP("benchmark_test"); + +using namespace vespalib; + +TEST_SETUP(Test) + +int +Test::Main() +{ + TEST_INIT("benchmark_test"); + + if (_argc > 1) { + size_t concurrency(1); + size_t numRuns(1000); + if (_argc > 2) { + numRuns = strtoul(_argv[2], NULL, 0); + if (_argc > 3) { + concurrency = strtoul(_argv[3], NULL, 0); + } + } + Benchmark::run(_argv[1], numRuns, concurrency); + } + + TEST_DONE(); +} diff --git a/vespalib/src/tests/benchmark/benchmark_test.sh b/vespalib/src/tests/benchmark/benchmark_test.sh new file mode 100755 index 00000000000..28dc6b518be --- /dev/null +++ b/vespalib/src/tests/benchmark/benchmark_test.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +set -e +TIME=time + +$TIME ./vespalib_benchmark_test_app vespalib::ParamByReferenceVectorInt 200000 1 +$TIME ./vespalib_benchmark_test_app vespalib::ParamByValueVectorInt 4000 1 +$TIME ./vespalib_benchmark_test_app vespalib::ParamByReferenceVectorString 30000 1 +$TIME ./vespalib_benchmark_test_app vespalib::ParamByValueVectorString 40 1 +$TIME ./vespalib_benchmark_test_app vespalib::ReturnByReferenceVectorString 10 1 +$TIME ./vespalib_benchmark_test_app vespalib::ReturnByValueVectorString 10 1 +$TIME ./vespalib_benchmark_test_app vespalib::ReturnByValueMultiVectorString 10 1 +$TIME ./vespalib_benchmark_test_app vespalib::ClockSystem 1000 1 +$TIME ./vespalib_benchmark_test_app vespalib::ClockGToD 1000 1 +$TIME ./vespalib_benchmark_test_app vespalib::ClockGToD 20000 1 +$TIME ./vespalib_benchmark_test_app vespalib::ClockREALTIME 1000 1 +$TIME ./vespalib_benchmark_test_app vespalib::ClockMONOTONIC 1000 1 +$TIME ./vespalib_benchmark_test_app vespalib::ClockMONOTONIC_RAW 1000 1 +$TIME ./vespalib_benchmark_test_app vespalib::ClockPROCESS_CPUTIME_ID 2500 1 +$TIME ./vespalib_benchmark_test_app vespalib::ClockTHREAD_CPUTIME_ID 2500 1 +$TIME ./vespalib_benchmark_test_app vespalib::CreateVespalibString 20000 1 diff --git a/vespalib/src/tests/benchmark/testbase.cpp b/vespalib/src/tests/benchmark/testbase.cpp new file mode 100644 index 00000000000..6b5f8d7d627 --- /dev/null +++ b/vespalib/src/tests/benchmark/testbase.cpp @@ -0,0 +1,279 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include "testbase.h" +#include <vespa/vespalib/util/time.h> +#include <cassert> + +#include <vespa/log/log.h> +LOG_SETUP(".testbase"); + +using namespace std::chrono; + +namespace vespalib { + +IMPLEMENT_IDENTIFIABLE_ABSTRACT_NS(vespalib, Benchmark, Identifiable); + +IMPLEMENT_BENCHMARK(ParamByReferenceVectorInt, Benchmark); +IMPLEMENT_BENCHMARK(ParamByValueVectorInt, Benchmark); +IMPLEMENT_BENCHMARK(ParamByReferenceVectorString, Benchmark); +IMPLEMENT_BENCHMARK(ParamByValueVectorString, Benchmark); +IMPLEMENT_BENCHMARK(ReturnByReferenceVectorString, Benchmark); +IMPLEMENT_BENCHMARK(ReturnByValueVectorString, Benchmark); +IMPLEMENT_BENCHMARK(ReturnByValueMultiVectorString, Benchmark); +IMPLEMENT_BENCHMARK(ClockSystem, Benchmark); +IMPLEMENT_BENCHMARK(ClockREALTIME, Benchmark); +IMPLEMENT_BENCHMARK(ClockMONOTONIC, Benchmark); +IMPLEMENT_BENCHMARK(ClockMONOTONIC_RAW, Benchmark); +IMPLEMENT_BENCHMARK(ClockPROCESS_CPUTIME_ID, Benchmark); +IMPLEMENT_BENCHMARK(ClockTHREAD_CPUTIME_ID, Benchmark); +IMPLEMENT_BENCHMARK(CreateVespalibString, Benchmark); + +void Benchmark::run(const char *name, size_t numRuns, size_t concurrency) +{ + const Identifiable::RuntimeClass * cInfo = Identifiable::classFromName(name); + if (cInfo) { + std::unique_ptr<Benchmark> test(static_cast<Benchmark *>(cInfo->create())); + test->run(numRuns, concurrency); + } else { + LOG(warning, "Could not find any test with the name %s", name); + } +} +void Benchmark::run(size_t numRuns, size_t concurrency) +{ + LOG(info, "Starting benchmark %s with %ld threads and %ld rep", getClass().name(), concurrency, numRuns); + for (size_t i(0); i < numRuns; i++) { + onRun(); + } + LOG(info, "Stopping benchmark %s", getClass().name()); +} + +size_t ParamByReferenceVectorInt::callByReference(const Vector & values) const +{ + return values.size(); +} + +size_t ParamByReferenceVectorInt::onRun() +{ + Vector values(1000); + size_t sum(0); + for (size_t i=0; i < 1000; i++) { + sum += callByReference(values); + } + return sum; +} + +size_t ParamByValueVectorInt::callByValue(Vector values) const +{ + return values.size(); +} + +size_t ParamByValueVectorInt::onRun() +{ + Vector values(1000); + size_t sum(0); + for (size_t i=0; i < 1000; i++) { + sum += callByValue(values); + } + return sum; +} + +size_t ParamByReferenceVectorString::callByReference(const Vector & values) const +{ + return values.size(); +} + +size_t ParamByReferenceVectorString::onRun() +{ + Vector values(1000, "This is a simple string copy test"); + size_t sum(0); + for (size_t i=0; i < 1000; i++) { + sum += callByReference(values); + } + return sum; +} + +size_t ParamByValueVectorString::callByValue(Vector values) const +{ + return values.size(); +} + +size_t ParamByValueVectorString::onRun() +{ + Vector values(1000, "This is a simple string copy test"); + size_t sum(0); + for (size_t i=0; i < 1000; i++) { + sum += callByValue(values); + } + return sum; +} + +const ReturnByReferenceVectorString::Vector & ReturnByReferenceVectorString::returnByReference(Vector & param) const +{ + Vector values(1000, "return by value"); + param.swap(values); + return param; +} + +size_t ReturnByReferenceVectorString::onRun() +{ + size_t sum(0); + for (size_t i=0; i < 1000; i++) { + Vector values; + sum += returnByReference(values).size(); + } + return sum; +} + +ReturnByValueVectorString::Vector ReturnByValueVectorString::returnByValue() const +{ + Vector values; + if (rand() % 7) { + Vector tmp(1000, "return by value"); + values.swap(tmp); + } else { + Vector tmp(1000, "Return by value"); + values.swap(tmp); + } + return values; +} + +size_t ReturnByValueVectorString::onRun() +{ + size_t sum(0); + for (size_t i=0; i < 1000; i++) { + sum += returnByValue().size(); + } + return sum; +} + +ReturnByValueMultiVectorString::Vector ReturnByValueMultiVectorString::returnByValue() const +{ + if (rand() % 7) { + Vector values(1000, "return by value"); + return values; + } else { + Vector values(1000, "Return by value"); + return values; + } +} + +size_t ReturnByValueMultiVectorString::onRun() +{ + size_t sum(0); + for (size_t i=0; i < 1000; i++) { + sum += returnByValue().size(); + } + return sum; +} + +size_t ClockSystem::onRun() +{ + vespalib::system_time start(vespalib::system_clock::now()); + vespalib::system_time end(start); + for (size_t i=0; i < 1000; i++) { + end = vespalib::system_clock::now(); + } + return std::chrono::duration_cast<std::chrono::nanoseconds>(start - end).count(); +} + +size_t ClockREALTIME::onRun() +{ + struct timespec ts; + int foo = clock_gettime(CLOCK_REALTIME, &ts); + assert(foo == 0); + (void) foo; + nanoseconds start(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + nanoseconds end(start); + for (size_t i=0; i < 1000; i++) { + clock_gettime(CLOCK_REALTIME, &ts); + end = nanoseconds(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + } + return count_ns(start - end); +} + +size_t ClockMONOTONIC::onRun() +{ + struct timespec ts; + int foo = clock_gettime(CLOCK_MONOTONIC, &ts); + assert(foo == 0); + (void) foo; + nanoseconds start(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + nanoseconds end(start); + for (size_t i=0; i < 1000; i++) { + clock_gettime(CLOCK_MONOTONIC, &ts); + end = nanoseconds(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + } + return count_ns(start - end);; +} + +ClockMONOTONIC_RAW::ClockMONOTONIC_RAW() +{ +#ifndef CLOCK_MONOTONIC_RAW + LOG(warning, "CLOCK_MONOTONIC_RAW is not defined, using CLOCK_MONOTONIC instead."); +#endif +} + +#ifndef CLOCK_MONOTONIC_RAW + #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC +#endif + +size_t ClockMONOTONIC_RAW::onRun() +{ + struct timespec ts; + int foo = clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + assert(foo == 0); + (void) foo; + nanoseconds start(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + nanoseconds end(start); + for (size_t i=0; i < 1000; i++) { + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + end = nanoseconds(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + } + return count_ns(start - end); +} + +size_t ClockPROCESS_CPUTIME_ID::onRun() +{ + struct timespec ts; + int foo = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); + assert(foo == 0); + (void) foo; + nanoseconds start(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + nanoseconds end(start); + for (size_t i=0; i < 1000; i++) { + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); + end =nanoseconds(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + } + return count_ns(start - end); +} + +size_t ClockTHREAD_CPUTIME_ID::onRun() +{ + struct timespec ts; + int foo = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + assert(foo == 0); + (void) foo; + nanoseconds start(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + nanoseconds end(start); + for (size_t i=0; i < 1000; i++) { + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); + end = nanoseconds(ts.tv_sec*1000L*1000L*1000L + ts.tv_nsec); + } + return count_ns(start - end); +} + +size_t CreateVespalibString::onRun() +{ + size_t sum(0); + const char * text1("Dette er en passe"); + const char * text2(" kort streng som passer paa stacken"); + char text[100]; + strcpy(text, text1); + strcat(text, text2); + for (size_t i=0; i < 1000; i++) { + string s(text); + sum += s.size(); + } + return sum; +} + +} diff --git a/vespalib/src/tests/benchmark/testbase.h b/vespalib/src/tests/benchmark/testbase.h new file mode 100644 index 00000000000..95621f52471 --- /dev/null +++ b/vespalib/src/tests/benchmark/testbase.h @@ -0,0 +1,168 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include <vespa/vespalib/objects/identifiable.h> +#include <vector> +#include <string> + +#define BENCHMARK_BASE_CID(n) (0x1000000 + n) + +#define CID_vespalib_Benchmark BENCHMARK_BASE_CID(0) +#define CID_vespalib_ParamByReferenceVectorInt BENCHMARK_BASE_CID(1) +#define CID_vespalib_ParamByReferenceVectorString BENCHMARK_BASE_CID(2) +#define CID_vespalib_ParamByValueVectorInt BENCHMARK_BASE_CID(3) +#define CID_vespalib_ParamByValueVectorString BENCHMARK_BASE_CID(4) +#define CID_vespalib_ReturnByReferenceVectorString BENCHMARK_BASE_CID(5) +#define CID_vespalib_ReturnByValueVectorString BENCHMARK_BASE_CID(6) +#define CID_vespalib_ReturnByValueMultiVectorString BENCHMARK_BASE_CID(7) +#define CID_vespalib_ClockSystem BENCHMARK_BASE_CID(8) +#define CID_vespalib_ClockREALTIME BENCHMARK_BASE_CID(10) +#define CID_vespalib_ClockMONOTONIC BENCHMARK_BASE_CID(11) +#define CID_vespalib_ClockMONOTONIC_RAW BENCHMARK_BASE_CID(12) +#define CID_vespalib_ClockPROCESS_CPUTIME_ID BENCHMARK_BASE_CID(13) +#define CID_vespalib_ClockTHREAD_CPUTIME_ID BENCHMARK_BASE_CID(14) +#define CID_vespalib_CreateVespalibString BENCHMARK_BASE_CID(15) + +#define DECLARE_BENCHMARK(a) DECLARE_IDENTIFIABLE_NS(vespalib, a) +#define IMPLEMENT_BENCHMARK(a, d) IMPLEMENT_IDENTIFIABLE_NS(vespalib, a, d) + +namespace vespalib { + +class Benchmark : public Identifiable +{ +public: + DECLARE_IDENTIFIABLE_ABSTRACT_NS(vespalib, Benchmark); + void run(size_t numRuns, size_t concurrency=1); + static void run(const char * testName, size_t numRuns, size_t concurrency); +private: + virtual size_t onRun() = 0; +}; + +class ParamByReferenceVectorInt : public Benchmark +{ +public: + DECLARE_BENCHMARK(ParamByReferenceVectorInt); +private: + typedef std::vector<int> Vector; + size_t callByReference(const Vector & values) const __attribute__((noinline)); + size_t onRun() override; +}; + +class ParamByValueVectorInt : public Benchmark +{ +public: + DECLARE_BENCHMARK(ParamByValueVectorInt); +private: + typedef std::vector<int> Vector; + size_t callByValue(Vector values) const __attribute__((noinline)); + size_t onRun() override; +}; + +class ParamByReferenceVectorString : public Benchmark +{ +public: + DECLARE_BENCHMARK(ParamByReferenceVectorString); +private: + typedef std::vector<std::string> Vector; + size_t callByReference(const Vector & values) const __attribute__((noinline)); + size_t onRun() override; +}; + +class ParamByValueVectorString : public Benchmark +{ +public: + DECLARE_BENCHMARK(ParamByValueVectorString); +private: + typedef std::vector<std::string> Vector; + size_t callByValue(Vector values) const __attribute__((noinline)); + size_t onRun() override; +}; + +class ReturnByReferenceVectorString : public Benchmark +{ +public: + DECLARE_BENCHMARK(ReturnByReferenceVectorString); +private: + typedef std::vector<std::string> Vector; + const Vector & returnByReference(Vector & values) const __attribute__((noinline)); + size_t onRun() override; +}; + +class ReturnByValueVectorString : public Benchmark +{ +public: + DECLARE_BENCHMARK(ReturnByValueVectorString); +private: + typedef std::vector<std::string> Vector; + Vector returnByValue() const __attribute__((noinline)); + size_t onRun() override; +}; + +class ReturnByValueMultiVectorString : public Benchmark +{ +public: + DECLARE_BENCHMARK(ReturnByValueMultiVectorString); +private: + typedef std::vector<std::string> Vector; + Vector returnByValue() const __attribute__((noinline)); + size_t onRun() override; +}; + +class CreateVespalibString : public Benchmark +{ +public: + DECLARE_BENCHMARK(CreateVespalibString); +private: + size_t onRun() override; +}; + +class ClockSystem : public Benchmark +{ +public: + DECLARE_BENCHMARK(ClockSystem); +private: + size_t onRun() override; +}; + +class ClockREALTIME : public Benchmark +{ +public: + DECLARE_BENCHMARK(ClockREALTIME); +private: + size_t onRun() override; +}; + +class ClockMONOTONIC : public Benchmark +{ +public: + DECLARE_BENCHMARK(ClockMONOTONIC); +private: + size_t onRun() override; +}; + +class ClockMONOTONIC_RAW : public Benchmark +{ +public: + DECLARE_BENCHMARK(ClockMONOTONIC_RAW); + ClockMONOTONIC_RAW(); +private: + size_t onRun() override; +}; + +class ClockPROCESS_CPUTIME_ID : public Benchmark +{ +public: + DECLARE_BENCHMARK(ClockPROCESS_CPUTIME_ID); +private: + size_t onRun() override; +}; + +class ClockTHREAD_CPUTIME_ID : public Benchmark +{ +public: + DECLARE_BENCHMARK(ClockTHREAD_CPUTIME_ID); +private: + size_t onRun() override; +}; + +} diff --git a/vespalib/src/tests/bits/.gitignore b/vespalib/src/tests/bits/.gitignore new file mode 100644 index 00000000000..b5330fc2580 --- /dev/null +++ b/vespalib/src/tests/bits/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +bits_test +vespalib_bits_test_app diff --git a/vespalib/src/tests/bits/CMakeLists.txt b/vespalib/src/tests/bits/CMakeLists.txt new file mode 100644 index 00000000000..f63196bc489 --- /dev/null +++ b/vespalib/src/tests/bits/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_bits_test_app TEST + SOURCES + bits_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_bits_test_app COMMAND vespalib_bits_test_app) diff --git a/vespalib/src/tests/bits/bits_test.cpp b/vespalib/src/tests/bits/bits_test.cpp new file mode 100644 index 00000000000..47d691c739d --- /dev/null +++ b/vespalib/src/tests/bits/bits_test.cpp @@ -0,0 +1,63 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/bits.h> +#include <boost/crc.hpp> +#include <boost/version.hpp> + +#if BOOST_VERSION < 106900 + #define REFLECT reflect +#else + #define REFLECT reflect_q +#endif + +using namespace vespalib; + +class Test : public TestApp +{ +public: + int Main() override; + template <typename T> + void testFixed(const T * v, size_t sz); + void testBuffer(); +}; + +int +Test::Main() +{ + TEST_INIT("bits_test"); + uint8_t u8[5] = { 0, 1, 127, 135, 255 }; + testFixed(u8, sizeof(u8)/sizeof(u8[0])); + uint16_t u16[5] = { 0, 1, 127, 135, 255 }; + testFixed(u16, sizeof(u16)/sizeof(u16[0])); + uint32_t u32[5] = { 0, 1, 127, 135, 255 }; + testFixed(u32, sizeof(u32)/sizeof(u32[0])); + uint64_t u64[5] = { 0, 1, 127, 135, 255 }; + testFixed(u64, sizeof(u64)/sizeof(u64[0])); + testBuffer(); + TEST_DONE(); +} + +template <typename T> +void Test::testFixed(const T * v, size_t sz) +{ + EXPECT_EQUAL(0u, Bits::reverse(static_cast<T>(0))); + EXPECT_EQUAL(1ul << (sizeof(T)*8 - 1), Bits::reverse(static_cast<T>(1))); + EXPECT_EQUAL(static_cast<T>(-1), Bits::reverse(static_cast<T>(-1))); + for (size_t i(0); i < sz; i++) { + EXPECT_EQUAL(Bits::reverse(v[i]), boost::detail::reflector<sizeof(T)*8>::REFLECT(v[i])); + EXPECT_EQUAL(Bits::reverse(Bits::reverse(v[i])), v[i]); + } +} + +void Test::testBuffer() +{ + uint64_t a(0x0102040810204080ul); + uint64_t b(a); + Bits::reverse(&a, sizeof(a)); + EXPECT_EQUAL(a, Bits::reverse(b)); + Bits::reverse(&a, sizeof(a)); + EXPECT_EQUAL(a, b); +} + +TEST_APPHOOK(Test) diff --git a/vespalib/src/tests/clock/.gitignore b/vespalib/src/tests/clock/.gitignore new file mode 100644 index 00000000000..96861fcc5d3 --- /dev/null +++ b/vespalib/src/tests/clock/.gitignore @@ -0,0 +1,5 @@ +.depend +Makefile +clock_test +vespalib_clock_test_app +vespalib_clock_benchmark_app diff --git a/vespalib/src/tests/clock/CMakeLists.txt b/vespalib/src/tests/clock/CMakeLists.txt new file mode 100644 index 00000000000..d3ee3178163 --- /dev/null +++ b/vespalib/src/tests/clock/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_clock_benchmark_app TEST + SOURCES + clock_benchmark.cpp + DEPENDS + vespalib +) +vespa_add_executable(vespalib_clock_test_app TEST + SOURCES + clock_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_clock_test_app COMMAND vespalib_clock_test_app) diff --git a/vespalib/src/tests/clock/clock_benchmark.cpp b/vespalib/src/tests/clock/clock_benchmark.cpp new file mode 100644 index 00000000000..249add4bc1a --- /dev/null +++ b/vespalib/src/tests/clock/clock_benchmark.cpp @@ -0,0 +1,178 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/util/clock.h> +#include <vespa/vespalib/util/invokeserviceimpl.h> +#include <vespa/fastos/thread.h> +#include <cassert> +#include <vector> +#include <atomic> +#include <cinttypes> +#include <cstring> +#include <condition_variable> +#include <mutex> + +using vespalib::Clock; +using vespalib::steady_time; +using vespalib::steady_clock; +using vespalib::duration; +using vespalib::to_s; + +struct UpdateClock { + virtual ~UpdateClock() {} + virtual void update() = 0; +}; + +struct NSValue : public UpdateClock { + void update() override { _value = std::chrono::steady_clock::now().time_since_epoch().count(); } + int64_t _value; +}; + +struct NSVolatile : public UpdateClock { + void update() override { _value = std::chrono::steady_clock::now().time_since_epoch().count(); } + volatile int64_t _value; +}; +struct NSAtomic : public UpdateClock { + void update() override { _value.store(std::chrono::steady_clock::now().time_since_epoch().count()); } + std::atomic<int64_t> _value; +}; + +class TestClock : public FastOS_Runnable +{ +private: + int _timePeriodMS; + std::mutex _lock; + std::condition_variable _cond; + UpdateClock &_clock; + bool _stop; + + void Run(FastOS_ThreadInterface *thisThread, void *arguments) override; + +public: + TestClock(UpdateClock & clock, double timePeriod) + : _timePeriodMS(static_cast<uint32_t>(timePeriod*1000)), + _lock(), + _cond(), + _clock(clock), + _stop(false) + { } + ~TestClock() { + std::lock_guard<std::mutex> guard(_lock); + _stop = true; + _cond.notify_all(); + } +}; + +void TestClock::Run(FastOS_ThreadInterface *thread, void *) +{ + std::unique_lock<std::mutex> guard(_lock); + while ( ! thread->GetBreakFlag() && !_stop) { + _clock.update(); + _cond.wait_for(guard, std::chrono::milliseconds(_timePeriodMS)); + } +} + +struct SamplerBase : public FastOS_Runnable { + SamplerBase(uint32_t threadId) + : _thread(nullptr), + _threadId(threadId), + _samples(0), + _count() + { + memset(_count, 0, sizeof(_count)); + } + FastOS_ThreadInterface * _thread; + uint32_t _threadId; + uint64_t _samples; + uint64_t _count[3]; +}; + +template<typename Func> +struct Sampler : public SamplerBase { + Sampler(Func func, uint32_t threadId) + : SamplerBase(threadId), + _func(func) + { } + void Run(FastOS_ThreadInterface *, void *) override { + uint64_t samples; + steady_time prev = _func(); + for (samples = 0; (samples < _samples); samples++) { + steady_time now = _func(); + duration diff = now - prev; + if (diff > duration::zero()) prev = now; + _count[1 + ((diff == duration::zero()) ? 0 : (diff > duration::zero()) ? 1 : -1)]++; + } + + } + Func _func; +}; + +template<typename Func> +void benchmark(const char * desc, FastOS_ThreadPool & pool, uint64_t samples, uint32_t numThreads, Func func) { + std::vector<std::unique_ptr<SamplerBase>> threads; + threads.reserve(numThreads); + steady_time start = steady_clock::now(); + for (uint32_t i(0); i < numThreads; i++) { + SamplerBase * sampler = new Sampler<Func>(func, i); + sampler->_samples = samples; + sampler->_thread = pool.NewThread(sampler, nullptr); + threads.emplace_back(sampler); + } + uint64_t count[3]; + memset(count, 0, sizeof(count)); + for (const auto & sampler : threads) { + sampler->_thread->Join(); + for (uint32_t i(0); i < 3; i++) { + count[i] += sampler->_count[i]; + } + } + printf("%s: Took %" PRId64 " clock samples in %2.3f with [%" PRId64 ", %" PRId64 ", %" PRId64 "] counts\n", desc, samples, to_s(steady_clock::now() - start), count[0], count[1], count[2]); +} + +int +main(int , char *argv[]) +{ + uint64_t frequency = atoll(argv[1]); + uint32_t numThreads = atoi(argv[2]); + uint64_t samples = atoll(argv[3]); + FastOS_ThreadPool pool(0x10000); + NSValue nsValue; + NSVolatile nsVolatile; + NSAtomic nsAtomic; + vespalib::InvokeServiceImpl invoker(vespalib::from_s(1.0/frequency)); + Clock clock(invoker.nowRef()); + TestClock nsClock(nsValue, 1.0/frequency); + TestClock nsVolatileClock(nsVolatile, 1.0/frequency); + TestClock nsAtomicClock(nsAtomic, 1.0/frequency); + assert(pool.NewThread(&nsClock, nullptr) != nullptr); + assert(pool.NewThread(&nsVolatileClock, nullptr) != nullptr); + assert(pool.NewThread(&nsAtomicClock, nullptr) != nullptr); + + benchmark("vespalib::Clock", pool, samples, numThreads, [&clock]() { + return clock.getTimeNS(); + }); + benchmark("uint64_t", pool, samples, numThreads, [&nsValue]() { + return steady_time (duration(nsValue._value)); + }); + benchmark("volatile uint64_t", pool, samples, numThreads, [&nsVolatile]() { + return steady_time(duration(nsVolatile._value)); + }); + benchmark("memory_order_relaxed", pool, samples, numThreads, [&nsAtomic]() { + return steady_time(duration(nsAtomic._value.load(std::memory_order_relaxed))); + }); + benchmark("memory_order_consume", pool, samples, numThreads, [&nsAtomic]() { + return steady_time(duration(nsAtomic._value.load(std::memory_order_consume))); + }); + benchmark("memory_order_acquire", pool, samples, numThreads, [&nsAtomic]() { + return steady_time(duration(nsAtomic._value.load(std::memory_order_acquire))); + }); + benchmark("memory_order_seq_cst", pool, samples, numThreads, [&nsAtomic]() { + return steady_time(duration(nsAtomic._value.load(std::memory_order_seq_cst))); + }); + + benchmark("vespalib::steady_time::now()", pool, samples, numThreads, []() { + return steady_clock::now(); + }); + + pool.Close(); + return 0; +} diff --git a/vespalib/src/tests/clock/clock_test.cpp b/vespalib/src/tests/clock/clock_test.cpp new file mode 100644 index 00000000000..f2de085da84 --- /dev/null +++ b/vespalib/src/tests/clock/clock_test.cpp @@ -0,0 +1,29 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/clock.h> +#include <vespa/vespalib/util/invokeserviceimpl.h> +#include <thread> + +using vespalib::Clock; +using vespalib::duration; +using vespalib::steady_time; +using vespalib::steady_clock; + +void waitForMovement(steady_time start, Clock & clock, vespalib::duration timeout) { + steady_time startOsClock = steady_clock::now(); + while ((clock.getTimeNS() <= start) && ((steady_clock::now() - startOsClock) < timeout)) { + std::this_thread::sleep_for(1ms); + } +} + +TEST("Test that clock is ticking forward") { + vespalib::InvokeServiceImpl invoker(50ms); + Clock clock(invoker.nowRef()); + steady_time start = clock.getTimeNS(); + waitForMovement(start, clock, 10s); + steady_time stop = clock.getTimeNS(); + EXPECT_TRUE(stop > start); +} + +TEST_MAIN() { TEST_RUN_ALL(); }
\ No newline at end of file diff --git a/vespalib/src/tests/crc/.gitignore b/vespalib/src/tests/crc/.gitignore new file mode 100644 index 00000000000..cd64c20e0a4 --- /dev/null +++ b/vespalib/src/tests/crc/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +crc_test +vespalib_crc_test_app diff --git a/vespalib/src/tests/crc/CMakeLists.txt b/vespalib/src/tests/crc/CMakeLists.txt new file mode 100644 index 00000000000..30adfd131f1 --- /dev/null +++ b/vespalib/src/tests/crc/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_crc_test_app TEST + SOURCES + crc_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_crc_test_app COMMAND vespalib_crc_test_app boost) diff --git a/vespalib/src/tests/crc/crc_test.cpp b/vespalib/src/tests/crc/crc_test.cpp new file mode 100644 index 00000000000..8afeed487ee --- /dev/null +++ b/vespalib/src/tests/crc/crc_test.cpp @@ -0,0 +1,78 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/crc.h> +#include <boost/crc.hpp> +#include <vector> + +using namespace vespalib; + +class Test : public TestApp +{ +public: + int Main() override; + void testCorrectNess(); + void testBenchmark(bool our, size_t bufSz, size_t numRep); +}; + +int +Test::Main() +{ + TEST_INIT("crc_test"); + testCorrectNess(); + if (_argc >= 2) { + testBenchmark(false, 1024, 1000*1000); + } else { + testBenchmark(true, 1024, 1000*1000); + } + TEST_DONE(); +} + +void Test::testCorrectNess() +{ + const char *a[7] = { "", "a", "ab", "abc", "abcd", "abcde", "doc:crawler:http://www.ntnu.no/" }; + for (size_t i(0); i < sizeof(a)/sizeof(a[0]); i++) { + uint32_t vespaCrc32 = crc_32_type::crc(a[i], strlen(a[i])); + boost::crc_32_type calculator; + calculator.process_bytes(a[i], strlen(a[i])); + EXPECT_EQUAL(vespaCrc32, calculator.checksum()); + vespalib::crc_32_type calculator2; + calculator2.process_bytes(a[i], strlen(a[i])); + EXPECT_EQUAL(vespaCrc32, calculator2.checksum()); + EXPECT_EQUAL(calculator.checksum(), calculator2.checksum()); + } + vespalib::crc_32_type calculator2; + boost::crc_32_type calculator; + for (size_t i(0); i < sizeof(a)/sizeof(a[0]); i++) { + calculator.process_bytes(a[i], strlen(a[i])); + calculator2.process_bytes(a[i], strlen(a[i])); + EXPECT_EQUAL(calculator.checksum(), calculator2.checksum()); + } + EXPECT_EQUAL(calculator.checksum(), calculator2.checksum()); +} + +void Test::testBenchmark(bool our, size_t bufSz, size_t numRep) +{ + std::vector<char> a(numRep+bufSz); + for(size_t i(0), m(a.size()); i < m; i++) { + a[i] = i&0xff; + } + uint32_t sum(0); + if (our) { + for (size_t i(0); i < (numRep); i++) { + //sum ^= crc_32_type::crc(&a[i], bufSz); + vespalib::crc_32_type calculator; + calculator.process_bytes(&a[i], bufSz); + sum ^=calculator.checksum(); + } + } else { + for (size_t i(0); i < (numRep); i++) { + boost::crc_32_type calculator; + calculator.process_bytes(&a[i], bufSz); + sum ^=calculator.checksum(); + } + } + printf("sum = %x\n", sum); +} + +TEST_APPHOOK(Test) diff --git a/vespalib/src/tests/directio/.gitignore b/vespalib/src/tests/directio/.gitignore new file mode 100644 index 00000000000..ad19022dfc3 --- /dev/null +++ b/vespalib/src/tests/directio/.gitignore @@ -0,0 +1 @@ +vespalib_directio_test_app diff --git a/vespalib/src/tests/directio/CMakeLists.txt b/vespalib/src/tests/directio/CMakeLists.txt new file mode 100644 index 00000000000..41a8dca85b9 --- /dev/null +++ b/vespalib/src/tests/directio/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_directio_test_app TEST + SOURCES + directio.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_directio_test_app COMMAND vespalib_directio_test_app) diff --git a/vespalib/src/tests/directio/directio.cpp b/vespalib/src/tests/directio/directio.cpp new file mode 100644 index 00000000000..77374f6f926 --- /dev/null +++ b/vespalib/src/tests/directio/directio.cpp @@ -0,0 +1,57 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/vespalib/stllike/string.h> +#include <vespa/vespalib/util/size_literals.h> +#include <vespa/vespalib/data/databuffer.h> +#include <vespa/fastos/file.h> + +using namespace vespalib; + +TEST("that DirectIOException propagates the correct information.") { + const char *msg("The buffer"); + DirectIOException e("file.a", msg, 10, 3); + EXPECT_EQUAL(10u, e.getLength()); + EXPECT_EQUAL(3u, e.getOffset()); + EXPECT_EQUAL(msg, e.getBuffer()); + EXPECT_EQUAL(0u, string(e.what()).find("DirectIO failed for file 'file.a' buffer=")); + EXPECT_EQUAL(string("file.a"), e.getFileName()); +} + +TEST("that DirectIOException is thrown on unaligned buf.") { + FastOS_File f("vespalib_directio_test_app"); + f.EnableDirectIO(); + EXPECT_TRUE(f.OpenReadOnly()); + DataBuffer buf(10000, 4_Ki); + bool caught(false); + try { + f.ReadBuf(buf.getFree()+1, 4_Ki, 0); + } catch (const DirectIOException & e) { + EXPECT_EQUAL(4_Ki, e.getLength()); + EXPECT_EQUAL(0u, e.getOffset()); + EXPECT_EQUAL(buf.getFree()+1, e.getBuffer()); + EXPECT_EQUAL(string(f.GetFileName()), e.getFileName()); + caught = true; + } + EXPECT_TRUE(caught); +} + +TEST("that DirectIOException is thrown on unaligned offset.") { + FastOS_File f("vespalib_directio_test_app"); + f.EnableDirectIO(); + EXPECT_TRUE(f.OpenReadOnly()); + DataBuffer buf(10000, 4_Ki); + bool caught(false); + try { + f.ReadBuf(buf.getFree(), 4_Ki, 1); + } catch (const DirectIOException & e) { + EXPECT_EQUAL(4_Ki, e.getLength()); + EXPECT_EQUAL(1u, e.getOffset()); + EXPECT_EQUAL(buf.getFree(), e.getBuffer()); + EXPECT_EQUAL(string(f.GetFileName()), e.getFileName()); + caught = true; + } + EXPECT_TRUE(caught); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/floatingpointtype/.gitignore b/vespalib/src/tests/floatingpointtype/.gitignore new file mode 100644 index 00000000000..abe8249f33a --- /dev/null +++ b/vespalib/src/tests/floatingpointtype/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +floatingpointtype_test +vespalib_floatingpointtype_test_app diff --git a/vespalib/src/tests/floatingpointtype/CMakeLists.txt b/vespalib/src/tests/floatingpointtype/CMakeLists.txt new file mode 100644 index 00000000000..3f0ec8eab69 --- /dev/null +++ b/vespalib/src/tests/floatingpointtype/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_floatingpointtype_test_app TEST + SOURCES + floatingpointtypetest.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_floatingpointtype_test_app COMMAND vespalib_floatingpointtype_test_app) diff --git a/vespalib/src/tests/floatingpointtype/floatingpointtypetest.cpp b/vespalib/src/tests/floatingpointtype/floatingpointtypetest.cpp new file mode 100644 index 00000000000..d26385f23bf --- /dev/null +++ b/vespalib/src/tests/floatingpointtype/floatingpointtypetest.cpp @@ -0,0 +1,73 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/objects/floatingpointtype.h> + +class Test : public vespalib::TestApp +{ +public: + void testFloatingPoint(); + int Main() override; +}; + +void +Test::testFloatingPoint() +{ + vespalib::Double d1(1.0); + vespalib::Double d2(1.000000000000001); + vespalib::Double d3(-1.00000000000001); + vespalib::Double d4(4.0); + + EXPECT_TRUE(d1.getValue() != d2.getValue()); + + EXPECT_EQUAL(d1, d2); + EXPECT_EQUAL(d2, d1); + + EXPECT_NOT_EQUAL(d1, d3); + EXPECT_NOT_EQUAL(d1, d4); + + EXPECT_TRUE(d1 - d2 == 0); + EXPECT_TRUE(d2 - d1 == 0); + + EXPECT_TRUE(d1 - 1 == 0); + EXPECT_TRUE(d1 + 1 != 0); + + EXPECT_TRUE(d2 * d4 == 4.0); + EXPECT_TRUE(d2 / d4 == 0.25); + + EXPECT_TRUE(d1 >= 1); + EXPECT_TRUE(d1 <= 1); + EXPECT_TRUE(!(d1 < 1)); + EXPECT_TRUE(!(d1 > 1)); + + EXPECT_EQUAL(d2 * 4, d4); + + EXPECT_EQUAL(++d4, 5.0); + EXPECT_EQUAL(d4++, 5.0); + EXPECT_EQUAL(d4, 6.0); + + d4 /= 3; + EXPECT_EQUAL(d4, 2.00000000001); + d4 *= 2; + EXPECT_EQUAL(d4, 4.000000000001); + + EXPECT_EQUAL(--d4, 3.0); + EXPECT_EQUAL(d4--, 3.0); + EXPECT_EQUAL(d4, 2.0); + d4 /= 0.50000000001; + + EXPECT_EQUAL(d4, 4.0); + + EXPECT_TRUE(!(d3 + 1 > 0)); + EXPECT_TRUE(!(d3 + 1 < 0)); +} + +int +Test::Main() +{ + TEST_INIT("floatingpointtype_test"); + testFloatingPoint(); + TEST_DONE(); +} + +TEST_APPHOOK(Test) diff --git a/vespalib/src/tests/growablebytebuffer/.gitignore b/vespalib/src/tests/growablebytebuffer/.gitignore new file mode 100644 index 00000000000..76218df9168 --- /dev/null +++ b/vespalib/src/tests/growablebytebuffer/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +growablebytebuffer_test +vespalib_growablebytebuffer_test_app diff --git a/vespalib/src/tests/growablebytebuffer/CMakeLists.txt b/vespalib/src/tests/growablebytebuffer/CMakeLists.txt new file mode 100644 index 00000000000..b518206ae56 --- /dev/null +++ b/vespalib/src/tests/growablebytebuffer/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_growablebytebuffer_test_app TEST + SOURCES + growablebytebuffer_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_growablebytebuffer_test_app COMMAND vespalib_growablebytebuffer_test_app) diff --git a/vespalib/src/tests/growablebytebuffer/growablebytebuffer_test.cpp b/vespalib/src/tests/growablebytebuffer/growablebytebuffer_test.cpp new file mode 100644 index 00000000000..0a616745023 --- /dev/null +++ b/vespalib/src/tests/growablebytebuffer/growablebytebuffer_test.cpp @@ -0,0 +1,37 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/growablebytebuffer.h> + +using namespace vespalib; + +class Test : public TestApp +{ +public: + void testGrowing(); + int Main() override; +}; + +void +Test::testGrowing() +{ + GrowableByteBuffer buf(10); + + buf.putInt(3); + buf.putInt(7); + buf.putLong(1234); + buf.putDouble(1234); + buf.putString("hei der"); + + EXPECT_EQUAL(35u, buf.position()); +} + +int +Test::Main() +{ + TEST_INIT("guard_test"); + testGrowing(); + TEST_DONE(); +} + +TEST_APPHOOK(Test) diff --git a/vespalib/src/tests/json/.gitignore b/vespalib/src/tests/json/.gitignore new file mode 100644 index 00000000000..9918fbce6e8 --- /dev/null +++ b/vespalib/src/tests/json/.gitignore @@ -0,0 +1 @@ +vespalib_json_test_app diff --git a/vespalib/src/tests/json/CMakeLists.txt b/vespalib/src/tests/json/CMakeLists.txt new file mode 100644 index 00000000000..0ea216b189b --- /dev/null +++ b/vespalib/src/tests/json/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_json_test_app TEST + SOURCES + json.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_json_test_app COMMAND vespalib_json_test_app boost) diff --git a/vespalib/src/tests/json/json.cpp b/vespalib/src/tests/json/json.cpp new file mode 100644 index 00000000000..1a707ae1776 --- /dev/null +++ b/vespalib/src/tests/json/json.cpp @@ -0,0 +1,470 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/vespalib/testkit/testapp.h> + +#include <vespa/vespalib/util/jsonstream.h> +#include <vespa/vespalib/util/jsonexception.h> +#include <vespa/vespalib/stllike/asciistream.h> + +using namespace vespalib; + +class JSONTest : public vespalib::TestApp +{ +private: + void testJSONWriterValues(); + void testJSONWriterObject(); + void testJSONWriterArray(); + void testJSONWriterComplex(); + void testJsonStream(); + void testJsonStreamErrors(); + void testJsonStreamStateReporting(); + +public: + int Main() override; +}; + +void +JSONTest::testJSONWriterValues() +{ + JSONStringer js; + + { // bool + js.appendBool(true); + EXPECT_EQUAL(js.toString(), "true"); + js.clear().appendBool(false); + EXPECT_EQUAL(js.toString(), "false"); + } + { // double + js.clear().appendDouble(1234.5678); + EXPECT_EQUAL(js.toString(), "1234.5678"); + js.clear().appendDouble(-1234.5678); + EXPECT_EQUAL(js.toString(), "-1234.5678"); + js.clear().appendDouble(0.0); + EXPECT_EQUAL(js.toString(), "0.0"); + js.clear().appendDouble(0.00000000012345678912356789123456789); + EXPECT_EQUAL(js.toString(), "1.234567891235679e-10"); + js.clear().appendDouble(std::numeric_limits<double>::max()); + EXPECT_EQUAL(js.toString(), "1.797693134862316e+308"); + js.clear().appendDouble(std::numeric_limits<double>::min()); + EXPECT_EQUAL(js.toString(), "2.225073858507201e-308"); + js.clear().appendDouble(1.0 * (uint64_t(1) << 53)); + EXPECT_EQUAL(js.toString(), "9007199254740992.0"); + js.clear().appendDouble(1000); + EXPECT_EQUAL(js.toString(), "1000.0"); + } + { // float + js.clear().appendFloat(1234.5678f); + EXPECT_EQUAL(js.toString(), "1234.5677"); + js.clear().appendFloat(-1234.5678f); + EXPECT_EQUAL(js.toString(), "-1234.5677"); + js.clear().appendFloat(0.0f); + EXPECT_EQUAL(js.toString(), "0.0"); + js.clear().appendFloat(0.00000000012345678912356789123456789f); + EXPECT_EQUAL(js.toString(), "1.2345679e-10"); + js.clear().appendFloat(std::numeric_limits<float>::max()); + EXPECT_EQUAL(js.toString(), "3.4028235e+38"); + js.clear().appendFloat(std::numeric_limits<float>::min()); + EXPECT_EQUAL(js.toString(), "1.1754944e-38"); + js.clear().appendFloat(1.0 * (uint64_t(1) << 24)); + EXPECT_EQUAL(js.toString(), "16777216.0"); + js.clear().appendFloat(1000); + EXPECT_EQUAL(js.toString(), "1000.0"); + } + { // long + js.clear().appendInt64(4294967296ll); + EXPECT_EQUAL(js.toString(), "4294967296"); + js.clear().appendInt64(-4294967296ll); + EXPECT_EQUAL(js.toString(), "-4294967296"); + } + { // string + js.clear().appendString("string"); + EXPECT_EQUAL(js.toString(), "\"string\""); + } + { // NULL + js.clear().appendNull(); + EXPECT_EQUAL(js.toString(), "null"); + } + { // quote + js.clear().appendString("x\"y"); + EXPECT_EQUAL(js.toString(), "\"x\\\"y\""); + js.clear().appendString("x\\y"); + EXPECT_EQUAL(js.toString(), "\"x\\\\y\""); + js.clear().appendString("x/y"); + EXPECT_EQUAL(js.toString(), "\"x/y\""); + js.clear().appendString("x\by"); + EXPECT_EQUAL(js.toString(), "\"x\\by\""); + js.clear().appendString("x\fy"); + EXPECT_EQUAL(js.toString(), "\"x\\fy\""); + js.clear().appendString("x\ny"); + EXPECT_EQUAL(js.toString(), "\"x\\ny\""); + js.clear().appendString("x\ry"); + EXPECT_EQUAL(js.toString(), "\"x\\ry\""); + js.clear().appendString("x\ty"); + EXPECT_EQUAL(js.toString(), "\"x\\ty\""); + } +} + +void +JSONTest::testJSONWriterObject() +{ + JSONStringer js; + + { // single pair + js.beginObject().appendKey("k1").appendInt64(1l).endObject(); + EXPECT_EQUAL(js.toString(), "{\"k1\":1}"); + } + { // multiple pairs + js.clear().beginObject().appendKey("k1").appendInt64(1l).appendKey("k2").appendInt64(2l).endObject(); + EXPECT_EQUAL(js.toString(), "{\"k1\":1,\"k2\":2}"); + } + { // object in object + js.clear().beginObject().appendKey("k1").beginObject().appendKey("k1.1").appendInt64(11l).endObject().endObject(); + EXPECT_EQUAL(js.toString(), "{\"k1\":{\"k1.1\":11}}"); + } + { // object in object (multiple pairs) + js.clear().beginObject(). + appendKey("k1"). + beginObject(). + appendKey("k1.1").appendInt64(11l). + appendKey("k1.2").appendInt64(12l). + endObject(). + appendKey("k2"). + beginObject(). + appendKey("k2.1").appendInt64(21l). + appendKey("k2.2").appendInt64(22l). + endObject(). + endObject(); + EXPECT_EQUAL(js.toString(), "{\"k1\":{\"k1.1\":11,\"k1.2\":12},\"k2\":{\"k2.1\":21,\"k2.2\":22}}"); + } + { // array in object + js.clear().beginObject().appendKey("k1"). + beginArray().appendInt64(1l).appendInt64(2l).endArray().endObject(); + EXPECT_EQUAL(js.toString(), "{\"k1\":[1,2]}"); + } + { // array in object (multiple pairs) + js.clear().beginObject(). + appendKey("k1").beginArray().appendInt64(1l).appendInt64(2l).endArray(). + appendKey("k2").beginArray().appendInt64(3l).appendInt64(4l).endArray(). + endObject(); + EXPECT_EQUAL(js.toString(), "{\"k1\":[1,2],\"k2\":[3,4]}"); + } +} + + +void +JSONTest::testJSONWriterArray() +{ + JSONStringer js; + + { // single element + js.beginArray().appendInt64(1l).endArray(); + EXPECT_EQUAL(js.toString(), "[1]"); + } + { // multiple elements + js.clear().beginArray().appendInt64(1l).appendInt64(2l).endArray(); + EXPECT_EQUAL(js.toString(), "[1,2]"); + } + { // array in array + js.clear().beginArray().beginArray().appendInt64(1l).endArray().endArray(); + EXPECT_EQUAL(js.toString(), "[[1]]"); + } + { // array in array (multiple elements) + js.clear().beginArray(). + beginArray().appendInt64(1l).appendInt64(2l).endArray(). + beginArray().appendInt64(3l).appendInt64(4l).endArray(). + endArray(); + EXPECT_EQUAL(js.toString(), "[[1,2],[3,4]]"); + } + { // object in array + js.clear().beginArray(). + beginObject().appendKey("k1").appendInt64(1l).endObject(). + endArray(); + EXPECT_EQUAL(js.toString(), "[{\"k1\":1}]"); + } + { // object in array (multiple elements) + js.clear().beginArray(). + beginObject().appendKey("k1").appendInt64(1l).appendKey("k2").appendInt64(2l).endObject(). + beginObject().appendKey("k3").appendInt64(3l).appendKey("k4").appendInt64(4l).endObject(). + endArray(); + EXPECT_EQUAL(js.toString(), "[{\"k1\":1,\"k2\":2},{\"k3\":3,\"k4\":4}]"); + } +} + + +void +JSONTest::testJSONWriterComplex() +{ + JSONStringer js; + + js.beginObject(); + { // object + js.appendKey("k1"); + js.beginObject(); + { + js.appendKey("k1.1"); + js.appendInt64(1l); + } + { + js.appendKey("k1.2"); + js.beginArray(); + js.appendInt64(2l); + js.appendInt64(3l); + js.endArray(); + } + js.endObject(); + } + { // object of object + js.appendKey("k2"); + js.beginObject(); + { + js.appendKey("k2.1"); + js.beginObject(); + { + js.appendKey("k2.1.1"); + js.appendInt64(4l); + } + { + js.appendKey("k2.1.2"); + js.beginArray(); + js.appendInt64(5l); + js.appendInt64(6l); + js.endArray(); + } + js.endObject(); + } + js.endObject(); + } + { // array of object + js.appendKey("k3"); + js.beginArray(); + { + js.beginObject(); + { + js.appendKey("k3.1"); + js.appendInt64(7l); + } + { + js.appendKey("k3.2"); + js.beginArray(); + js.appendInt64(8l); + js.appendInt64(9l); + js.endArray(); + } + js.endObject(); + } + { + js.beginObject(); + { + js.appendKey("k3.1"); + js.appendInt64(10l); + } + { + js.appendKey("k3.2"); + js.beginArray(); + js.appendInt64(11l); + js.appendInt64(12l); + js.endArray(); + } + js.endObject(); + } + js.endArray(); + } + js.endObject(); + EXPECT_EQUAL(js.toString(), "{\"k1\":{\"k1.1\":1,\"k1.2\":[2,3]},\"k2\":{\"k2.1\":{\"k2.1.1\":4,\"k2.1.2\":[5,6]}},\"k3\":[{\"k3.1\":7,\"k3.2\":[8,9]},{\"k3.1\":10,\"k3.2\":[11,12]}]}"); +} + +namespace { + struct Builder : public vespalib::JsonStreamTypes { + void build(JsonStream& s) { + s << Object() << "k1" << Object() + << "k1.1" << 1 + << "k1.2" << Array() + << 2l << 3ll << End() + << End() + << "k2" << Object() + << "k2.1" << Object() + << "k2.1.1" << 4u + << "k2.1.2" << Array() + << 5ul << 6ull << End() + << End() + << End() + << "k3" << Array() + << Object() + << "k3.1" << -7 + << "k3.2" << Array() + << -8l << -9ll << End() + << End() + << Object() + << "k3.1" << 10l + << "k3.2" << Array() + << 11l << 12l << End() + << End() + << End() + << End(); + } + }; +} + +void +JSONTest::testJsonStream() +{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + Builder b; + b.build(stream); + stream.finalize(); + EXPECT_EQUAL(as.str(), "{\"k1\":{\"k1.1\":1,\"k1.2\":[2,3]},\"k2\":{\"k2.1\":{\"k2.1.1\":4,\"k2.1.2\":[5,6]}},\"k3\":[{\"k3.1\":-7,\"k3.2\":[-8,-9]},{\"k3.1\":10,\"k3.2\":[11,12]}]}"); +} + +void +JSONTest::testJsonStreamErrors() +{ + using namespace vespalib::jsonstream; + // Unsupported object keys + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << Object(); + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: An object value cannot be an object key ({}(ObjectExpectingKey))", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << true; + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: A bool value cannot be an object key ({}(ObjectExpectingKey))", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << 13; + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: An int64_t value cannot be an object key ({}(ObjectExpectingKey))", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << uint64_t(13); + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: A uint64_t value cannot be an object key ({}(ObjectExpectingKey))", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << 0.5; + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: A double value cannot be an object key ({}(ObjectExpectingKey))", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << jsonstream::Array(); + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: An array value cannot be an object key ({}(ObjectExpectingKey))", e.getReason()); + } + // Invalid points to add End() + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << "foo" << End(); + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: Object got key but not value. Cannot end it now ({foo}(ObjectExpectingValue))", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << End(); + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: No tag to end. At root ((RootExpectingArrayOrObjectStart))", e.getReason()); + } + // Adding to finalized stream + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << End() << "foo"; + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: Stream already finalized. Can't add a string value. (Finalized)", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << End() << false; + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: Stream already finalized. Can't add a bool value. (Finalized)", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << End() << 13; + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: Stream already finalized. Can't add a long long value. (Finalized)", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << End() << 13u; + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: Stream already finalized. Can't add an unsigned long long value. (Finalized)", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << End() << 0.2; + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: Stream already finalized. Can't add a double value. (Finalized)", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << End() << Object(); + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: Stream already finalized. Can't start a new object. (Finalized)", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << End() << jsonstream::Array(); + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: Stream already finalized. Can't start a new array. (Finalized)", e.getReason()); + } + try{ + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << Object() << End() << End(); + } catch (vespalib::JsonStreamException& e) { + EXPECT_EQUAL("Invalid state on call: Stream already finalized. Can't end it. (Finalized)", e.getReason()); + } +} + +void +JSONTest::testJsonStreamStateReporting() +{ + using namespace vespalib::jsonstream; + vespalib::asciistream as; + vespalib::JsonStream stream(as); + stream << jsonstream::Array() << 13 + << "foo" + << Object() << "key" << "value" << End() + << false + << End(); + EXPECT_EQUAL("Current: Finalized", stream.getJsonStreamState()); +} + +int +JSONTest::Main() +{ + TEST_INIT("json_test"); + + testJSONWriterValues(); + testJSONWriterObject(); + testJSONWriterArray(); + testJSONWriterComplex(); + testJsonStream(); + testJsonStreamErrors(); + testJsonStreamStateReporting(); + + TEST_DONE(); +} + +TEST_APPHOOK(JSONTest); + diff --git a/vespalib/src/tests/memorydatastore/.gitignore b/vespalib/src/tests/memorydatastore/.gitignore new file mode 100644 index 00000000000..6809bff5d3d --- /dev/null +++ b/vespalib/src/tests/memorydatastore/.gitignore @@ -0,0 +1 @@ +vespalib_memorydatastore_test_app diff --git a/vespalib/src/tests/memorydatastore/CMakeLists.txt b/vespalib/src/tests/memorydatastore/CMakeLists.txt new file mode 100644 index 00000000000..65d9231455a --- /dev/null +++ b/vespalib/src/tests/memorydatastore/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_memorydatastore_test_app TEST + SOURCES + memorydatastore.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_memorydatastore_test_app COMMAND vespalib_memorydatastore_test_app) diff --git a/vespalib/src/tests/memorydatastore/memorydatastore.cpp b/vespalib/src/tests/memorydatastore/memorydatastore.cpp new file mode 100644 index 00000000000..1d49b0af91b --- /dev/null +++ b/vespalib/src/tests/memorydatastore/memorydatastore.cpp @@ -0,0 +1,72 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/data/memorydatastore.h> +#include <vespa/vespalib/stllike/asciistream.h> + +using namespace vespalib; + +class MemoryDataStoreTest : public vespalib::TestApp +{ +private: + void testMemoryDataStore(); + void testVariableSizeVector(); +public: + int Main() override; +}; + +void +MemoryDataStoreTest::testMemoryDataStore() +{ + MemoryDataStore s(alloc::Alloc::alloc(256)); + std::vector<MemoryDataStore::Reference> v; + v.push_back(s.push_back("mumbo", 5)); + for (size_t i(0); i < 50; i++) { + v.push_back(s.push_back("mumbo", 5)); + EXPECT_EQUAL(static_cast<const char *>(v[i].data()) + 5, v[i+1].data()); + } + v.push_back(s.push_back("mumbo", 5)); + EXPECT_EQUAL(52ul, v.size()); + EXPECT_NOT_EQUAL(static_cast<const char *>(v[50].data()) + 5, v[51].data()); + for (size_t i(0); i < v.size(); i++) { + EXPECT_EQUAL(0, memcmp("mumbo", v[i].data(), 5)); + } +} + +void +MemoryDataStoreTest::testVariableSizeVector() +{ + VariableSizeVector v(20000, 5*20000); + for (size_t i(0); i < 10000; i++) { + asciistream os; + os << i; + v.push_back(os.str().data(), os.str().size()); + } + for (size_t i(0); i < v.size(); i++) { + asciistream os; + os << i; + EXPECT_EQUAL(os.str().size(), v[i].size()); + EXPECT_EQUAL(0, memcmp(os.str().data(), v[i].data(), os.str().size())); + } + size_t i(0); + for (auto it(v.begin()), mt(v.end()); it != mt; it++, i++) { + asciistream os; + os << i; + EXPECT_EQUAL(os.str().size(), it->size()); + EXPECT_EQUAL(0, memcmp(os.str().data(), (*it).data(), os.str().size())); + } + +} + +int +MemoryDataStoreTest::Main() +{ + TEST_INIT("data_test"); + testMemoryDataStore(); + testVariableSizeVector(); + + TEST_DONE(); +} + +TEST_APPHOOK(MemoryDataStoreTest); + diff --git a/vespalib/src/tests/objects/identifiable/.gitignore b/vespalib/src/tests/objects/identifiable/.gitignore new file mode 100644 index 00000000000..a547ace8ee4 --- /dev/null +++ b/vespalib/src/tests/objects/identifiable/.gitignore @@ -0,0 +1,5 @@ +.depend +Makefile +asciistream_test +identifiable_test +vespalib_identifiable_test_app diff --git a/vespalib/src/tests/objects/identifiable/CMakeLists.txt b/vespalib/src/tests/objects/identifiable/CMakeLists.txt new file mode 100644 index 00000000000..c4aefa44350 --- /dev/null +++ b/vespalib/src/tests/objects/identifiable/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_identifiable_test_app TEST + SOURCES + identifiable_test.cpp + namedobject.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_identifiable_test_app COMMAND vespalib_identifiable_test_app) diff --git a/vespalib/src/tests/objects/identifiable/identifiable_test.cpp b/vespalib/src/tests/objects/identifiable/identifiable_test.cpp new file mode 100644 index 00000000000..b3adfbfa9e2 --- /dev/null +++ b/vespalib/src/tests/objects/identifiable/identifiable_test.cpp @@ -0,0 +1,338 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "namedobject.h" +#include <vespa/vespalib/objects/identifiable.hpp> +#include <vespa/vespalib/objects/nbostream.h> +#include <vespa/vespalib/util/exceptions.h> +#include <vespa/vespalib/testkit/testapp.h> + +using namespace vespalib; + +class IdentifiableTest : public TestApp { + void requireThatIdentifiableCastCanCastPointers(); + void requireThatIdentifiableCastCanCastReferences(); + void testNamedObject(); + void testNboStream(); + template <typename T> + void testStream(const T & a); + void testNboSerializer(); + template <typename T> + void testSerializer(const T & a); +public: + int Main() override; +}; + +#define CID_Abstract 0x700000 +#define CID_A 0x700001 +#define CID_B 0x700002 +#define CID_C 0x700003 + +class Abstract : public Identifiable +{ +public: + DECLARE_IDENTIFIABLE_ABSTRACT(Abstract); + virtual ~Abstract() { } + virtual void someAbstractVirtualMethod() = 0; +}; + +class A : public Abstract +{ +public: + DECLARE_IDENTIFIABLE(A); + A() { } + void someAbstractVirtualMethod() override { }; +}; + +class B : public A +{ +public: + DECLARE_IDENTIFIABLE(B); + B() { } +}; + +class C : public Identifiable +{ +private: + int _value; + +public: + DECLARE_IDENTIFIABLE(C); + C() : _value(0) {} + C(int value) : _value(value) {} + C *clone() const { return new C(*this); } + virtual int cmp(const Identifiable &rhs) const { + int result(cmpClassId(rhs)); + if (result == 0) { + result = _value - static_cast<const C &>(rhs)._value; + } + return result; + } +}; + +IMPLEMENT_IDENTIFIABLE_ABSTRACT(Abstract, Identifiable); +IMPLEMENT_IDENTIFIABLE(A, Abstract); +IMPLEMENT_IDENTIFIABLE(B, A); +IMPLEMENT_IDENTIFIABLE(C, Identifiable); + +void +IdentifiableTest::testNamedObject() +{ + NamedObject a("first"), b("second");; + nbostream os; + NBOSerializer nos(os); + nos << a << b; + EXPECT_EQUAL(27u,os.size()); + Identifiable::UP o1; + o1 = Identifiable::create(nos); + EXPECT_EQUAL(14u, os.size()); + ASSERT_TRUE(o1->inherits(NamedObject::classId)); + ASSERT_TRUE(o1->getClass().id() == NamedObject::classId); + EXPECT_TRUE(static_cast<const NamedObject &>(*o1).getName() == "first"); + o1 = Identifiable::create(nos); + EXPECT_EQUAL(0u, os.size()); + ASSERT_TRUE(o1->inherits(NamedObject::classId)); + ASSERT_TRUE(o1->getClass().id() == NamedObject::classId); + EXPECT_TRUE(static_cast<const NamedObject &>(*o1).getName() == "second"); +} + +template <typename T> +void IdentifiableTest::testStream(const T & a) +{ + nbostream s; + s << a; + T b; + s >> b; + EXPECT_TRUE(s.empty()); + EXPECT_EQUAL(a, b); + EXPECT_EQUAL(nbostream::ok, s.state()); + EXPECT_TRUE(s.good()); +} + +template <typename T> +void IdentifiableTest::testSerializer(const T & a) +{ + nbostream t; + NBOSerializer s(t); + s << a; + T b; + s >> b; + EXPECT_TRUE(s.getStream().empty()); + EXPECT_EQUAL(a, b); + EXPECT_EQUAL(nbostream::ok, s.getStream().state()); +} + +void IdentifiableTest::testNboSerializer() +{ + testSerializer(true); + testSerializer(false); + testSerializer(static_cast<int8_t>('a')); + testSerializer(static_cast<uint8_t>(156)); + testSerializer(static_cast<int16_t>(156)); + testSerializer(static_cast<int32_t>(156)); + testSerializer(static_cast<int64_t>(156)); + testSerializer(static_cast<uint16_t>(156)); + testSerializer(static_cast<uint32_t>(156)); + testSerializer(static_cast<uint64_t>(156)); + testSerializer(static_cast<float>(156)); + testSerializer(static_cast<double>(156)); + testSerializer(vespalib::string("abcdefgh")); +} + +void IdentifiableTest::testNboStream() +{ + testStream(true); + testStream(false); + testStream('a'); + testStream(static_cast<unsigned char>(156)); + testStream(static_cast<int16_t>(156)); + testStream(static_cast<int32_t>(156)); + testStream(static_cast<int64_t>(156)); + testStream(static_cast<uint16_t>(156)); + testStream(static_cast<uint32_t>(156)); + testStream(static_cast<uint64_t>(156)); + testStream(static_cast<float>(156)); + testStream(static_cast<double>(156)); + testStream(std::string("abcdefgh")); + testStream(vespalib::string("abcdefgh")); + { + nbostream s(4); + EXPECT_EQUAL(4u, s.capacity()); + s << "abcdef"; + EXPECT_EQUAL(nbostream::ok, s.state()); + EXPECT_EQUAL(10u, s.size()); + EXPECT_EQUAL(16u, s.capacity()); + EXPECT_EQUAL(0, strncmp(s.data() + 4, "abcdef", 6)); + } + { + nbostream s(8); + EXPECT_EQUAL(0u, s.size()); + EXPECT_EQUAL(8u, s.capacity()); + const char * prev = s.data(); + s << "ABCD"; + EXPECT_EQUAL(8u, s.size()); + EXPECT_EQUAL(8u, s.capacity()); + EXPECT_EQUAL(prev, s.data()); + s << "A long string that will cause resizing"; + EXPECT_EQUAL(50u, s.size()); + EXPECT_EQUAL(64u, s.capacity()); + EXPECT_NOT_EQUAL(prev, s.data()); + } + { + nbostream s(8); + EXPECT_EQUAL(0u, s.size()); + EXPECT_EQUAL(8u, s.capacity()); + const char * prev = s.data(); + s << "ABCD"; + EXPECT_EQUAL(8u, s.size()); + EXPECT_EQUAL(8u, s.capacity()); + EXPECT_EQUAL(prev, s.data()); + s.reserve(50); + EXPECT_NOT_EQUAL(prev, s.data()); + EXPECT_EQUAL(8u, s.size()); + EXPECT_EQUAL(64u, s.capacity()); + prev = s.data(); + s << "A long string that will cause resizing"; + EXPECT_EQUAL(50u, s.size()); + EXPECT_EQUAL(64u, s.capacity()); + EXPECT_EQUAL(prev, s.data()); + } + { + nbostream s; + s << int64_t(9); + EXPECT_EQUAL(8u, s.size()); + EXPECT_EQUAL(0u, s.rp()); + int64_t a(7), b(1); + s >> a; + EXPECT_EQUAL(0u, s.size()); + EXPECT_EQUAL(8u, s.rp()); + EXPECT_TRUE(s.empty()); + EXPECT_TRUE(s.good()); + EXPECT_EQUAL(9, a); + try { + s >> b; + EXPECT_TRUE(false); + } catch (const IllegalStateException & e) { + EXPECT_EQUAL("Stream failed bufsize(1024), readp(8), writep(8)", e.getMessage()); + } + EXPECT_EQUAL(0u, s.size()); + EXPECT_EQUAL(8u, s.rp()); + EXPECT_TRUE(s.empty()); + EXPECT_FALSE(s.good()); + EXPECT_EQUAL(1, b); + EXPECT_EQUAL(nbostream::eof, s.state()); + } +} + +int +IdentifiableTest::Main() +{ + TEST_INIT("identifiable_test"); + + TEST_DO(requireThatIdentifiableCastCanCastPointers()); + TEST_DO(requireThatIdentifiableCastCanCastReferences()); + testNamedObject(); + testNboStream(); + testNboSerializer(); + + A a; + B b; + + const Identifiable::RuntimeClass & rtcA = a.getClass(); + EXPECT_EQUAL(rtcA.id(), static_cast<unsigned int>(A::classId)); + EXPECT_EQUAL(strcmp(rtcA.name(), "A"), 0); + + const Identifiable::RuntimeClass & rtcB = b.getClass(); + EXPECT_EQUAL(rtcB.id(), static_cast<unsigned int>(B::classId)); + EXPECT_EQUAL(strcmp(rtcB.name(), "B"), 0); + + const Identifiable::RuntimeClass * rt(Identifiable::classFromId(0x1ab76245)); + ASSERT_TRUE(rt == NULL); + rt = Identifiable::classFromId(Abstract::classId); + ASSERT_TRUE(rt != NULL); + Identifiable * u = rt->create(); + ASSERT_TRUE(u == NULL); + rt = Identifiable::classFromId(A::classId); + ASSERT_TRUE(rt != NULL); + rt = Identifiable::classFromId(B::classId); + ASSERT_TRUE(rt != NULL); + + Identifiable * o = rt->create(); + ASSERT_TRUE(o != NULL); + + const Identifiable::RuntimeClass & rtc = o->getClass(); + ASSERT_TRUE(rtc.id() == B::classId); + ASSERT_TRUE(strcmp(rtc.name(), "B") == 0); + ASSERT_TRUE(o->inherits(B::classId)); + ASSERT_TRUE(o->inherits(A::classId)); + ASSERT_TRUE(o->inherits(Abstract::classId)); + ASSERT_TRUE(o->inherits(Identifiable::classId)); + ASSERT_TRUE(o->getClass().id() == B::classId); + nbostream os; + NBOSerializer nos(os); + nos << *o; + EXPECT_EQUAL(os.size(), 4u); + Identifiable::UP o2 = Identifiable::create(nos); + EXPECT_TRUE(os.empty()); + ASSERT_TRUE(o->inherits(B::classId)); + ASSERT_TRUE(o->getClass().id() == B::classId); + delete o; + + rt = Identifiable::classFromName("NotBNorA"); + ASSERT_TRUE(rt == NULL); + rt = Identifiable::classFromName("B"); + ASSERT_TRUE(rt != NULL); + o = rt->create(); + ASSERT_TRUE(o != NULL); + const Identifiable::RuntimeClass & rtc2 = o->getClass(); + ASSERT_TRUE(rtc2.id() == B::classId); + ASSERT_TRUE(strcmp(rtc2.name(), "B") == 0); + ASSERT_TRUE(o->inherits(B::classId)); + ASSERT_TRUE(o->inherits(A::classId)); + ASSERT_TRUE(o->inherits(Abstract::classId)); + ASSERT_TRUE(o->inherits(Identifiable::classId)); + ASSERT_TRUE(o->getClass().id() == B::classId); + delete o; + + IdentifiablePtr<C> c0(NULL); + IdentifiablePtr<C> c1(new C(10)); + IdentifiablePtr<C> c2(new C(20)); + + EXPECT_LESS(c0.cmp(c1), 0); + EXPECT_EQUAL(c0.cmp(c0), 0); + EXPECT_GREATER(c1.cmp(c0), 0); + + EXPECT_LESS(c1.cmp(c2), 0); + EXPECT_EQUAL(c1.cmp(c1), 0); + EXPECT_GREATER(c2.cmp(c1), 0); + + TEST_DONE(); +} + +void IdentifiableTest::requireThatIdentifiableCastCanCastPointers() { + A a; + B b; + EXPECT_TRUE(Identifiable::cast<A *>(&a)); + EXPECT_TRUE(Identifiable::cast<A *>(&b)); + EXPECT_TRUE(!Identifiable::cast<B *>(&a)); + EXPECT_TRUE(Identifiable::cast<B *>(&b)); + EXPECT_TRUE(Identifiable::cast<Abstract *>(&a)); + EXPECT_TRUE(Identifiable::cast<Abstract *>(&b)); +} + +void IdentifiableTest::requireThatIdentifiableCastCanCastReferences() { + A a; + B b; + try { + // These should not throw. + Identifiable::cast<A &>(a); + Identifiable::cast<A &>(b); + Identifiable::cast<B &>(b); + Identifiable::cast<Abstract &>(a); + Identifiable::cast<Abstract &>(b); + } catch (std::bad_cast &e) { + TEST_FATAL(e.what()); + } + EXPECT_EXCEPTION(Identifiable::cast<B &>(a), std::bad_cast, "bad_cast"); +} + +TEST_APPHOOK(IdentifiableTest) diff --git a/vespalib/src/tests/objects/identifiable/namedobject.cpp b/vespalib/src/tests/objects/identifiable/namedobject.cpp new file mode 100644 index 00000000000..3e8d3291177 --- /dev/null +++ b/vespalib/src/tests/objects/identifiable/namedobject.cpp @@ -0,0 +1,19 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include "namedobject.h" + +namespace vespalib { + +IMPLEMENT_IDENTIFIABLE_NS(vespalib, NamedObject, Identifiable); + + +Serializer & NamedObject::onSerialize(Serializer & os) const +{ + return os.put(_name); +} + +Deserializer & NamedObject::onDeserialize(Deserializer & is) +{ + return is.get(_name); +} + +} diff --git a/vespalib/src/tests/objects/identifiable/namedobject.h b/vespalib/src/tests/objects/identifiable/namedobject.h new file mode 100644 index 00000000000..784715a66f6 --- /dev/null +++ b/vespalib/src/tests/objects/identifiable/namedobject.h @@ -0,0 +1,23 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include <vespa/vespalib/objects/identifiable.h> +#include <string> + +namespace vespalib +{ + +class NamedObject : public Identifiable +{ +public: + DECLARE_IDENTIFIABLE_NS(vespalib, NamedObject); + DECLARE_NBO_SERIALIZE; + NamedObject() : _name() { } + NamedObject(const string & name) : _name(name) { } + const string & getName() const { return _name; } +private: + string _name; +}; + +} + diff --git a/vespalib/src/tests/objects/objectdump/.gitignore b/vespalib/src/tests/objects/objectdump/.gitignore new file mode 100644 index 00000000000..6ddd515391d --- /dev/null +++ b/vespalib/src/tests/objects/objectdump/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +objectdump_test +vespalib_objectdump_test_app diff --git a/vespalib/src/tests/objects/objectdump/CMakeLists.txt b/vespalib/src/tests/objects/objectdump/CMakeLists.txt new file mode 100644 index 00000000000..67395998b39 --- /dev/null +++ b/vespalib/src/tests/objects/objectdump/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_objectdump_test_app TEST + SOURCES + objectdump.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_objectdump_test_app COMMAND vespalib_objectdump_test_app) diff --git a/vespalib/src/tests/objects/objectdump/objectdump.cpp b/vespalib/src/tests/objects/objectdump/objectdump.cpp new file mode 100644 index 00000000000..812b1e79e17 --- /dev/null +++ b/vespalib/src/tests/objects/objectdump/objectdump.cpp @@ -0,0 +1,115 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/objects/identifiable.h> +#include <vespa/vespalib/objects/visit.hpp> + +#define CID_Base 10000000 +#define CID_Foo 10000001 +#define CID_Bar 10000002 +#define CID_Baz 10000003 + +using vespalib::ObjectVisitor; +using vespalib::IdentifiablePtr; + +struct Base : public vespalib::Identifiable +{ + DECLARE_IDENTIFIABLE(Base); + virtual Base *clone() const { return new Base(*this); } +}; +IMPLEMENT_IDENTIFIABLE(Base, vespalib::Identifiable); + +struct Baz : public Base +{ + DECLARE_IDENTIFIABLE(Baz); + Baz *clone() const override { return new Baz(*this); } +}; +IMPLEMENT_IDENTIFIABLE(Baz, Base); + +struct Bar : public Base +{ + DECLARE_IDENTIFIABLE(Bar); + bool _bool; + int8_t _int8; + uint8_t _uint8; + int16_t _int16; + uint16_t _uint16; + int32_t _int32; + uint32_t _uint32; + int64_t _int64; + uint64_t _uint64; + float _float; + double _double; + vespalib::string _string; + Bar() : _bool(true), _int8(-1), _uint8(1), _int16(-2), _uint16(2), + _int32(-4), _uint32(4), _int64(-8), _uint64(8), + _float(2.5), _double(2.75), _string("bla bla") {} + + Bar *clone() const override { return new Bar(*this); } + + void visitMembers(ObjectVisitor &v) const override { + visit(v, "_bool", _bool); + visit(v, "_int8", _int8); + visit(v, "_uint8", _uint8); + visit(v, "_int16", _int16); + visit(v, "_uint16", _uint16); + visit(v, "_int32", _int32); + visit(v, "_uint32", _uint32); + visit(v, "_int64", _int64); + visit(v, "_uint64", _uint64); + visit(v, "_float", _float); + visit(v, "_double", _double); + visit(v, "_string", _string); + visit(v, "info", "a dummy string"); + visit(v, "(const char*)0", (const char*)0); + } +}; +IMPLEMENT_IDENTIFIABLE(Bar, Base); + +struct Foo : public Base +{ + DECLARE_IDENTIFIABLE(Foo); + Bar _objMember; + Baz _objMember2; + Baz *_objPtr; + std::vector<Bar> _list; + std::vector<IdentifiablePtr<Base> > _list2; + + Foo(); + ~Foo(); + Foo *clone() const override { return new Foo(*this); } + void visitMembers(ObjectVisitor &v) const override; +}; + +Foo::~Foo() { } +Foo::Foo() + : _objMember(), _objMember2(), _objPtr(0), _list(), _list2() +{ + _list.push_back(Bar()); + _list.push_back(Bar()); + _list.push_back(Bar()); + _list2.push_back(Bar()); + _list2.push_back(Baz()); +} + +void +Foo::visitMembers(ObjectVisitor &v) const { + visit(v, "_objMember", _objMember); + visit(v, "_objMember2", _objMember2); + visit(v, "_objPtr", _objPtr); + visit(v, "_list", _list); + visit(v, "_list2", _list2); +} + +IMPLEMENT_IDENTIFIABLE(Foo, Base); + +TEST_SETUP(Test); + +int +Test::Main() +{ + TEST_INIT("objectdump_test"); + Foo foo; + fprintf(stderr, "%s", foo.asString().c_str()); + TEST_DONE(); +} diff --git a/vespalib/src/tests/objects/objectselection/.gitignore b/vespalib/src/tests/objects/objectselection/.gitignore new file mode 100644 index 00000000000..f6aefd07270 --- /dev/null +++ b/vespalib/src/tests/objects/objectselection/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +objectselection_test +vespalib_objectselection_test_app diff --git a/vespalib/src/tests/objects/objectselection/CMakeLists.txt b/vespalib/src/tests/objects/objectselection/CMakeLists.txt new file mode 100644 index 00000000000..94f43078820 --- /dev/null +++ b/vespalib/src/tests/objects/objectselection/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_objectselection_test_app TEST + SOURCES + objectselection.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_objectselection_test_app COMMAND vespalib_objectselection_test_app) diff --git a/vespalib/src/tests/objects/objectselection/objectselection.cpp b/vespalib/src/tests/objects/objectselection/objectselection.cpp new file mode 100644 index 00000000000..aa9c841f2dc --- /dev/null +++ b/vespalib/src/tests/objects/objectselection/objectselection.cpp @@ -0,0 +1,94 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/objects/identifiable.hpp> +#include <vespa/vespalib/objects/objectpredicate.h> +#include <vespa/vespalib/objects/objectoperation.h> + +using namespace vespalib; + +#define CID_Foo 60000005 +#define CID_Bar 60000010 + +struct Foo : public Identifiable +{ + typedef IdentifiablePtr<Foo> CP; + std::vector<CP> nodes; + + DECLARE_IDENTIFIABLE(Foo); + virtual Foo *clone() const { return new Foo(*this); } + void selectMembers(const ObjectPredicate &p, ObjectOperation &o) override { + for (uint32_t i = 0; i < nodes.size(); ++i) { + nodes[i]->select(p, o); + } + } +}; +IMPLEMENT_IDENTIFIABLE(Foo, Identifiable); + +struct Bar : public Foo +{ + int value; + + DECLARE_IDENTIFIABLE(Bar); + Bar() : value(0) {} + Bar(int v) { value = v; } + Bar *clone() const override { return new Bar(*this); } +}; +IMPLEMENT_IDENTIFIABLE(Bar, Identifiable); + +struct ObjectType : public ObjectPredicate +{ + uint32_t cid; + ObjectType(uint32_t id) : cid(id) {} + bool check(const Identifiable &obj) const override { + return (obj.getClass().id() == cid); + } +}; + +struct ObjectCollect : public ObjectOperation +{ + std::vector<Identifiable*> nodes; + ~ObjectCollect() override; + void execute(Identifiable &obj) override { + nodes.push_back(&obj); + } +}; + +ObjectCollect::~ObjectCollect() = default; + +TEST_SETUP(Test); + +int +Test::Main() +{ + TEST_INIT("objectselection_test"); + { + Foo f1; + Foo f2; + Foo f3; + Bar b1(1); + Bar b2(2); + Bar b3(3); + Bar b4(4); + f2.nodes.push_back(b1); + f2.nodes.push_back(b2); + f3.nodes.push_back(b3); + f3.nodes.push_back(b4); + f1.nodes.push_back(f2); + f1.nodes.push_back(f3); + + ObjectType predicate(Bar::classId); + ObjectCollect operation; + f1.select(predicate, operation); + ASSERT_TRUE(operation.nodes.size() == 4); + ASSERT_TRUE(operation.nodes[0]->getClass().id() == Bar::classId); + ASSERT_TRUE(operation.nodes[1]->getClass().id() == Bar::classId); + ASSERT_TRUE(operation.nodes[2]->getClass().id() == Bar::classId); + ASSERT_TRUE(operation.nodes[3]->getClass().id() == Bar::classId); + ASSERT_TRUE(((Bar*)operation.nodes[0])->value == 1); + ASSERT_TRUE(((Bar*)operation.nodes[1])->value == 2); + ASSERT_TRUE(((Bar*)operation.nodes[2])->value == 3); + ASSERT_TRUE(((Bar*)operation.nodes[3])->value == 4); + } + TEST_DONE(); +} diff --git a/vespalib/src/tests/polymorphicarray/.gitignore b/vespalib/src/tests/polymorphicarray/.gitignore new file mode 100644 index 00000000000..e0decc87f2c --- /dev/null +++ b/vespalib/src/tests/polymorphicarray/.gitignore @@ -0,0 +1 @@ +vespalib_polymorphicarray_test_app diff --git a/vespalib/src/tests/polymorphicarray/CMakeLists.txt b/vespalib/src/tests/polymorphicarray/CMakeLists.txt new file mode 100644 index 00000000000..14edfbec4b4 --- /dev/null +++ b/vespalib/src/tests/polymorphicarray/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_polymorphicarray_test_app TEST + SOURCES + polymorphicarray_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_polymorphicarray_test_app COMMAND vespalib_polymorphicarray_test_app) diff --git a/vespalib/src/tests/polymorphicarray/polymorphicarray_test.cpp b/vespalib/src/tests/polymorphicarray/polymorphicarray_test.cpp new file mode 100644 index 00000000000..d4ec8f3ed7c --- /dev/null +++ b/vespalib/src/tests/polymorphicarray/polymorphicarray_test.cpp @@ -0,0 +1,123 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/vespalib/util/polymorphicarrays.h> + +using namespace vespalib; + +class A { +public: + virtual ~A() = default; + virtual void assign(const A & rhs) { (void) rhs; assert(false); } // Required by the primitive array. + virtual A * clone() const { assert(false); return nullptr; } // Required for the complex array. + + // For testing + virtual bool operator == (const A & rhs) const = 0; + virtual void print(std::ostream & os) const = 0; +}; + +class Primitive : public A +{ +public: + Primitive(size_t v=11) noexcept : _v(v) { } + size_t value() const { return _v; } + bool operator == (const A & rhs) const override { + return dynamic_cast<const Primitive &>(rhs).value() == value(); + } + void assign(const A & rhs) override { + _v = dynamic_cast<const Primitive &>(rhs).value(); + } + void print(std::ostream & os) const override { + os << _v; + } +private: + size_t _v; +}; + + +class Complex : public A +{ +public: + Complex(size_t v=11) noexcept : _v(v) { } + size_t value() const { return _v; } + bool operator == (const A & rhs) const override { + return dynamic_cast<const Complex &>(rhs).value() == value(); + } + Complex * clone() const override { + return new Complex(_v); + } + void print(std::ostream & os) const override { + os << _v; + } +private: + size_t _v; +}; + +std::ostream & operator << (std::ostream & os, const A & v) { + v.print(os); + return os; +} + + +template <typename T> +void +verifyArray(IArrayT<A> & array) +{ + EXPECT_EQUAL(0u, array.size()); + for (size_t i(0); i < 10; i++) { + array.push_back(T(i)); + } + EXPECT_EQUAL(10u, array.size()); + for (size_t i(0); i < 10; i++) { + EXPECT_EQUAL(T(i), array[i]); + } + IArrayT<A>::UP copy(array.clone()); + array.clear(); + EXPECT_EQUAL(0u, array.size()); + + for (size_t i(0); i < copy->size(); i++) { + array.push_back((*copy)[i]); + } + + array.resize(19); + EXPECT_EQUAL(19u, array.size()); + for (size_t i(0); i < 10; i++) { + EXPECT_EQUAL(T(i), array[i]); + } + for (size_t i(10); i < array.size(); i++) { + EXPECT_EQUAL(T(11), array[i]); + } + array.resize(13); + EXPECT_EQUAL(13u, array.size()); + for (size_t i(0); i < 10; i++) { + EXPECT_EQUAL(T(i), array[i]); + } + for (size_t i(10); i < array.size(); i++) { + EXPECT_EQUAL(T(11), array[i]); + } + dynamic_cast<T &>(array[1]) = T(17); + EXPECT_EQUAL(T(0), array[0]); + EXPECT_EQUAL(T(17), array[1]); + EXPECT_EQUAL(T(2), array[2]); +} + + +TEST("require that primitive arrays conforms") { + PrimitiveArrayT<Primitive, A> a; + verifyArray<Primitive>(a); + EXPECT_EQUAL(7u, a[7].value()); +} + +class Factory : public ComplexArrayT<A>::Factory +{ +public: + A * create() override { return new Complex(); } + Factory * clone() const override { return new Factory(*this); } +}; + +TEST("require that complex arrays conforms") { + ComplexArrayT<A> a(Factory::UP(new Factory())); + verifyArray<Complex>(a); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/programoptions/.gitignore b/vespalib/src/tests/programoptions/.gitignore new file mode 100644 index 00000000000..f083a1e093d --- /dev/null +++ b/vespalib/src/tests/programoptions/.gitignore @@ -0,0 +1,4 @@ +.depend +Makefile +programoptions_test +vespalib_programoptions_test_app diff --git a/vespalib/src/tests/programoptions/CMakeLists.txt b/vespalib/src/tests/programoptions/CMakeLists.txt new file mode 100644 index 00000000000..fb2fdb48dd7 --- /dev/null +++ b/vespalib/src/tests/programoptions/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_programoptions_test_app TEST + SOURCES + programoptions_test.cpp + programoptions_testutils.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_programoptions_test_app COMMAND vespalib_programoptions_test_app) diff --git a/vespalib/src/tests/programoptions/programoptions_test.cpp b/vespalib/src/tests/programoptions/programoptions_test.cpp new file mode 100644 index 00000000000..4b63eae949b --- /dev/null +++ b/vespalib/src/tests/programoptions/programoptions_test.cpp @@ -0,0 +1,361 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "programoptions_testutils.h" +#include <vespa/vespalib/util/programoptions.h> +#include <vespa/vespalib/testkit/testapp.h> +#include <iostream> + +namespace vespalib { + +class Test : public vespalib::TestApp +{ +public: + void testSyntaxPage(); + void testNormalUsage(); + void testFailures(); + void testVectorArgument(); + void testAllHiddenOption(); + void testOptionsAfterArguments(); + int Main() override; +}; + +int +Test::Main() +{ + TEST_INIT("programoptions_test"); + srandom(1); + testSyntaxPage(); + testNormalUsage(); + testFailures(); + testVectorArgument(); + testAllHiddenOption(); + // Currently not supported + // testOptionsAfterArguments(); + TEST_DONE(); +} + +struct MyOptions : public ProgramOptions { + bool boolOpt; + bool boolWithDefOpt; + int intOpt; + uint32_t uintOpt; + float floatOpt; + std::string stringOpt; + std::string argString; + int argInt; + std::string argOptionalString; + std::map<std::string, std::string> properties; + int anotherOptionalArg; + + MyOptions(int argc, const char *const *argv); + ~MyOptions(); +}; + +MyOptions::MyOptions(int argc, const char* const* argv) + : ProgramOptions(argc, argv) +{ + // Required options + addOption("uintopt u", uintOpt, "Sets an unsigned int"); + // Optional options + addOption("b bool", boolOpt, "Enables a flag"); + addOption("boolwithdef", boolWithDefOpt, true, "If set turns to false"); + + addOption("intopt i", intOpt, 5, "Sets a signed int"); + addOption("floatopt", floatOpt, 4.0f, "Sets a float\nMultiline baby"); + addOption("string s", stringOpt, std::string("ballalaika"), + "Sets a string value. This is a very long description that " + "should be broken down into multiple lines in some sensible " + "way."); + addOptionHeader("Advanced options"); + addOption("p properties", properties, "Property map"); + addHiddenIdentifiers("prop"); + setArgumentTypeName("key"); + setArgumentTypeName("value", 1); + + addArgument("argString", argString, "Required string argument."); + addArgument("argInt", argInt, "Required int argument."); + addArgument("argOptionalString", argOptionalString, std::string("foo"), + "Optional string argument with a long description so we " + "can see that it will be broken correctly."); + addArgument("argSecondOptional", anotherOptionalArg, 3, + "Yet another optional argument"); + + setSyntaxMessage("A test program to see if this utility works."); + setSyntaxPageMaxLeftColumnSize(25); +} + +MyOptions::~MyOptions() { } + +void Test::testSyntaxPage() { + AppOptions opts("myapp"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + std::ostringstream actual; + options.writeSyntaxPage(actual); + + std::string expected( +"\nA test program to see if this utility works.\n\n" +"Usage: myapp [options] <argString> <argInt> [argOptionalString] [argSecondOptional]\n\n" +"Arguments:\n" +" argString (string) : Required string argument.\n" +" argInt (int) : Required int argument.\n" +" argOptionalString (string)\n" +" : Optional string argument with a long description so\n" +" we can see that it will be broken correctly.\n" +" (optional)\n" +" argSecondOptional (int) : Yet another optional argument (optional)\n\n" +"Options:\n" +" --uintopt -u <uint> : Sets an unsigned int (required)\n" +" -b --bool : Enables a flag\n" +" --boolwithdef : If set turns to false\n" +" --intopt -i <int> : Sets a signed int (default 5)\n" +" --floatopt <float> : Sets a float\n" +" Multiline baby (default 4)\n" +" --string -s <string> : Sets a string value. This is a very long description\n" +" that should be broken down into multiple lines in some\n" +" sensible way. (default \"ballalaika\")\n\n" +"Advanced options:\n" +" -p --properties <key> <value> : Property map (default empty)\n" + ); + EXPECT_EQUAL(expected, actual.str()); +} + +void Test::testNormalUsage() { + { + AppOptions opts("myapp -b --uintopt 4 -s foo tit 1 tei 6"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + options.parse(); + EXPECT_EQUAL(true, options.boolOpt); + EXPECT_EQUAL(true, options.boolWithDefOpt); + EXPECT_EQUAL(5, options.intOpt); + EXPECT_EQUAL(4u, options.uintOpt); + EXPECT_APPROX(4, options.floatOpt, 0.00001); + EXPECT_EQUAL("foo", options.stringOpt); + EXPECT_EQUAL("tit", options.argString); + EXPECT_EQUAL(1, options.argInt); + EXPECT_EQUAL("tei", options.argOptionalString); + EXPECT_EQUAL(0u, options.properties.size()); + EXPECT_EQUAL(6, options.anotherOptionalArg); + } + { + AppOptions opts("myapp --uintopt 6 tit 1"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + options.parse(); + EXPECT_EQUAL(false, options.boolOpt); + EXPECT_EQUAL(true, options.boolWithDefOpt); + EXPECT_EQUAL(5, options.intOpt); + EXPECT_EQUAL(6u, options.uintOpt); + EXPECT_APPROX(4, options.floatOpt, 0.00001); + EXPECT_EQUAL("ballalaika", options.stringOpt); + EXPECT_EQUAL("tit", options.argString); + EXPECT_EQUAL(1, options.argInt); + EXPECT_EQUAL("foo", options.argOptionalString); + EXPECT_EQUAL(0u, options.properties.size()); + EXPECT_EQUAL(3, options.anotherOptionalArg); + } + // Arguments coming after options. + // (Required for nesting of short options) + { + AppOptions opts("myapp --uintopt --intopt 6 -8 tit 1 tei"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + options.parse(); + EXPECT_EQUAL(false, options.boolOpt); + EXPECT_EQUAL(true, options.boolWithDefOpt); + EXPECT_EQUAL(-8, options.intOpt); + EXPECT_EQUAL(6u, options.uintOpt); + EXPECT_APPROX(4, options.floatOpt, 0.00001); + EXPECT_EQUAL("ballalaika", options.stringOpt); + EXPECT_EQUAL("tit", options.argString); + EXPECT_EQUAL(1, options.argInt); + EXPECT_EQUAL("tei", options.argOptionalString); + EXPECT_EQUAL(0u, options.properties.size()); + } + { + AppOptions opts( "myapp -uib 6 -8 --boolwithdef tit 1 tei"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + options.parse(); + EXPECT_EQUAL(true, options.boolOpt); + EXPECT_EQUAL(false, options.boolWithDefOpt); + EXPECT_EQUAL(-8, options.intOpt); + EXPECT_EQUAL(6u, options.uintOpt); + EXPECT_APPROX(4, options.floatOpt, 0.00001); + EXPECT_EQUAL("ballalaika", options.stringOpt); + EXPECT_EQUAL("tit", options.argString); + EXPECT_EQUAL(1, options.argInt); + EXPECT_EQUAL("tei", options.argOptionalString); + EXPECT_EQUAL(0u, options.properties.size()); + } + // Properties + { + AppOptions opts("myapp -u 6 -p foo bar --prop hmm brr tit 1 tei"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + options.parse(); + EXPECT_EQUAL(false, options.boolOpt); + EXPECT_EQUAL(true, options.boolWithDefOpt); + EXPECT_EQUAL(5, options.intOpt); + EXPECT_EQUAL(6u, options.uintOpt); + EXPECT_APPROX(4, options.floatOpt, 0.00001); + EXPECT_EQUAL("ballalaika", options.stringOpt); + EXPECT_EQUAL("tit", options.argString); + EXPECT_EQUAL(1, options.argInt); + EXPECT_EQUAL("tei", options.argOptionalString); + EXPECT_EQUAL(2u, options.properties.size()); + EXPECT_EQUAL("bar", options.properties["foo"]); + EXPECT_EQUAL("brr", options.properties["hmm"]); + } +} + +void Test::testFailures() { + // Non-existing long option + { + AppOptions opts("myapp -b --uintopt 4 -s foo --none"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + try{ + options.parse(); + TEST_FATAL("Expected exception"); + } catch (InvalidCommandLineArgumentsException& e) { + EXPECT_EQUAL("Invalid option 'none'.", e.getMessage()); + } + } + // Non-existing short option + { + AppOptions opts("myapp -b --uintopt 4 -s foo -q"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + try{ + options.parse(); + TEST_FATAL("Expected exception"); + } catch (InvalidCommandLineArgumentsException& e) { + EXPECT_EQUAL("Invalid option 'q'.", e.getMessage()); + } + } + // Lacking option argument + { + AppOptions opts("myapp -b --uintopt 4 -s"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + try{ + options.parse(); + TEST_FATAL("Expected exception"); + } catch (InvalidCommandLineArgumentsException& e) { + EXPECT_EQUAL("Option 's' needs 1 arguments. Only 0 available.", + e.getMessage()); + } + } + // Out of signed ranged + { + AppOptions opts("myapp -b --uintopt 4 -intopt 3000000000"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + try{ + options.parse(); + TEST_FATAL("Expected exception"); + } catch (InvalidCommandLineArgumentsException& e) { + EXPECT_EQUAL("The argument '3000000000' can not be interpreted as a " + "number of type int.", e.getMessage()); + } + } + // Negative value to unsigned var (Currently doesnt fail) +/* + { + AppOptions opts("myapp -b --uintopt -1 foo 0"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + try{ + options.parse(); + TEST_FATAL("Expected exception"); + } catch (InvalidCommandLineArgumentsException& e) { + EXPECT_EQUAL("The argument '-1' can not be interpreted as a " + "number of type uint.", e.getMessage()); + } + } + */ + // Lacking required option + { + AppOptions opts("myapp -b"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + try{ + options.parse(); + TEST_FATAL("Expected exception"); + } catch (InvalidCommandLineArgumentsException& e) { + EXPECT_EQUAL("Option 'uintopt' has no default and must be set.", + e.getMessage()); + } + } + // Lacking required argument + { + AppOptions opts("myapp --uintopt 1 tit"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + try{ + options.parse(); + TEST_FATAL("Expected exception"); + } catch (InvalidCommandLineArgumentsException& e) { + EXPECT_EQUAL("Insufficient data is given to set required argument " + "'argInt'.", + e.getMessage()); + } + } + // Argument of wrong type + { + AppOptions opts("myapp --uintopt 1 tit en"); + MyOptions options(opts.getArgCount(), opts.getArguments()); + try{ + options.parse(); + TEST_FATAL("Expected exception"); + } catch (InvalidCommandLineArgumentsException& e) { + EXPECT_EQUAL("The argument 'en' can not be interpreted as a number " + "of type int.", + e.getMessage()); + } + } +} + +void Test::testVectorArgument() +{ + AppOptions opts("myapp foo bar baz"); + std::vector<std::string> args; + ProgramOptions options(opts.getArgCount(), opts.getArguments()); + options.addListArgument("ids", args, "Vector element"); + std::ostringstream actual; + options.writeSyntaxPage(actual); + std::string expected( +"\nUsage: myapp [ids...]\n\n" +"Arguments:\n" +" ids (string[]) : Vector element\n" + ); + EXPECT_EQUAL(expected, actual.str()); + + options.parse(); + EXPECT_EQUAL(3u, args.size()); + EXPECT_EQUAL("foo", args[0]); + EXPECT_EQUAL("bar", args[1]); + EXPECT_EQUAL("baz", args[2]); +} + +void Test::testAllHiddenOption() +{ + AppOptions opts("myapp --foo bar"); + std::string option; + ProgramOptions options(opts.getArgCount(), opts.getArguments()); + options.addOption("", option, "Description"); + options.addHiddenIdentifiers("foo"); + std::ostringstream actual; + options.writeSyntaxPage(actual); + std::string expected("\nUsage: myapp\n"); + EXPECT_EQUAL(expected, actual.str()); + + options.parse(); + EXPECT_EQUAL("bar", option); +} + +void Test::testOptionsAfterArguments() +{ + AppOptions opts("myapp bar --foo baz"); + std::string option; + std::string argument; + ProgramOptions options(opts.getArgCount(), opts.getArguments()); + options.addOption("foo", option, "Description"); + options.addArgument("arg", argument, "Description"); + options.parse(); + EXPECT_EQUAL("baz", option); + EXPECT_EQUAL("bar", argument); +} + +} // vespalib + +TEST_APPHOOK(vespalib::Test) diff --git a/vespalib/src/tests/programoptions/programoptions_testutils.cpp b/vespalib/src/tests/programoptions/programoptions_testutils.cpp new file mode 100644 index 00000000000..948413c36db --- /dev/null +++ b/vespalib/src/tests/programoptions/programoptions_testutils.cpp @@ -0,0 +1,48 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "programoptions_testutils.h" + +namespace vespalib { + +namespace { + std::vector<std::string> splitString(const std::string& source) { + std::vector<std::string> target; + std::string::size_type start = 0; + std::string::size_type stop = source.find(' '); + while (stop != std::string::npos) { + target.push_back(source.substr(start, stop - start)); + start = stop + 1; + stop = source.find(' ', start); + } + target.push_back(source.substr(start)); + return target; + } +} // anonymous + +AppOptions::AppOptions(const std::string& optString) + : _argc(0), _argv(0), _source() +{ + _source = splitString(optString); + _argc = _source.size(); + _argv = new const char*[_source.size()]; + for (int i=0; i<_argc; ++i) { + if (_source[i].size() > 1 + && _source[i][0] == _source[i][_source[i].size() - 1] + && (_source[i][0] == '\'' || _source[i][0] == '"')) + { + if (_source[i].size() == 2) { + _source[i] = ""; + } else { + _source[i] = _source[i].substr(1, _source.size() - 2); + } + } + _argv[i] = _source[i].c_str(); + } +} + +AppOptions::~AppOptions() +{ + delete[] _argv; +} + +} // vespalib diff --git a/vespalib/src/tests/programoptions/programoptions_testutils.h b/vespalib/src/tests/programoptions/programoptions_testutils.h new file mode 100644 index 00000000000..a6f103f3e95 --- /dev/null +++ b/vespalib/src/tests/programoptions/programoptions_testutils.h @@ -0,0 +1,32 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * This class contains some test utilities, to create argc/argv inputs for + * application tests. + */ + +#pragma once + +#include <string> +#include <vector> + +namespace vespalib { + +class AppOptions { + int _argc; + const char** _argv; + std::vector<std::string> _source; + + AppOptions(const AppOptions&); + AppOptions& operator=(const AppOptions&); + +public: + AppOptions(const std::string& optString); + ~AppOptions(); + + int getArgCount() const { return _argc; } + const char* const* getArguments() const { return _argv; } + +}; + +} // vespalib + diff --git a/vespalib/src/tests/rusage/.gitignore b/vespalib/src/tests/rusage/.gitignore new file mode 100644 index 00000000000..c01c01ed328 --- /dev/null +++ b/vespalib/src/tests/rusage/.gitignore @@ -0,0 +1 @@ +vespalib_rusage_test_app diff --git a/vespalib/src/tests/rusage/CMakeLists.txt b/vespalib/src/tests/rusage/CMakeLists.txt new file mode 100644 index 00000000000..1c1ab85facd --- /dev/null +++ b/vespalib/src/tests/rusage/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_rusage_test_app TEST + SOURCES + rusage_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_rusage_test_app COMMAND vespalib_rusage_test_app) diff --git a/vespalib/src/tests/rusage/rusage_test.cpp b/vespalib/src/tests/rusage/rusage_test.cpp new file mode 100644 index 00000000000..7e30f3b968b --- /dev/null +++ b/vespalib/src/tests/rusage/rusage_test.cpp @@ -0,0 +1,57 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/rusage.h> + +using namespace vespalib; + +TEST("testRUsage") +{ + RUsage r1; + EXPECT_EQUAL("", r1.toString()); + RUsage r2; + EXPECT_EQUAL(r2.toString(), r1.toString()); + RUsage diff = r2-r1; + EXPECT_EQUAL(diff.toString(), r2.toString()); + { + RUsage then = RUsage::createSelf(steady_time(7ns)); + RUsage now = RUsage::createSelf(); + EXPECT_NOT_EQUAL(now.toString(), then.toString()); + } + { + RUsage then = RUsage::createChildren(steady_time(1337583ns)); + RUsage now = RUsage::createChildren(); + EXPECT_NOT_EQUAL(now.toString(), then.toString()); + } + { + timeval a, b, c, d, r; + a.tv_usec = 7; + a.tv_sec = 7; + b.tv_usec = 7; + b.tv_sec = 7; + c.tv_usec = 1; + c.tv_sec = 8; + d.tv_usec = 9; + d.tv_sec = 4; + r = a - b; + EXPECT_EQUAL(0, r.tv_sec); + EXPECT_EQUAL(0, r.tv_usec); + r = b - a; + EXPECT_EQUAL(0, r.tv_sec); + EXPECT_EQUAL(0, r.tv_usec); + r = a - c; + EXPECT_EQUAL(-1, r.tv_sec); + EXPECT_EQUAL( 6, r.tv_usec); + r = c - a; + EXPECT_EQUAL(0, r.tv_sec); + EXPECT_EQUAL(999994, r.tv_usec); + r = a - d; + EXPECT_EQUAL(2, r.tv_sec); + EXPECT_EQUAL(999998, r.tv_usec); + r = d - a; + EXPECT_EQUAL(-3, r.tv_sec); + EXPECT_EQUAL( 2, r.tv_usec); + } +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/shutdownguard/.gitignore b/vespalib/src/tests/shutdownguard/.gitignore new file mode 100644 index 00000000000..c167d4784ca --- /dev/null +++ b/vespalib/src/tests/shutdownguard/.gitignore @@ -0,0 +1 @@ +vespalib_shutdownguard_test_app diff --git a/vespalib/src/tests/shutdownguard/CMakeLists.txt b/vespalib/src/tests/shutdownguard/CMakeLists.txt new file mode 100644 index 00000000000..6714842fbbf --- /dev/null +++ b/vespalib/src/tests/shutdownguard/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_shutdownguard_test_app TEST + SOURCES + shutdownguard_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_shutdownguard_test_app NO_VALGRIND COMMAND vespalib_shutdownguard_test_app) diff --git a/vespalib/src/tests/shutdownguard/shutdownguard_test.cpp b/vespalib/src/tests/shutdownguard/shutdownguard_test.cpp new file mode 100644 index 00000000000..348e9bbd503 --- /dev/null +++ b/vespalib/src/tests/shutdownguard/shutdownguard_test.cpp @@ -0,0 +1,43 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/shutdownguard.h> +#include <vespa/vespalib/util/malloc_mmap_guard.h> +#include <thread> +#include <unistd.h> +#include <sys/wait.h> +#include <cstdlib> + +using namespace vespalib; + +TEST("test shutdown guard") +{ + { + ShutdownGuard farFuture(1000000s); + std::this_thread::sleep_for(20ms); + } + EXPECT_TRUE(true); + pid_t child = fork(); + if (child == 0) { + ShutdownGuard soon(30ms); + for (int i = 0; i < 1000; ++i) { + std::this_thread::sleep_for(20ms); + } + std::_Exit(0); + } + for (int i = 0; i < 1000; ++i) { + std::this_thread::sleep_for(20ms); + int stat = 0; + if (waitpid(child, &stat, WNOHANG) == child) { + EXPECT_TRUE(WIFEXITED(stat)); + EXPECT_EQUAL(1, WEXITSTATUS(stat)); + break; + } + EXPECT_TRUE(i < 800); + } +} + +TEST("test malloc mmap guard") { + MallocMmapGuard guard(0x100000); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/stllike/CMakeLists.txt b/vespalib/src/tests/stllike/CMakeLists.txt index 80509c565c6..005ef3d1ed0 100644 --- a/vespalib/src/tests/stllike/CMakeLists.txt +++ b/vespalib/src/tests/stllike/CMakeLists.txt @@ -55,3 +55,17 @@ vespa_add_executable(vespalib_replace_variable_test_app TEST GTest::GTest ) vespa_add_test(NAME vespalib_replace_variable_test_app COMMAND vespalib_replace_variable_test_app) +vespa_add_executable(vespalib_lrucache_test_app TEST + SOURCES + lrucache.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_lrucache_test_app COMMAND vespalib_lrucache_test_app) +vespa_add_executable(vespalib_cache_test_app TEST + SOURCES + cache_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_cache_test_app COMMAND vespalib_cache_test_app) diff --git a/vespalib/src/tests/stllike/cache_test.cpp b/vespalib/src/tests/stllike/cache_test.cpp new file mode 100644 index 00000000000..35f04d91510 --- /dev/null +++ b/vespalib/src/tests/stllike/cache_test.cpp @@ -0,0 +1,139 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/stllike/string.h> +#include <vespa/vespalib/stllike/cache.hpp> +#include <map> + +using namespace vespalib; + +template<typename K, typename V> +class Map : public std::map<K, V> { + typedef typename std::map<K, V>::const_iterator const_iterator; + typedef std::map<K, V> M; +public: + bool read(const K & k, V & v) const { + const_iterator found = M::find(k); + bool ok(found != this->end()); + if (ok) { + v = found->second; + } + return ok; + } + void write(const K & k, const V & v) { + (*this)[k] = v; + } + void erase(const K & k) { + M::erase(k); + } +}; + +using P = LruParam<uint32_t, string>; +using B = Map<uint32_t, string>; + +TEST("testCache") { + B m; + cache< CacheParam<P, B> > cache(m, -1); + // Verfify start conditions. + EXPECT_TRUE(cache.size() == 0); + EXPECT_TRUE( ! cache.hasKey(1) ); + cache.write(1, "First inserted string"); + EXPECT_TRUE( cache.hasKey(1) ); + m[2] = "String inserted beneath"; + EXPECT_TRUE( ! cache.hasKey(2) ); + EXPECT_EQUAL( cache.read(2), "String inserted beneath"); + EXPECT_TRUE( cache.hasKey(2) ); + cache.erase(1); + EXPECT_TRUE( ! cache.hasKey(1) ); + EXPECT_TRUE(cache.size() == 1); +} + +TEST("testCacheSize") +{ + B m; + cache< CacheParam<P, B> > cache(m, -1); + cache.write(1, "10 bytes string"); + EXPECT_EQUAL(80u, cache.sizeBytes()); + cache.write(1, "10 bytes string"); // Still the same size + EXPECT_EQUAL(80u, cache.sizeBytes()); +} + +TEST("testCacheSizeDeep") +{ + B m; + cache< CacheParam<P, B, zero<uint32_t>, size<string> > > cache(m, -1); + cache.write(1, "15 bytes string"); + EXPECT_EQUAL(95u, cache.sizeBytes()); + cache.write(1, "10 bytes s"); + EXPECT_EQUAL(90u, cache.sizeBytes()); + cache.write(1, "20 bytes string ssss"); + EXPECT_EQUAL(100u, cache.sizeBytes()); +} + +TEST("testCacheEntriesHonoured") { + B m; + cache< CacheParam<P, B, zero<uint32_t>, size<string> > > cache(m, -1); + cache.maxElements(1); + cache.write(1, "15 bytes string"); + EXPECT_EQUAL(1u, cache.size()); + EXPECT_EQUAL(95u, cache.sizeBytes()); + cache.write(2, "16 bytes stringg"); + EXPECT_EQUAL(1u, cache.size()); + EXPECT_TRUE( cache.hasKey(2) ); + EXPECT_FALSE( cache.hasKey(1) ); + EXPECT_EQUAL(96u, cache.sizeBytes()); +} + +TEST("testCacheMaxSizeHonoured") { + B m; + cache< CacheParam<P, B, zero<uint32_t>, size<string> > > cache(m, 200); + cache.write(1, "15 bytes string"); + EXPECT_EQUAL(1u, cache.size()); + EXPECT_EQUAL(95u, cache.sizeBytes()); + cache.write(2, "16 bytes stringg"); + EXPECT_EQUAL(2u, cache.size()); + EXPECT_EQUAL(191u, cache.sizeBytes()); + cache.write(3, "17 bytes stringgg"); + EXPECT_EQUAL(3u, cache.size()); + EXPECT_EQUAL(288u, cache.sizeBytes()); + cache.write(4, "18 bytes stringggg"); + EXPECT_EQUAL(3u, cache.size()); + EXPECT_EQUAL(291u, cache.sizeBytes()); +} + +TEST("testThatMultipleRemoveOnOverflowIsFine") { + B m; + cache< CacheParam<P, B, zero<uint32_t>, size<string> > > cache(m, 2000); + + for (size_t j(0); j < 5; j++) { + for (size_t i(0); cache.size() == i; i++) { + cache.write(j*53+i, "a"); + } + } + EXPECT_EQUAL(25u, cache.size()); + EXPECT_EQUAL(2025u, cache.sizeBytes()); + EXPECT_FALSE( cache.hasKey(0) ); + string ls("long string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + string vls=ls+ls+ls+ls+ls+ls; + cache.write(53+5, ls); + EXPECT_EQUAL(25u, cache.size()); + EXPECT_EQUAL(2498u, cache.sizeBytes()); + EXPECT_FALSE( cache.hasKey(1) ); + cache.write(53*7+5, ls); + EXPECT_EQUAL(19u, cache.size()); + EXPECT_EQUAL(2485u, cache.sizeBytes()); + EXPECT_FALSE( cache.hasKey(2) ); + cache.write(53*8+5, vls); + EXPECT_EQUAL(14u, cache.size()); + EXPECT_EQUAL(4923u, cache.sizeBytes()); + cache.write(53*9+6, vls); + EXPECT_EQUAL(1u, cache.size()); + EXPECT_EQUAL(2924u, cache.sizeBytes()); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/stllike/lrucache.cpp b/vespalib/src/tests/stllike/lrucache.cpp new file mode 100644 index 00000000000..2cc6f2b4ee8 --- /dev/null +++ b/vespalib/src/tests/stllike/lrucache.cpp @@ -0,0 +1,190 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/stllike/string.h> +#include <vespa/vespalib/stllike/lrucache_map.hpp> + +using namespace vespalib; + +TEST("testCache") { + lrucache_map< LruParam<int, string> > cache(7); + // Verfify start conditions. + EXPECT_TRUE(cache.size() == 0); + cache.insert(1, "First inserted string"); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_TRUE(cache.size() == 1); + EXPECT_TRUE(cache.hasKey(1)); + cache.insert(2, "Second inserted string"); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_TRUE(cache.size() == 2); + EXPECT_TRUE(cache.hasKey(1)); + EXPECT_TRUE(cache.hasKey(2)); + cache.insert(3, "Third inserted string"); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_TRUE(cache.size() == 3); + EXPECT_TRUE(cache.hasKey(1)); + EXPECT_TRUE(cache.hasKey(2)); + EXPECT_TRUE(cache.hasKey(3)); + cache.insert(4, "Fourth inserted string"); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_TRUE(cache.size() == 4); + EXPECT_TRUE(cache.hasKey(1)); + EXPECT_TRUE(cache.hasKey(2)); + EXPECT_TRUE(cache.hasKey(3)); + EXPECT_TRUE(cache.hasKey(4)); + cache.insert(5, "Fifth inserted string"); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_TRUE(cache.size() == 5); + EXPECT_TRUE(cache.hasKey(1)); + EXPECT_TRUE(cache.hasKey(2)); + EXPECT_TRUE(cache.hasKey(3)); + EXPECT_TRUE(cache.hasKey(4)); + EXPECT_TRUE(cache.hasKey(5)); + cache.insert(6, "Sixt inserted string"); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_TRUE(cache.size() == 6); + EXPECT_TRUE(cache.hasKey(1)); + EXPECT_TRUE(cache.hasKey(2)); + EXPECT_TRUE(cache.hasKey(3)); + EXPECT_TRUE(cache.hasKey(4)); + EXPECT_TRUE(cache.hasKey(5)); + EXPECT_TRUE(cache.hasKey(6)); + cache.insert(7, "Seventh inserted string"); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_EQUAL(cache.size(), 7u); + EXPECT_TRUE(cache.hasKey(1)); + EXPECT_TRUE(cache.hasKey(2)); + EXPECT_TRUE(cache.hasKey(3)); + EXPECT_TRUE(cache.hasKey(4)); + EXPECT_TRUE(cache.hasKey(5)); + EXPECT_TRUE(cache.hasKey(6)); + EXPECT_TRUE(cache.hasKey(7)); + cache.insert(8, "Eighth inserted string"); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_EQUAL(cache.size(), 7u); + EXPECT_TRUE(cache.hasKey(2)); + EXPECT_TRUE(cache.hasKey(3)); + EXPECT_TRUE(cache.hasKey(4)); + EXPECT_TRUE(cache.hasKey(5)); + EXPECT_TRUE(cache.hasKey(6)); + EXPECT_TRUE(cache.hasKey(7)); + EXPECT_TRUE(cache.hasKey(8)); + cache.insert(15, "Eighth inserted string"); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_EQUAL(cache.size(), 7u); + EXPECT_TRUE(cache.hasKey(3)); + EXPECT_TRUE(cache.hasKey(4)); + EXPECT_TRUE(cache.hasKey(5)); + EXPECT_TRUE(cache.hasKey(6)); + EXPECT_TRUE(cache.hasKey(7)); + EXPECT_TRUE(cache.hasKey(8)); + EXPECT_TRUE(cache.hasKey(15)); + // Test get and erase + cache.get(3); + EXPECT_TRUE(cache.verifyInternals()); + cache.erase(3); + EXPECT_TRUE(cache.verifyInternals()); + EXPECT_TRUE(!cache.hasKey(3)); +} + +typedef std::shared_ptr<std::string> MyKey; +typedef std::shared_ptr<std::string> MyData; + +struct SharedEqual { + bool operator()(const MyKey & a, const MyKey & b) { + return ((*a) == (*b)); + } +}; + +struct SharedHash { + size_t operator() (const MyKey & arg) const { return arg->size(); } +}; + + +TEST("testCacheInsertOverResize") { + using LS = std::shared_ptr<std::string>; + using Cache = lrucache_map< LruParam<int, LS> >; + + Cache cache(100); + size_t sum(0); + for (size_t i(0); i < cache.capacity()*10; i++) { + LS s(new std::string("abc")); + cache[random()] = s; + sum += strlen(s->c_str()); + EXPECT_EQUAL(strlen(s->c_str()), s->size()); + } + EXPECT_EQUAL(sum, cache.capacity()*10*3); +} + +TEST("testCacheErase") { + lrucache_map< LruParam<MyKey, MyData, SharedHash, SharedEqual> > cache(4); + + MyData d(new std::string("foo")); + MyKey k(new std::string("barlol")); + // Verfify start conditions. + EXPECT_TRUE(cache.size() == 0); + EXPECT_TRUE(d.use_count() == 1); + EXPECT_TRUE(k.use_count() == 1); + cache.insert(k, d); + EXPECT_TRUE(d.use_count() == 2); + EXPECT_TRUE(k.use_count() == 2); + cache.erase(k); + EXPECT_TRUE(d.use_count() == 1); + EXPECT_TRUE(k.use_count() == 1); +} + +TEST("testCacheIterator") { + typedef lrucache_map< LruParam<int, string> > Cache; + Cache cache(3); + cache.insert(1, "first"); + cache.insert(2, "second"); + cache.insert(3, "third"); + Cache::iterator it(cache.begin()); + Cache::iterator mt(cache.end()); + ASSERT_TRUE(it != mt); + ASSERT_EQUAL("third", *it); + ASSERT_TRUE(it != mt); + ASSERT_EQUAL("second", *(++it)); + ASSERT_TRUE(it != mt); + ASSERT_EQUAL("second", *it++); + ASSERT_TRUE(it != mt); + ASSERT_EQUAL("first", *it); + ASSERT_TRUE(it != mt); + it++; + ASSERT_TRUE(it == mt); + cache.insert(4, "fourth"); + Cache::iterator it2(cache.begin()); + Cache::iterator it3(cache.begin()); + ASSERT_EQUAL("fourth", *it2); + ASSERT_TRUE(it2 == it3); + it2++; + ASSERT_TRUE(it2 != it3); + it2++; + it2++; + ASSERT_TRUE(it2 == mt); + Cache::iterator it4 = cache.erase(it3); + ASSERT_EQUAL("third", *it4); + ASSERT_EQUAL("third", *cache.begin()); + Cache::iterator it5(cache.erase(cache.end())); + ASSERT_TRUE(it5 == cache.end()); +} + +TEST("testCacheIteratorErase") { + typedef lrucache_map< LruParam<int, string> > Cache; + Cache cache(3); + cache.insert(1, "first"); + cache.insert(8, "second"); + cache.insert(15, "third"); + cache.insert(15, "third"); + cache.insert(8, "second"); + cache.insert(1, "first"); + Cache::iterator it(cache.begin()); + ASSERT_EQUAL("first", *it); + it++; + ASSERT_EQUAL("second", *it); + it = cache.erase(it); + ASSERT_EQUAL("third", *it); + cache.erase(it); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/util/process_memory_stats/.gitignore b/vespalib/src/tests/util/process_memory_stats/.gitignore new file mode 100644 index 00000000000..81af04ee64f --- /dev/null +++ b/vespalib/src/tests/util/process_memory_stats/.gitignore @@ -0,0 +1,2 @@ +mapfile +vespalib_process_memory_stats_test_app diff --git a/vespalib/src/tests/util/process_memory_stats/CMakeLists.txt b/vespalib/src/tests/util/process_memory_stats/CMakeLists.txt new file mode 100644 index 00000000000..30a0f90d952 --- /dev/null +++ b/vespalib/src/tests/util/process_memory_stats/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_process_memory_stats_test_app TEST + SOURCES + process_memory_stats_test.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_process_memory_stats_test_app COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/process_memory_stats_test.sh + DEPENDS vespalib_process_memory_stats_test_app) diff --git a/vespalib/src/tests/util/process_memory_stats/process_memory_stats_test.cpp b/vespalib/src/tests/util/process_memory_stats/process_memory_stats_test.cpp new file mode 100644 index 00000000000..6d0917e6d15 --- /dev/null +++ b/vespalib/src/tests/util/process_memory_stats/process_memory_stats_test.cpp @@ -0,0 +1,92 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/vespalib/util/process_memory_stats.h> +#include <vespa/vespalib/util/size_literals.h> +#include <iostream> +#include <fstream> +#include <sys/mman.h> +#include <fcntl.h> +#include <unistd.h> + + +using namespace vespalib; + +namespace { + +constexpr uint64_t SIZE_EPSILON = 4095; + +std::string toString(const ProcessMemoryStats &stats) +{ + std::ostringstream os; + os << "Mapped(" + << stats.getMappedVirt() << "," << stats.getMappedRss() << + "), Anonymous(" + << stats.getAnonymousVirt() << "," << stats.getAnonymousRss() << ")"; + return os.str(); +} + +} + +TEST("Simple stats") +{ + ProcessMemoryStats stats(ProcessMemoryStats::create(SIZE_EPSILON)); + std::cout << toString(stats) << std::endl; + EXPECT_LESS(0u, stats.getMappedVirt()); + EXPECT_LESS(0u, stats.getMappedRss()); + EXPECT_LESS(0u, stats.getAnonymousVirt()); + EXPECT_LESS(0u, stats.getAnonymousRss()); +} + +TEST("grow anonymous memory") +{ + ProcessMemoryStats stats1(ProcessMemoryStats::create(SIZE_EPSILON)); + std::cout << toString(stats1) << std::endl; + size_t mapLen = 64_Ki; + void *mapAddr = mmap(nullptr, mapLen, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + EXPECT_NOT_EQUAL(reinterpret_cast<void *>(-1), mapAddr); + ProcessMemoryStats stats2(ProcessMemoryStats::create()); + std::cout << toString(stats2) << std::endl; + EXPECT_LESS_EQUAL(stats1.getAnonymousVirt() + mapLen, + stats2.getAnonymousVirt()); + memset(mapAddr, 1, mapLen); + ProcessMemoryStats stats3(ProcessMemoryStats::create(SIZE_EPSILON)); + std::cout << toString(stats3) << std::endl; + // Cannot check that resident grows if swap is enabled and system loaded + munmap(mapAddr, mapLen); +} + +TEST("grow mapped memory") +{ + std::ofstream of("mapfile"); + size_t mapLen = 64_Ki; + std::vector<char> buf(mapLen, 4); + of.write(&buf[0], buf.size()); + of.close(); + int mapfileFileDescriptor = open("mapfile", O_RDONLY, 0666); + EXPECT_LESS_EQUAL(0, mapfileFileDescriptor); + ProcessMemoryStats stats1(ProcessMemoryStats::create(SIZE_EPSILON)); + std::cout << toString(stats1) << std::endl; + void *mapAddr = mmap(nullptr, mapLen, PROT_READ, MAP_SHARED, + mapfileFileDescriptor, 0); + EXPECT_NOT_EQUAL(reinterpret_cast<void *>(-1), mapAddr); + ProcessMemoryStats stats2(ProcessMemoryStats::create(SIZE_EPSILON)); + std::cout << toString(stats2) << std::endl; + EXPECT_LESS_EQUAL(stats1.getMappedVirt() + mapLen, stats2.getMappedVirt()); + EXPECT_EQUAL(0, memcmp(mapAddr, &buf[0], mapLen)); + ProcessMemoryStats stats3(ProcessMemoryStats::create(SIZE_EPSILON)); + std::cout << toString(stats3) << std::endl; + // Cannot check that resident grows if swap is enabled and system loaded + munmap(mapAddr, mapLen); +} + +TEST("order samples") +{ + ProcessMemoryStats a(0,0,0,7,0); + ProcessMemoryStats b(0,0,0,8,0); + EXPECT_TRUE(a < b); + EXPECT_FALSE(b < a); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/tests/util/process_memory_stats/process_memory_stats_test.sh b/vespalib/src/tests/util/process_memory_stats/process_memory_stats_test.sh new file mode 100755 index 00000000000..7fe5261ab2d --- /dev/null +++ b/vespalib/src/tests/util/process_memory_stats/process_memory_stats_test.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +set -e +rm -f mapfile +$VALGRIND ./vespalib_process_memory_stats_test_app +rm -f mapfile diff --git a/vespalib/src/tests/xmlserializable/.gitignore b/vespalib/src/tests/xmlserializable/.gitignore new file mode 100644 index 00000000000..8573da1cd93 --- /dev/null +++ b/vespalib/src/tests/xmlserializable/.gitignore @@ -0,0 +1,4 @@ +*_test +.depend +Makefile +vespalib_xmlserializable_test_app diff --git a/vespalib/src/tests/xmlserializable/CMakeLists.txt b/vespalib/src/tests/xmlserializable/CMakeLists.txt new file mode 100644 index 00000000000..119a1294253 --- /dev/null +++ b/vespalib/src/tests/xmlserializable/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_xmlserializable_test_app TEST + SOURCES + xmlserializabletest.cpp + DEPENDS + vespalib +) +vespa_add_test(NAME vespalib_xmlserializable_test_app COMMAND vespalib_xmlserializable_test_app) diff --git a/vespalib/src/tests/xmlserializable/xmlserializabletest.cpp b/vespalib/src/tests/xmlserializable/xmlserializabletest.cpp new file mode 100644 index 00000000000..cc8d61cb7c2 --- /dev/null +++ b/vespalib/src/tests/xmlserializable/xmlserializabletest.cpp @@ -0,0 +1,163 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/xmlstream.h> + +namespace vespalib { + +class Test : public vespalib::TestApp +{ +public: + void testNormalUsage(); + void testEscaping(); + void testNesting(); + void testIndent(); + + int Main() override; +}; + +int +Test::Main() +{ + TEST_INIT("xmlserializables_test"); + srandom(1); + testNormalUsage(); + testEscaping(); + testNesting(); + testIndent(); + TEST_DONE(); +} + +void +Test::testNormalUsage() +{ + std::ostringstream ost; + XmlOutputStream xos(ost); + using namespace vespalib::xml; + xos << XmlTag("car") + << XmlTag("door") + << XmlAttribute("windowstate", "up") + << XmlEndTag() + << XmlTag("description") + << "This is a car description used to test" + << XmlEndTag() + << XmlEndTag(); + std::string expected = + "<car>\n" + "<door windowstate=\"up\"/>\n" + "<description>This is a car description used to test</description>\n" + "</car>"; + EXPECT_EQUAL(expected, ost.str()); +} + +void +Test::testEscaping() +{ + std::ostringstream ost; + XmlOutputStream xos(ost); + using namespace vespalib::xml; + xos << XmlTag("!#trash%-", XmlTagFlags::CONVERT_ILLEGAL_CHARACTERS) + << XmlTag("foo") + << XmlAttribute("bar", "<100%\" &\n>") + << XmlEndTag() + << XmlTag("escaped") + << XmlEscapedContent() + << XmlContentWrapper("<>&\"'% \r\n\t\f\0", 12) + << XmlEndTag() + << XmlTag("encoded") + << XmlBase64Content() + << XmlContentWrapper("<>&\"'% \t\f\0", 10) + << XmlEndTag() + << XmlTag("auto1") + << XmlContentWrapper("<>&\t\f\r\nfoo", 10) + << XmlEndTag() + << XmlTag("auto2") + << XmlContentWrapper("<>&\t\0\r\nfoo", 10) + << XmlEndTag() + << XmlEndTag(); + std::string expected = + "<__trash_->\n" + "<foo bar=\"<100%" & >\"/>\n" + "<escaped><>&\"'% \n	�</escaped>\n" + "<encoded binaryencoding=\"base64\">PD4mIiclIAkMAA==</encoded>\n" + "<auto1><>&	 \nfoo</auto1>\n" + "<auto2 binaryencoding=\"base64\">PD4mCQANCmZvbw==</auto2>\n" + "</__trash_->"; + EXPECT_EQUAL(expected, ost.str()); +} + +namespace { + struct LookAndFeel : public XmlSerializable { + + LookAndFeel() {} + + void printXml(XmlOutputStream& out) const override { + using namespace vespalib::xml; + out << XmlAttribute("color", "blue") + << XmlTag("other") + << XmlAttribute("count", 5) + << XmlTag("something") << "foo" << XmlEndTag() + << XmlTag("else") << "bar" << XmlEndTag() + << XmlEndTag(); + } + }; +} + +void +Test::testNesting() +{ + std::ostringstream ost; + XmlOutputStream xos(ost); + using namespace vespalib::xml; + xos << XmlTag("car") + << XmlTag("door") + << LookAndFeel() + << XmlEndTag() + << XmlTag("description") + << "This is a car description used to test" + << XmlEndTag() + << XmlEndTag(); + std::string expected = + "<car>\n" + "<door color=\"blue\">\n" + "<other count=\"5\">\n" + "<something>foo</something>\n" + "<else>bar</else>\n" + "</other>\n" + "</door>\n" + "<description>This is a car description used to test</description>\n" + "</car>"; + EXPECT_EQUAL(expected, ost.str()); +} + +void +Test::testIndent() +{ + std::ostringstream ost; + XmlOutputStream xos(ost, " "); + using namespace vespalib::xml; + xos << XmlTag("foo") + << XmlTag("bar") << 2.14 << XmlEndTag() + << "Litt innhold" + << XmlTag("nytag") + << "Mer innhold" + << XmlTag("base") + << XmlBase64Content() << "foobar" + << XmlEndTag() + << XmlEndTag() + << XmlEndTag(); + std::string expected = + "<foo>\n" + " <bar>2.14</bar>\n" + " Litt innhold\n" + " <nytag>\n" + " Mer innhold\n" + " <base binaryencoding=\"base64\">Zm9vYmFy</base>\n" + " </nytag>\n" + "</foo>"; + EXPECT_EQUAL(expected, ost.str()); +} + +} // vespalib + +TEST_APPHOOK(vespalib::Test) |