Skip to content

Commit

Permalink
initial dai::Path and test cases
Browse files Browse the repository at this point in the history
- fixes luxonis#352
  • Loading branch information
diablodale committed Feb 16, 2022
1 parent c6bdd3d commit 11cdcf5
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 0 deletions.
64 changes: 64 additions & 0 deletions include/depthai/utility/Path.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once
#include <codecvt>
#if(__cplusplus >= 201703L) || (_MSVC_LANG >= 201703L)
#include <filesystem>
#endif
#include <locale>
#include <string>

namespace dai {

/**
* @brief accepts utf-8, Windows wchar_t, or std::filesystem::path
* and returns the path in a type accepted by fstream()
*
*/
class Path {
public:
#if defined(_WIN32) && defined(_MSC_VER)
using value_type = wchar_t;
#else
// TODO C++20 char8_t
using value_type = char;
#endif
using string_type = std::basic_string<value_type>;

public:
Path() = default;
~Path() = default;
Path(const Path&) = default;
Path(Path&&) = default;
Path& operator=(const Path&) = default;
Path& operator=(Path&&) = default;

Path(string_type&& source) noexcept : nativePath(std::move(source)) {}
Path(const string_type& source) : nativePath(source) {}
Path(const value_type* source) : nativePath(string_type(source)) {}

#if defined(__cpp_lib_filesystem)
// Path(std::filesystem::path source) : nativePath(source) {}
Path(const std::filesystem::path& source) : nativePath(source) {}
#endif

#if defined(_WIN32) && defined(_MSC_VER)
// Path(std::string source) : nativePath(convert_utf8_to_wide(source)) {}
Path(const std::string& source) : nativePath(convert_utf8_to_wide(source)) {}
Path(const char* source) : nativePath(convert_utf8_to_wide(std::string(source))) {}
#endif

// implicitly convert *this into a string containing the native format
operator string_type() const noexcept {
return nativePath;
}

private:
string_type nativePath;

std::wstring convert_utf8_to_wide(const std::string& utf8string) {
//#pragma warning(suppress : 4996)
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(utf8string);
}
};

} // namespace dai
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,6 @@ dai_add_test(serialization_test src/serialization_test.cpp)

# Multiple devices test
dai_add_test(multiple_devices_test src/multiple_devices_test.cpp)

# Filesystem test
dai_add_test(filesystem_test src/filesystem_test.cpp)
99 changes: 99 additions & 0 deletions tests/src/filesystem_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

// Include depthai library
#include <cstdio>
#include <depthai/depthai.hpp>
#if(__cplusplus >= 201703L) || (_MSVC_LANG >= 201703L)
#include <filesystem>
#endif
#include <string>

#include "depthai/utility/Path.hpp"

#if defined(_WIN32) && defined(_MSC_VER)
#define NATIVETYPE std::wstring
#define MAKENATIVEx(x) L##x
#define MAKENATIVE(x) MAKENATIVEx(x)
#define DELETEFILE _wremove
#define NATIVELENGTH(x) static_cast<std::wstring>(x).length()
#else
#define NATIVETYPE std::string
#define MAKENATIVE(x) x
#define DELETEFILE std::remove
#define NATIVELENGTH(x) u8length(static_cast<std::string>(x).c_str())
#endif

#define PATH1 "C:\\dir1\\file1.txt"
#define PATH2 "file2.txt"
#define PATH3 "/dir3/dir33/file3.txt"
#define PATH4 u8"\u00e4\u00eb\u00ef\u00f6\u00fc\u00e1\u00e9\u00ed\u00f3\u00fa\u00df\u00c6\u002e\u010c\u011a\u0141"
#define FILETEXT "This is a test\n"

int u8length(const char* str) noexcept {
int count = 0;
for(; *str != 0; ++str) count += ((*str & 0xc0) != 0x80);
return count;
}

TEST_CASE("dai::Path utf-8 and native char set handling") {
const dai::Path emptyPath;
const dai::Path::string_type emptyString = emptyPath;
REQUIRE(emptyString.empty());

const dai::Path path1(PATH1);
const NATIVETYPE string1(path1);
REQUIRE(string1 == MAKENATIVE(PATH1));

const dai::Path path2(PATH2);
const NATIVETYPE string2(path2);
REQUIRE(string2 == MAKENATIVE(PATH2));

const dai::Path path3(PATH3);
const NATIVETYPE string3(path3);
REQUIRE(string3 == MAKENATIVE(PATH3));

char tmp_name4[L_tmpnam];
REQUIRE(std::tmpnam(tmp_name4) != nullptr);
std::string string4(tmp_name4);
string4 += PATH4;
const dai::Path path4(string4);
REQUIRE(u8length(string4.c_str()) == NATIVELENGTH(path4));

REQUIRE_NOTHROW([&]() {
std::ofstream file(path4);
file.write(FILETEXT, sizeof(FILETEXT) - 1);
}());
REQUIRE_NOTHROW([&]() {
std::ifstream file(path4, std::ios::binary);
if(!file.is_open() || !file.good() || file.bad()) {
throw std::runtime_error("file not found or corrupted");
}
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
const char c1 = file.get();
}());
REQUIRE(0 == DELETEFILE(static_cast<NATIVETYPE>(path4).c_str()));

auto getBlob = [](const dai::Path& path) -> bool { return !static_cast<NATIVETYPE>(path).empty(); };
REQUIRE(getBlob(string4));
REQUIRE(getBlob("mypath/myfile.blob"));
REQUIRE(getBlob([]() -> std::string { return "mypath/rvalue.blob"; }()));
#if defined(_WIN32) && defined(_MSC_VER)
REQUIRE(getBlob(L"mywidepath/myfile.blob"));
std::wstring move2ndTry(L"move my data on the second try");
REQUIRE(getBlob(move2ndTry));
REQUIRE(getBlob(std::move(move2ndTry)));
#endif

auto wrapper = [&getBlob](const dai::Path& path) -> bool { return getBlob(path); };
REQUIRE(wrapper("pass dai::Path across functions"));

#if defined(__cpp_lib_filesystem)
const std::filesystem::path fspath1(PATH1);
const std::filesystem::path fspath2(PATH2);
const std::filesystem::path fspath3(PATH3);
REQUIRE(getBlob(fspath1));
REQUIRE(getBlob(fspath2));
REQUIRE(getBlob(fspath3));
#endif
}

0 comments on commit 11cdcf5

Please sign in to comment.