diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d8c264..01f4855 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ set(LIBREXY_LIBFLAGS "-lrexy") option(ENABLE_SHARED "Build shared library" ON) option(ENABLE_PROFILING "Enable asan" OFF) +option(BUILD_TESTS "Enable testing" OFF) mark_as_advanced(ENABLE_PROFILING) set(SOURCE_LIST "src/filerd.cpp" "src/string.cpp" "src/binary.cpp" "src/static_string.cpp") @@ -28,8 +29,12 @@ if(ENABLE_PROFILING) target_compile_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) endif() +if(BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() -set(LIBREXY_PUBLIC_HEADERS "include/rexy/traits.hpp" "include/rexy/steal.hpp" "include/rexy/binary.hpp" "include/rexy/expression.hpp" "include/rexy/binary_base.hpp" "include/rexy/binary_base.tpp" "include/rexy/string_base.hpp" "include/rexy/string.hpp" "include/rexy/filerd.hpp" "include/rexy/string_base.tpp") +set(LIBREXY_PUBLIC_HEADERS "include/rexy/traits.hpp" "include/rexy/steal.hpp" "include/rexy/binary.hpp" "include/rexy/expression.hpp" "include/rexy/binary_base.hpp" "include/rexy/binary_base.tpp" "include/rexy/string_base.hpp" "include/rexy/string.hpp" "include/rexy/filerd.hpp" "include/rexy/string_base.tpp" "include/rexy/allocator.hpp") target_compile_options(rexy PRIVATE -Wall -Wextra -pedantic -std=c++17) install(TARGETS rexy diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..d290f34 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.0.2) +project(rexylib_tests) +set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include) +include_directories("${INCLUDE_PATH}") +add_compile_options(-Wall -Wextra -pedantic -std=c++17) +link_libraries(rexy) + +if(ENABLE_PROFILING) + add_compile_options(-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) + add_link_options(-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) +endif() + +add_executable(basic_string "basic_string.cpp") +set_target_properties(basic_string PROPERTIES OUTPUT_NAME basic_string) + +add_test(NAME basic_string-test COMMAND basic_string) diff --git a/tests/basic_string.cpp b/tests/basic_string.cpp new file mode 100644 index 0000000..5f8989c --- /dev/null +++ b/tests/basic_string.cpp @@ -0,0 +1,249 @@ +#include "rexy/string.hpp" +#include "rexy/allocator.hpp" + +#include +#include +#include +#include + +[[noreturn]] void error(const char* str){ + fprintf(stderr, "%s", str); + exit(1); +} + +using test_str = rexy::basic_string>; + +void check_empty_construction(){ + test_str str1; + if(str1.length() != 0) + error("length() should return 0 on default init\n"); + if(test_str::uses_sso()){ + if(str1.get()[0] != 0) + error("get() should return an empty, zero length string\n"); + }else{ + if(str1.get() != nullptr) + error("get() should return a null string\n"); + } + if(str1.valid()) + error("valid() should return false on empty string\n"); + if(str1.get() != str1.c_str()) + error("c_str() should be a synonymn of get()\n"); + if(char* c = str1;c != str1.get()) + error("conversion to pointer type should be synonymous with get()\n"); + + test_str str2(str1); + if(str2.length() != str1.length()) + error("copy construction on empty string should give equivalent length()\n"); + if(str2.capacity() != str1.capacity()) + error("copy construction on empty string should give equivalent capacity()\n"); + if(test_str::uses_sso()){ + if(str2.get()[0] != str1.get()[0]) + error("copy construction on empty string should give equivalent get()\n"); + }else{ + if(str2.get() != str1.get()) + error("copy construction on empty string should give equivalent get()\n"); + } + + test_str str3(std::move(str2)); + if(str3.length() != str1.length()) + error("move construction on empty string should give equivalent length()\n"); + if(str3.capacity() != str1.capacity()) + error("move construction on empty string should give equivalent capacity()\n"); + if(test_str::uses_sso()){ + if(str3.get()[0] != str1.get()[0]) + error("move construction on empty string should give equivalent get()\n"); + }else{ + if(str3.get() != str1.get()) + error("move construction on empty string should give equivalent get()\n"); + } +} + +void check_short_construction(){ + if(!test_str::uses_sso()) + return; + + test_str::size_type cap = test_str::short_string_size(); + test_str str1("a"); + if(str1.length() != 1) + error("short constructed string 'a' should be length() == 1\n"); + if(str1.capacity() != cap) + error("short constructed string 'a' should be capacity() == short_string_size()\n"); + if(strcmp(str1.get(), "a")) + error("short constructed string 'a' should be !strcmp(get(), \"a\")\n"); + + test_str str2(str1); + if(str2.length() != str1.length()) + error("short copy constructed string should have equal length()\n"); + if(str2.capacity() != str1.capacity()) + error("short copy constructed string should have equal capacity()\n"); + if(strcmp(str2.get(), str1.get())) + error("short copy constructed string should have equivalent get()\n"); + + test_str str3(std::move(str2)); + if(str3.length() != str1.length()) + error("short move constructed string should have equal length()\n"); + if(str3.capacity() != str1.capacity()) + error("short move constructed string should have equal capacity()\n"); + if(strcmp(str3.get(), str1.get())) + error("short move constructed string should have equivalent get()\n"); +} +void check_long_construction(){ + const char* data = "this is a really long string that should ensure that it makes a dynamic allocation even if it has a big buffer."; + size_t len = strlen(data); + test_str str1(data); + if(str1.length() != len) + error("long constructed string should be length() == strlen(data)\n"); + if(str1.capacity() < len) + error("long constructed string should be capacity() >= strlen(data)\n"); + if(strcmp(str1.get(), data)) + error("long constructed string should be !strcmp(get(), data)\n"); + + test_str str2(str1); + if(str2.length() != str1.length()) + error("long copy constructed string should have equal length()\n"); + if(str2.capacity() != str1.capacity()) + error("long copy constructed string should have equal capacity()\n"); + if(strcmp(str2.get(), str1.get())) + error("long copy constructed string should have equivalent get()\n"); + + test_str str3(std::move(str2)); + if(str3.length() != str1.length()) + error("long move constructed string should have equal length()\n"); + if(str3.capacity() != str1.capacity()) + error("long move constructed string should have equal capacity()\n"); + if(strcmp(str3.get(), str1.get())) + error("long move constructed string should have equivalent get()\n"); +} +void check_short_assignment(){ + if(!test_str::uses_sso()) + return; + + const char* longstartdata = "this is another really long string that should ensure that it makes some sort of dyn alloc for big buf"; + + test_str::size_type cap = test_str::short_string_size(); + test_str str1("zy"); + str1 = "a"; + if(str1.length() != 1) + error("short assigned string 'a' should be length() == 1\n"); + if(str1.capacity() != cap) + error("short assigned string 'a' should be capacity() == short_string_size()\n"); + if(strcmp(str1.get(), "a")) + error("short assigned string 'a' should be !strcmp(get(), \"a\")\n"); + + test_str str2("ba"); + str2 = str1; + if(str2.length() != str1.length()) + error("short copy assigned string should have equal length()\n"); + if(str2.capacity() != str1.capacity()) + error("short copy assigned string should have equal capacity()\n"); + if(strcmp(str2.get(), str1.get())) + error("short copy assigned string should have equivalent get()\n"); + + test_str str3("cb"); + str3 = std::move(str2); + if(str3.length() != str1.length()) + error("short move assigned string should have equal length()\n"); + if(str3.capacity() != str1.capacity()) + error("short move assigned string should have equal capacity()\n"); + if(strcmp(str3.get(), str1.get())) + error("short move assigned string should have equivalent get()\n"); + + test_str str4(longstartdata); + str4 = str1; + if(str4.length() != str1.length()) + error("long->short copy assigned string should have equal length()\n"); + if(str4.capacity() < str1.capacity()) + error("long->short copy assigned string should have equal or greater capacity()\n"); + if(strcmp(str4.get(), str1.get())) + error("long->short copy assigned string should have equivalent get()\n"); + + test_str str5(longstartdata); + str5 = std::move(str4); + if(str5.length() != str1.length()) + error("long->short move assigned string should have equal length()\n"); + if(str5.capacity() < str1.capacity()) + error("long->short move assigned string should have equal or greater capacity()\n"); + if(strcmp(str5.get(), str1.get())) + error("long->short move assigned string should have equivalent get()\n"); +} +void check_long_assignment(){ + const char* startdata1 = "this is another really long string that should ensure that it makes some sort of dyn alloc for big buf"; + const char* startdata2 = "zy"; + const char* data = "this is a really long string that should ensure that it makes a dynamic allocation even if it has a big buffer."; + size_t len = strlen(data); + test_str str1(startdata1); + str1 = data; + if(str1.length() != len) + error("long assigned string should be length() == strlen(data)\n"); + if(str1.capacity() < len) + error("long assigned string should be capacity() >= strlen(data)\n"); + if(strcmp(str1.get(), data)) + error("long assigned string should be !strcmp(get(), data)\n"); + + test_str str2(startdata1); + str2 = str1; + if(str2.length() != str1.length()) + error("long copy assigned string should have equal length()\n"); + if(str2.capacity() != str1.capacity()) + error("long copy assigned string should have equal capacity()\n"); + if(strcmp(str2.get(), str1.get())) + error("long copy assigned string should have equivalent get()\n"); + + test_str str3(startdata1); + str3 = std::move(str2); + if(str3.length() != str1.length()) + error("long move assigned string should have equal length()\n"); + if(str3.capacity() != str1.capacity()) + error("long move assigned string should have equal capacity()\n"); + if(strcmp(str3.get(), str1.get())) + error("long move assigned string should have equivalent get()\n"); + + test_str str4(startdata2); + str4 = str1; + if(str4.length() != str1.length()) + error("short->long copy assigned string should have equal length()\n"); + if(str4.capacity() != str1.capacity()) + error("short->long copy assigned string should have equal capacity()\n"); + if(strcmp(str4.get(), str1.get())) + error("short->long copy assigned string should have equivalent get()\n"); + + test_str str5(startdata2); + str5 = std::move(str4); + if(str5.length() != str1.length()) + error("short->long move assigned string should have equal length()\n"); + if(str5.capacity() != str1.capacity()) + error("short->long move assigned string should have equal capacity()\n"); + if(strcmp(str5.get(), str1.get())) + error("short->long move assigned string should have equivalent get()\n"); +} +void check_short_append(){ + test_str str1; + test_str str2("bc"); + test_str str3("really long string that should trigger a short to long conversion in the string"); + str1.append("a"); + str1.append("b"); + str1.append(str2); + if(strcmp(str1.get(), "abbc")) + error("short append should have resulted in abbc\n"); + str1.append(str3); + if(strcmp(str1, "abbcreally long string that should trigger a short to long conversion in the string")) + error("short->long append should have resulted in abbcreally long string that should trigger a short to long conversion in the string\n"); +} +void check_long_append(){ + const char* startdata1 = "this is another really long string that should ensure that it makes some sort of dyn alloc for big buf"; + const char* appendeddata = "this is another really long string that should ensure that it makes some sort of dyn alloc for big bufstuff"; + test_str str1(startdata1); + str1.append("stuff"); + if(strcmp(str1.get(), appendeddata)) + error("long append should have resulted in this is another really long string that should ensure that it makes some sort of dyn alloc for big bufstuff"); +} + +int main(){ + check_empty_construction(); + check_short_construction(); + check_long_construction(); + check_short_assignment(); + check_long_assignment(); + check_short_append(); + check_long_append(); +}