diff --git a/docs/README.md b/docs/README.md index bbfefc1..5978cb1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -66,6 +66,7 @@ int main(void) { ```bash meson setup build meson compile -C build +meson test -C build ``` ### Build Library Only diff --git a/meson.build b/meson.build index dbb8a31..19c1502 100644 --- a/meson.build +++ b/meson.build @@ -78,6 +78,15 @@ if get_option('cli') install : false) endif +# Build unit tests if get_option('tests') - # TODO + add_languages('cpp', required: true) + + # get gtest + gtest_proj = subproject('gtest') + gtest_dep = gtest_proj.get_variable('gtest_dep') + gmock_dep = gtest_proj.get_variable('gmock_dep') + + # build tests + subdir('tests') endif diff --git a/src/windows.c b/src/windows.c index 693ce86..480d4bd 100644 --- a/src/windows.c +++ b/src/windows.c @@ -117,27 +117,38 @@ char *envuGetDirectory(const char *path) { char *copied_path = AllocStrWithConst(path); char *p = copied_path; - char *last_slash_p = NULL; - char *second_slash_p = NULL; + char *slash_p[3] = { NULL, NULL, NULL }; while (*p != '\0') { if (*p == '\\' || *p == '/') { - second_slash_p = last_slash_p; - last_slash_p = p; + // store the last three slashes. + slash_p[2] = slash_p[1]; + slash_p[1] = slash_p[0]; + slash_p[0] = p; } p++; } - if (last_slash_p == NULL) { + if (slash_p[0] == NULL) { + // slash not found envuFree(copied_path); return AllocStrWithConst("."); } - if (last_slash_p + 1 == p) { - if (second_slash_p == NULL) { + if (slash_p[0] + 1 == p) { + // the last character is a slash + if (slash_p[1] == NULL) { + // only the last character is a slash envuFree(copied_path); return AllocStrWithConst("."); } - last_slash_p = second_slash_p; + slash_p[0] = slash_p[1]; + slash_p[1] = slash_p[2]; + } + if (slash_p[1] == NULL) { + // keep the last slash because there is no other slashes. + slash_p[0][1] = '\0'; + } else { + // overwrite the last slash with null + slash_p[0][0] = '\0'; } - last_slash_p[1] = '\0'; char *str = AllocStrWithConst(copied_path); envuFree(copied_path); return str; diff --git a/subprojects/gtest.wrap b/subprojects/gtest.wrap new file mode 100644 index 0000000..ca5d699 --- /dev/null +++ b/subprojects/gtest.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory = googletest-release-1.10.0 + +source_url = https://github.com/google/googletest/archive/release-1.10.0.zip +source_filename = gtest-1.10.0.zip +source_hash = 94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91 + +patch_url = https://wrapdb.mesonbuild.com/v1/projects/gtest/1.10.0/1/get_zip +patch_filename = gtest-1.10.0-1-wrap.zip +patch_hash = 04ff14e8880e4e465f6260221e9dfd56fea6bc7cce4c4aff0dc528e4a2c8f514 diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..de43564 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,14 @@ +#include +#include +#include "util_tests.hpp" +#include "true_env_info.h" + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + printf("true_env_info.h:\n"); + printf(" build dir: %s\n", TRUE_BUILD_DIR); + printf(" exe path: %s\n", TRUE_EXE_PATH); + printf(" cwd: %s\n", TRUE_CWD); + printf(" os: %s\n", TRUE_OS); + return RUN_ALL_TESTS(); +} diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..192fa24 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,26 @@ +if meson.is_cross_build() + error('unit tests does NOT support cross build.') +endif + +true_build_dir = meson.current_build_dir().replace('\\', '\\\\') +true_source_root = meson.project_source_root().replace('\\', '\\\\') +true_exe_name = 'env_utils_test' +if envu_OS == 'windows' + true_exe_name += '.exe' +endif + +test_conf = configuration_data() +test_conf.set('BUILD_DIR', true_build_dir) +test_conf.set('SOURCE_ROOT', true_source_root) +test_conf.set('EXE_NAME', true_exe_name) +test_conf.set('OS', envu_OS) +configure_file(input : 'true_env_info.h.in', + output : 'true_env_info.h', + configuration : test_conf) + +test_exe = executable('env_utils_test', + 'main.cpp', + dependencies : [env_utils_dep, gtest_dep, gmock_dep], + install : false) + +test('env_utils_test', test_exe, workdir: true_source_root) diff --git a/tests/true_env_info.h.in b/tests/true_env_info.h.in new file mode 100644 index 0000000..06fa69b --- /dev/null +++ b/tests/true_env_info.h.in @@ -0,0 +1,5 @@ +#pragma once +const char *TRUE_BUILD_DIR = "@BUILD_DIR@"; +const char *TRUE_EXE_PATH = "@BUILD_DIR@\\\\@EXE_NAME@"; +const char *TRUE_CWD = "@SOURCE_ROOT@"; +const char *TRUE_OS = "@OS@"; diff --git a/tests/util_tests.hpp b/tests/util_tests.hpp new file mode 100644 index 0000000..a7644af --- /dev/null +++ b/tests/util_tests.hpp @@ -0,0 +1,42 @@ +#pragma once +#include +#include "env_utils.h" +#include "true_env_info.h" +#include +#include +#include + +TEST(UtilTest, envuGetVersion) { + ASSERT_STREQ(ENVU_VERSION, envuGetVersion()); +} + +TEST(UtilTest, envuGetVersionInt) { + ASSERT_EQ(ENVU_VERSION_INT, envuGetVersionAsInt()); +} + +TEST(UtilTest, envuGetOS) { + char* os = envuGetOS(); + std::string os_lower(os); + std::transform(os_lower.begin(), os_lower.end(), os_lower.begin(), + [](unsigned char c){ return std::tolower(c); }); + envuFree(os); + ASSERT_STREQ(TRUE_OS, os_lower.c_str()); +} + +TEST(UtilTest, envuGetExecutablePath) { + char* exe_path = envuGetExecutablePath(); + ASSERT_STREQ(TRUE_EXE_PATH, exe_path); + envuFree(exe_path); +} + +TEST(UtilTest, envuGetExecutableDir) { + char* exe_dir = envuGetExecutableDir(); + ASSERT_STREQ(TRUE_BUILD_DIR, exe_dir); + envuFree(exe_dir); +} + +TEST(UtilTest, envuGetCwd) { + char* cwd = envuGetCwd(); + ASSERT_STREQ(TRUE_CWD, cwd); + envuFree(cwd); +}