Collect together and rigorously test all our ad-hoc path functions.
parent
60f85f5a39
commit
335c217114
|
@ -33,10 +33,12 @@ set(libslvs_SOURCES
|
|||
expr.cpp
|
||||
constraint.cpp
|
||||
constrainteq.cpp
|
||||
system.cpp)
|
||||
system.cpp
|
||||
platform/platform.cpp)
|
||||
|
||||
set(libslvs_HEADERS
|
||||
solvespace.h)
|
||||
solvespace.h
|
||||
platform/platform.h)
|
||||
|
||||
add_library(slvs SHARED
|
||||
${libslvs_SOURCES}
|
||||
|
@ -139,6 +141,7 @@ set(solvespace_core_HEADERS
|
|||
sketch.h
|
||||
solvespace.h
|
||||
ui.h
|
||||
platform/platform.h
|
||||
render/render.h
|
||||
render/gl2shader.h
|
||||
srf/surface.h)
|
||||
|
@ -180,6 +183,7 @@ set(solvespace_core_SOURCES
|
|||
undoredo.cpp
|
||||
util.cpp
|
||||
view.cpp
|
||||
platform/platform.cpp
|
||||
render/render.cpp
|
||||
render/render2d.cpp
|
||||
srf/boolean.cpp
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Common platform-dependent functionality.
|
||||
//
|
||||
// Copyright 2017 whitequark
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined(__APPLE__)
|
||||
# include <CoreFoundation/CFString.h>
|
||||
#endif
|
||||
#include "solvespace.h"
|
||||
#if defined(WIN32)
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace SolveSpace {
|
||||
|
||||
using namespace Platform;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Utility functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static std::vector<std::string> Split(const std::string &joined, char separator) {
|
||||
std::vector<std::string> parts;
|
||||
|
||||
size_t oldpos = 0, pos = 0;
|
||||
while(true) {
|
||||
oldpos = pos;
|
||||
pos = joined.find(separator, pos);
|
||||
if(pos == std::string::npos) break;
|
||||
parts.push_back(joined.substr(oldpos, pos - oldpos));
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
if(oldpos != joined.length() - 1) {
|
||||
parts.push_back(joined.substr(oldpos));
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
static std::string Concat(const std::vector<std::string> &parts, char separator) {
|
||||
std::string joined;
|
||||
|
||||
bool first = true;
|
||||
for(auto &part : parts) {
|
||||
if(!first) joined += separator;
|
||||
joined += part;
|
||||
first = false;
|
||||
}
|
||||
|
||||
return joined;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Path manipulation.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined(WIN32)
|
||||
const char SEPARATOR = '\\';
|
||||
#else
|
||||
const char SEPARATOR = '/';
|
||||
#endif
|
||||
|
||||
Path Path::From(std::string raw) {
|
||||
Path path = { raw };
|
||||
return path;
|
||||
}
|
||||
|
||||
Path Path::CurrentDirectory() {
|
||||
#if defined(WIN32)
|
||||
// On Windows, ssfopen needs an absolute UNC path proper, so get that.
|
||||
std::wstring rawW;
|
||||
rawW.resize(GetCurrentDirectoryW(0, NULL));
|
||||
DWORD length = GetCurrentDirectoryW((int)rawW.length(), &rawW[0]);
|
||||
ssassert(length > 0 && length == rawW.length() - 1, "Cannot get current directory");
|
||||
rawW.resize(length);
|
||||
return From(Narrow(rawW));
|
||||
#else
|
||||
char *raw = getcwd(NULL, 0);
|
||||
ssassert(raw != NULL, "Cannot get current directory");
|
||||
Path path = From(raw);
|
||||
free(raw);
|
||||
return path;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Path::FileName() const {
|
||||
std::string fileName = raw;
|
||||
size_t slash = fileName.rfind(SEPARATOR);
|
||||
if(slash != std::string::npos) {
|
||||
fileName = fileName.substr(slash + 1);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
std::string Path::FileStem() const {
|
||||
std::string baseName = FileName();
|
||||
size_t dot = baseName.rfind('.');
|
||||
if(dot != std::string::npos) {
|
||||
baseName = baseName.substr(0, dot);
|
||||
}
|
||||
return baseName;
|
||||
}
|
||||
|
||||
std::string Path::Extension() const {
|
||||
size_t dot = raw.rfind('.');
|
||||
if(dot != std::string::npos) {
|
||||
return raw.substr(dot + 1);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool Path::HasExtension(std::string theirExt) const {
|
||||
std::string ourExt = Extension();
|
||||
std::transform(ourExt.begin(), ourExt.end(), ourExt.begin(), ::tolower);
|
||||
std::transform(theirExt.begin(), theirExt.end(), theirExt.begin(), ::tolower);
|
||||
return ourExt == theirExt;
|
||||
}
|
||||
|
||||
Path Path::WithExtension(std::string ext) const {
|
||||
Path withExt = *this;
|
||||
size_t dot = withExt.raw.rfind('.');
|
||||
if(dot != std::string::npos) {
|
||||
withExt.raw.erase(dot);
|
||||
}
|
||||
withExt.raw += ".";
|
||||
withExt.raw += ext;
|
||||
return withExt;
|
||||
}
|
||||
|
||||
static void FindPrefix(const std::string &raw, size_t *pos) {
|
||||
*pos = std::string::npos;
|
||||
#if defined(WIN32)
|
||||
if(raw.size() >= 7 && raw[2] == '?' && raw[3] == '\\' &&
|
||||
isalpha(raw[4]) && raw[5] == ':' && raw[6] == '\\') {
|
||||
*pos = 7;
|
||||
} else if(raw.size() >= 3 && isalpha(raw[0]) && raw[1] == ':' && raw[2] == '\\') {
|
||||
*pos = 3;
|
||||
} else if(raw.size() >= 2 && raw[0] == '\\' && raw[1] == '\\') {
|
||||
size_t slashAt = raw.find('\\', 2);
|
||||
if(slashAt != std::string::npos) {
|
||||
*pos = raw.find('\\', slashAt + 1);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if(raw.size() >= 1 && raw[0] == '/') {
|
||||
*pos = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Path::IsAbsolute() const {
|
||||
size_t pos;
|
||||
FindPrefix(raw, &pos);
|
||||
return pos != std::string::npos;
|
||||
}
|
||||
|
||||
// Removes one component from the end of the path.
|
||||
// Returns an empty path if the path consists only of a root.
|
||||
Path Path::Parent() const {
|
||||
Path parent = { raw };
|
||||
if(!parent.raw.empty() && parent.raw.back() == SEPARATOR) {
|
||||
parent.raw.pop_back();
|
||||
}
|
||||
size_t slash = parent.raw.rfind(SEPARATOR);
|
||||
if(slash != std::string::npos) {
|
||||
parent.raw = parent.raw.substr(0, slash + 1);
|
||||
} else {
|
||||
parent.raw.clear();
|
||||
}
|
||||
if(IsAbsolute() && !parent.IsAbsolute()) {
|
||||
return From("");
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
// Concatenates a component to this path.
|
||||
// Returns an empty path if this path or the component is empty.
|
||||
Path Path::Join(const std::string &component) const {
|
||||
ssassert(component.find(SEPARATOR) == std::string::npos,
|
||||
"Use the Path::Join(const Path &) overload to append an entire path");
|
||||
return Join(Path::From(component));
|
||||
}
|
||||
|
||||
// Concatenates a relative path to this path.
|
||||
// Returns an empty path if either path is empty, or the other path is absolute.
|
||||
Path Path::Join(const Path &other) const {
|
||||
if(IsEmpty() || other.IsEmpty() || other.IsAbsolute()) {
|
||||
return From("");
|
||||
}
|
||||
|
||||
Path joined = { raw };
|
||||
if(joined.raw.back() != SEPARATOR) {
|
||||
joined.raw += SEPARATOR;
|
||||
}
|
||||
joined.raw += other.raw;
|
||||
return joined;
|
||||
}
|
||||
|
||||
// Expands the "." and ".." components in this path.
|
||||
// On Windows, additionally prepends the UNC prefix to absolute paths without one.
|
||||
// Returns an empty path if a ".." component would escape from the root.
|
||||
Path Path::Expand(bool fromCurrentDirectory) const {
|
||||
Path source;
|
||||
Path expanded;
|
||||
|
||||
if(fromCurrentDirectory && !IsAbsolute()) {
|
||||
source = CurrentDirectory().Join(*this);
|
||||
} else {
|
||||
source = *this;
|
||||
}
|
||||
|
||||
size_t splitAt;
|
||||
FindPrefix(source.raw, &splitAt);
|
||||
if(splitAt != std::string::npos) {
|
||||
expanded.raw = source.raw.substr(0, splitAt);
|
||||
} else {
|
||||
splitAt = 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> expandedComponents;
|
||||
for(std::string component : Split(source.raw.substr(splitAt), SEPARATOR)) {
|
||||
if(component == ".") {
|
||||
// skip
|
||||
} else if(component == "..") {
|
||||
if(!expandedComponents.empty()) {
|
||||
expandedComponents.pop_back();
|
||||
} else {
|
||||
return From("");
|
||||
}
|
||||
} else if(!component.empty()) {
|
||||
expandedComponents.push_back(component);
|
||||
}
|
||||
}
|
||||
|
||||
if(expanded.IsEmpty()) {
|
||||
if(expandedComponents.empty()) {
|
||||
expandedComponents.push_back(".");
|
||||
}
|
||||
expanded = From(Concat(expandedComponents, SEPARATOR));
|
||||
} else if(!expandedComponents.empty()) {
|
||||
expanded = expanded.Join(From(Concat(expandedComponents, SEPARATOR)));
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
if(expanded.IsAbsolute() && expanded.raw.substr(0, 2) != "\\\\") {
|
||||
expanded.raw = "\\\\?\\" + expanded.raw;
|
||||
}
|
||||
#endif
|
||||
|
||||
return expanded;
|
||||
}
|
||||
|
||||
static std::string FilesystemNormalize(const std::string &str) {
|
||||
#if defined(WIN32)
|
||||
std::wstring strW = Widen(str);
|
||||
std::transform(strW.begin(), strW.end(), strW.begin(), towlower);
|
||||
return Narrow(strW);
|
||||
#elif defined(__APPLE__)
|
||||
CFMutableStringRef cfStr =
|
||||
CFStringCreateMutableCopy(NULL, 0,
|
||||
CFStringCreateWithBytesNoCopy(NULL, (const UInt8*)str.data(), str.size(),
|
||||
kCFStringEncodingUTF8, /*isExternalRepresentation=*/false, kCFAllocatorNull));
|
||||
CFStringLowercase(cfStr, NULL);
|
||||
std::string normalizedStr;
|
||||
normalizedStr.resize(CFStringGetMaximumSizeOfFileSystemRepresentation(cfStr));
|
||||
CFStringGetFileSystemRepresentation(cfStr, &normalizedStr[0], normalizedStr.size());
|
||||
return normalizedStr;
|
||||
#else
|
||||
return str;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Path::Equals(const Path &other) const {
|
||||
return FilesystemNormalize(raw) == FilesystemNormalize(other.raw);
|
||||
}
|
||||
|
||||
// Returns a relative path from a given base path.
|
||||
// Returns an empty path if any of the paths is not absolute, or
|
||||
// if they belong to different roots, or
|
||||
// if they cannot be expanded.
|
||||
Path Path::RelativeTo(const Path &base) const {
|
||||
Path expanded = Expand();
|
||||
Path baseExpanded = base.Expand();
|
||||
if(!(expanded.IsAbsolute() && baseExpanded.IsAbsolute())){
|
||||
return From("");
|
||||
}
|
||||
|
||||
size_t splitAt;
|
||||
FindPrefix(expanded.raw, &splitAt);
|
||||
size_t baseSplitAt;
|
||||
FindPrefix(baseExpanded.raw, &baseSplitAt);
|
||||
if(FilesystemNormalize(expanded.raw.substr(0, splitAt)) !=
|
||||
FilesystemNormalize(baseExpanded.raw.substr(0, splitAt))) {
|
||||
return From("");
|
||||
}
|
||||
|
||||
std::vector<std::string> components =
|
||||
Split(expanded.raw.substr(splitAt), SEPARATOR);
|
||||
std::vector<std::string> baseComponents =
|
||||
Split(baseExpanded.raw.substr(baseSplitAt), SEPARATOR);
|
||||
size_t common;
|
||||
for(common = 0; common < baseComponents.size() &&
|
||||
common < components.size(); common++) {
|
||||
if(FilesystemNormalize(baseComponents[common]) !=
|
||||
FilesystemNormalize(components[common])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> resultComponents;
|
||||
for(size_t i = common; i < baseComponents.size(); i++) {
|
||||
resultComponents.push_back("..");
|
||||
}
|
||||
resultComponents.insert(resultComponents.end(),
|
||||
components.begin() + common, components.end());
|
||||
if(resultComponents.empty()) {
|
||||
resultComponents.push_back(".");
|
||||
}
|
||||
return From(Concat(resultComponents, SEPARATOR));
|
||||
}
|
||||
|
||||
Path Path::FromPortable(const std::string &repr) {
|
||||
return From(Concat(Split(repr, '/'), SEPARATOR));
|
||||
}
|
||||
|
||||
std::string Path::ToPortable() const {
|
||||
ssassert(!IsAbsolute(), "absolute paths cannot be made portable");
|
||||
|
||||
return Concat(Split(raw, SEPARATOR), '/');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Common platform-dependent functionality.
|
||||
//
|
||||
// Copyright 2017 whitequark
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef SOLVESPACE_PLATFORM_H
|
||||
#define SOLVESPACE_PLATFORM_H
|
||||
|
||||
namespace Platform {
|
||||
|
||||
// A filesystem path, respecting the conventions of the current platform.
|
||||
// Transformation functions return an empty path on error.
|
||||
class Path {
|
||||
public:
|
||||
std::string raw;
|
||||
|
||||
static Path From(std::string raw);
|
||||
static Path CurrentDirectory();
|
||||
|
||||
void Clear() { raw.clear(); }
|
||||
|
||||
bool Equals(const Path &other) const;
|
||||
bool IsEmpty() const { return raw.empty(); }
|
||||
bool IsAbsolute() const;
|
||||
bool HasExtension(std::string ext) const;
|
||||
|
||||
std::string FileName() const;
|
||||
std::string FileStem() const;
|
||||
std::string Extension() const;
|
||||
|
||||
Path WithExtension(std::string ext) const;
|
||||
Path Parent() const;
|
||||
Path Join(const std::string &component) const;
|
||||
Path Join(const Path &other) const;
|
||||
Path Expand(bool fromCurrentDirectory = false) const;
|
||||
Path RelativeTo(const Path &base) const;
|
||||
|
||||
// Converting to and from a platform-independent representation
|
||||
// (conventionally, the Unix one).
|
||||
static Path FromPortable(const std::string &repr);
|
||||
std::string ToPortable() const;
|
||||
};
|
||||
|
||||
struct PathLess {
|
||||
bool operator()(const Path &a, const Path &b) const { return a.raw < b.raw; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -141,6 +141,9 @@ enum class ContextCommand : uint32_t;
|
|||
|
||||
//================
|
||||
// From the platform-specific code.
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#define PATH_SEP "\\"
|
||||
#else
|
||||
|
|
|
@ -13,6 +13,7 @@ set(testsuite_SOURCES
|
|||
harness.cpp
|
||||
core/expr/test.cpp
|
||||
core/locale/test.cpp
|
||||
core/path/test.cpp
|
||||
constraint/points_coincident/test.cpp
|
||||
constraint/pt_pt_distance/test.cpp
|
||||
constraint/pt_plane_distance/test.cpp
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
#include "harness.h"
|
||||
|
||||
using Platform::Path;
|
||||
|
||||
#if defined(WIN32)
|
||||
#define S "\\"
|
||||
#define R "C:"
|
||||
#define U "\\\\?\\C:"
|
||||
#else
|
||||
#define S "/"
|
||||
#define R ""
|
||||
#define U ""
|
||||
#endif
|
||||
|
||||
TEST_CASE(from_raw) {
|
||||
Path path = Path::From("/foo");
|
||||
CHECK_EQ_STR(path.raw, "/foo");
|
||||
}
|
||||
|
||||
#if defined(WIN32) || defined(__APPLE__)
|
||||
TEST_CASE(equals_win32_apple) {
|
||||
CHECK_TRUE(Path::From(R S "foo").Equals(Path::From(R S "foo")));
|
||||
CHECK_TRUE(Path::From(R S "foo").Equals(Path::From(R S "FOO")));
|
||||
CHECK_FALSE(Path::From(R S "foo").Equals(Path::From(R S "bar")));
|
||||
}
|
||||
#else
|
||||
TEST_CASE(equals_unix) {
|
||||
CHECK_TRUE(Path::From(R S "foo").Equals(Path::From(R S "foo")));
|
||||
CHECK_FALSE(Path::From(R S "foo").Equals(Path::From(R S "FOO")));
|
||||
CHECK_FALSE(Path::From(R S "foo").Equals(Path::From(R S "bar")));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WIN32)
|
||||
TEST_CASE(is_absolute_win32) {
|
||||
CHECK_TRUE(Path::From("c:\\foo").IsAbsolute());
|
||||
CHECK_TRUE(Path::From("\\\\?\\c:\\").IsAbsolute());
|
||||
CHECK_TRUE(Path::From("\\\\server\\share\\").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("c:/foo").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("c:foo").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("\\\\?").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("\\\\server\\").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("\\\\?\\c:").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("\\\\server\\share").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("foo").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("/foo").IsAbsolute());
|
||||
}
|
||||
#else
|
||||
TEST_CASE(is_absolute_unix) {
|
||||
CHECK_TRUE(Path::From("/foo").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("c:/foo").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("c:\\foo").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("\\\\?\\foo").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("c:foo").IsAbsolute());
|
||||
CHECK_FALSE(Path::From("foo").IsAbsolute());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE(has_extension) {
|
||||
CHECK_TRUE(Path::From("foo.bar").HasExtension("bar"));
|
||||
CHECK_TRUE(Path::From("foo.bar").HasExtension("BAR"));
|
||||
CHECK_TRUE(Path::From("foo.bAr").HasExtension("BaR"));
|
||||
CHECK_TRUE(Path::From("foo.bar").HasExtension("bar"));
|
||||
CHECK_FALSE(Path::From("foo.bar").HasExtension("baz"));
|
||||
}
|
||||
|
||||
TEST_CASE(file_name) {
|
||||
CHECK_EQ_STR(Path::From("foo").FileName(), "foo");
|
||||
CHECK_EQ_STR(Path::From("foo" S "bar").FileName(), "bar");
|
||||
}
|
||||
|
||||
TEST_CASE(file_stem) {
|
||||
CHECK_EQ_STR(Path::From("foo").FileStem(), "foo");
|
||||
CHECK_EQ_STR(Path::From("foo" S "bar").FileStem(), "bar");
|
||||
CHECK_EQ_STR(Path::From("foo.ext").FileStem(), "foo");
|
||||
CHECK_EQ_STR(Path::From("foo" S "bar.ext").FileStem(), "bar");
|
||||
}
|
||||
|
||||
TEST_CASE(extension) {
|
||||
CHECK_EQ_STR(Path::From("foo").Extension(), "");
|
||||
CHECK_EQ_STR(Path::From("foo.bar").Extension(), "bar");
|
||||
CHECK_EQ_STR(Path::From("foo.bar.baz").Extension(), "baz");
|
||||
}
|
||||
|
||||
TEST_CASE(with_extension) {
|
||||
CHECK_EQ_STR(Path::From("foo.bar").WithExtension("baz").raw, "foo.baz");
|
||||
CHECK_EQ_STR(Path::From("foo").WithExtension("baz").raw, "foo.baz");
|
||||
}
|
||||
|
||||
TEST_CASE(parent) {
|
||||
Path path;
|
||||
path = Path::From("foo" S "bar");
|
||||
CHECK_EQ_STR(path.Parent().raw, "foo" S);
|
||||
path = Path::From("foo" S "bar" S);
|
||||
CHECK_EQ_STR(path.Parent().raw, "foo" S);
|
||||
path = Path::From(R S "foo" S "bar");
|
||||
CHECK_EQ_STR(path.Parent().raw, R S "foo" S);
|
||||
path = Path::From(R S "foo");
|
||||
CHECK_EQ_STR(path.Parent().raw, R S);
|
||||
|
||||
path = Path::From("");
|
||||
CHECK_TRUE(path.Parent().IsEmpty());
|
||||
path = Path::From("foo");
|
||||
CHECK_TRUE(path.Parent().IsEmpty());
|
||||
path = Path::From("foo" S);
|
||||
CHECK_TRUE(path.Parent().IsEmpty());
|
||||
path = Path::From(R S);
|
||||
CHECK_TRUE(path.Parent().IsEmpty());
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
TEST_CASE(parent_win32) {
|
||||
Path path;
|
||||
path = Path::From(U S);
|
||||
CHECK_TRUE(path.Parent().IsEmpty());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE(join) {
|
||||
Path path;
|
||||
path = Path::From("foo");
|
||||
CHECK_EQ_STR(path.Join(Path::From("bar")).raw, "foo" S "bar");
|
||||
path = Path::From("foo" S);
|
||||
CHECK_EQ_STR(path.Join(Path::From("bar")).raw, "foo" S "bar");
|
||||
|
||||
path = Path::From("");
|
||||
CHECK_TRUE(path.Join(Path::From("bar")).IsEmpty());
|
||||
path = Path::From("foo");
|
||||
CHECK_TRUE(path.Join(Path::From("")).IsEmpty());
|
||||
path = Path::From("foo");
|
||||
CHECK_TRUE(path.Join(Path::From(R S "bar")).IsEmpty());
|
||||
}
|
||||
|
||||
TEST_CASE(expand) {
|
||||
Path path;
|
||||
path = Path::From("foo");
|
||||
CHECK_EQ_STR(path.Expand().raw, "foo");
|
||||
path = Path::From("foo" S "bar");
|
||||
CHECK_EQ_STR(path.Expand().raw, "foo" S "bar");
|
||||
path = Path::From("foo" S);
|
||||
CHECK_EQ_STR(path.Expand().raw, "foo");
|
||||
path = Path::From("foo" S ".");
|
||||
CHECK_EQ_STR(path.Expand().raw, "foo");
|
||||
path = Path::From("foo" S "." S "bar");
|
||||
CHECK_EQ_STR(path.Expand().raw, "foo" S "bar");
|
||||
path = Path::From("foo" S ".." S "bar");
|
||||
CHECK_EQ_STR(path.Expand().raw, "bar");
|
||||
path = Path::From("foo" S "..");
|
||||
CHECK_EQ_STR(path.Expand().raw, ".");
|
||||
path = Path::From(R S "foo" S "..");
|
||||
CHECK_EQ_STR(path.Expand().raw, U S);
|
||||
path = Path::From(R S);
|
||||
CHECK_EQ_STR(path.Expand().raw, U S);
|
||||
|
||||
path = Path::From(R S "..");
|
||||
CHECK_TRUE(path.Expand().IsEmpty());
|
||||
path = Path::From(R S ".." S "foo");
|
||||
CHECK_TRUE(path.Expand().IsEmpty());
|
||||
path = Path::From("..");
|
||||
CHECK_TRUE(path.Expand().IsEmpty());
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
TEST_CASE(expand_win32) {
|
||||
Path path;
|
||||
path = Path::From(R S "foo");
|
||||
CHECK_EQ_STR(path.Expand().raw, U S "foo");
|
||||
path = Path::From(U S "foo");
|
||||
CHECK_EQ_STR(path.Expand().raw, U S "foo");
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE(expand_from_cwd) {
|
||||
Path cwd = Path::CurrentDirectory().Expand();
|
||||
|
||||
Path path;
|
||||
path = Path::From(R S "foo");
|
||||
CHECK_EQ_STR(path.Expand(/*fromCurrentDirectory=*/true).raw,
|
||||
U S "foo");
|
||||
path = Path::From("foo" S "bar");
|
||||
CHECK_EQ_STR(path.Expand(/*fromCurrentDirectory=*/true).raw,
|
||||
cwd.raw + S "foo" S "bar");
|
||||
path = Path::From(".." S "bar");
|
||||
CHECK_EQ_STR(path.Expand(/*fromCurrentDirectory=*/true).raw,
|
||||
cwd.Parent().raw + "bar");
|
||||
}
|
||||
|
||||
TEST_CASE(relative_to) {
|
||||
Path base;
|
||||
base = Path::From(R S "foo" S "bar");
|
||||
CHECK_EQ_STR(Path::From(R S "foo").RelativeTo(base).raw,
|
||||
"..");
|
||||
base = Path::From(R S "foo" S "bar");
|
||||
CHECK_EQ_STR(Path::From(R S "foo" S "baz").RelativeTo(base).raw,
|
||||
".." S "baz");
|
||||
base = Path::From(R S "foo" S "bar");
|
||||
CHECK_EQ_STR(Path::From(R S "foo" S "bar" S "quux").RelativeTo(base).raw,
|
||||
"quux");
|
||||
base = Path::From(R S "foo" S "bar");
|
||||
CHECK_EQ_STR(Path::From(R S "foo" S "bar").RelativeTo(base).raw,
|
||||
".");
|
||||
|
||||
base = Path::From("foo");
|
||||
CHECK_TRUE(Path::From(R S "foo" S "bar").RelativeTo(base).IsEmpty());
|
||||
base = Path::From(R S "foo" S "bar");
|
||||
CHECK_TRUE(Path::From("foo").RelativeTo(base).IsEmpty());
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
TEST_CASE(relative_to_win32) {
|
||||
Path base;
|
||||
base = Path::From("C:\\foo");
|
||||
CHECK_EQ_STR(Path::From("\\\\?\\C:\\bar").RelativeTo(base).raw,
|
||||
"..\\bar");
|
||||
base = Path::From("C:\\foo");
|
||||
CHECK_EQ_STR(Path::From("c:\\FOO").RelativeTo(base).raw,
|
||||
".");
|
||||
|
||||
base = Path::From("C:\\foo");
|
||||
CHECK_TRUE(Path::From("D:\\bar").RelativeTo(base).IsEmpty());
|
||||
CHECK_TRUE(Path::From("\\\\server\\share\\bar").RelativeTo(base).IsEmpty());
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
TEST_CASE(relative_to_apple) {
|
||||
Path base;
|
||||
base = Path::From("/users/foo");
|
||||
CHECK_EQ_STR(Path::From("/Users/FOO").RelativeTo(base).raw,
|
||||
".");
|
||||
}
|
||||
#else
|
||||
TEST_CASE(relative_to_unix) {
|
||||
Path base;
|
||||
base = Path::From("/users/foo");
|
||||
CHECK_EQ_STR(Path::From("/Users/FOO").RelativeTo(base).raw,
|
||||
"../../Users/FOO");
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE(from_portable) {
|
||||
CHECK_EQ_STR(Path::FromPortable("foo/bar").raw, "foo" S "bar");
|
||||
}
|
||||
|
||||
TEST_CASE(to_portable) {
|
||||
CHECK_EQ_STR(Path::From("foo" S "bar").ToPortable(), "foo/bar");
|
||||
}
|
|
@ -174,10 +174,25 @@ std::string Test::Helper::GetAssetPath(std::string testFile, std::string assetNa
|
|||
return PathSepUnixToPlatform(HostRoot() + "/" + testFile + assetName);
|
||||
}
|
||||
|
||||
bool Test::Helper::CheckTrue(const char *file, int line, const char *expr, bool result) {
|
||||
if(!RecordCheck(result)) {
|
||||
PrintFailure(file, line,
|
||||
ssprintf("(%s) == %s", expr, result ? "true" : "false"));
|
||||
bool Test::Helper::CheckBool(const char *file, int line, const char *expr, bool value,
|
||||
bool reference) {
|
||||
if(!RecordCheck(value == reference)) {
|
||||
std::string msg = ssprintf("(%s) = %s ≠ %s", expr,
|
||||
value ? "true" : "false",
|
||||
reference ? "true" : "false");
|
||||
PrintFailure(file, line, msg);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Test::Helper::CheckEqualString(const char *file, int line, const char *valueExpr,
|
||||
const std::string &value, const std::string &reference) {
|
||||
if(!RecordCheck(value == reference)) {
|
||||
std::string msg = ssprintf("(%s) = \"%s\" ≠ \"%s\"", valueExpr,
|
||||
value.c_str(), reference.c_str());
|
||||
PrintFailure(file, line, msg);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
@ -188,7 +203,8 @@ bool Test::Helper::CheckEqualEpsilon(const char *file, int line, const char *val
|
|||
double value, double reference) {
|
||||
bool result = fabs(value - reference) < LENGTH_EPS;
|
||||
if(!RecordCheck(result)) {
|
||||
std::string msg = ssprintf("(%s) = %.4g ≉ %.4g", valueExpr, value, reference);
|
||||
std::string msg = ssprintf("(%s) = %.4g ≉ %.4g", valueExpr,
|
||||
value, reference);
|
||||
PrintFailure(file, line, msg);
|
||||
return false;
|
||||
} else {
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
// Hack... we should rename the one in ui.h instead.
|
||||
// Hack... we should rename the ones in ui.h instead.
|
||||
#undef CHECK_TRUE
|
||||
#undef CHECK_FALSE
|
||||
|
||||
namespace SolveSpace {
|
||||
namespace Test {
|
||||
|
@ -21,7 +22,10 @@ public:
|
|||
std::string GetAssetPath(std::string testFile, std::string assetName,
|
||||
std::string mangle = "");
|
||||
|
||||
bool CheckTrue(const char *file, int line, const char *expr, bool result);
|
||||
bool CheckBool(const char *file, int line, const char *expr,
|
||||
bool value, bool reference);
|
||||
bool CheckEqualString(const char *file, int line, const char *valueExpr,
|
||||
const std::string &value, const std::string &reference);
|
||||
bool CheckEqualEpsilon(const char *file, int line, const char *valueExpr,
|
||||
double value, double reference);
|
||||
bool CheckLoad(const char *file, int line, const char *fixture);
|
||||
|
@ -52,7 +56,12 @@ using namespace SolveSpace;
|
|||
static void Test_##name(Test::Helper *helper) // { ... }
|
||||
|
||||
#define CHECK_TRUE(cond) \
|
||||
do { if(!helper->CheckTrue(__FILE__, __LINE__, #cond, cond)) return; } while(0)
|
||||
do { if(!helper->CheckBool(__FILE__, __LINE__, #cond, cond, true)) return; } while(0)
|
||||
#define CHECK_FALSE(cond) \
|
||||
do { if(!helper->CheckBool(__FILE__, __LINE__, #cond, cond, false)) return; } while(0)
|
||||
#define CHECK_EQ_STR(value, reference) \
|
||||
do { if(!helper->CheckEqualString(__FILE__, __LINE__, \
|
||||
#value, value, reference)) return; } while(0)
|
||||
#define CHECK_EQ_EPS(value, reference) \
|
||||
do { if(!helper->CheckEqualEpsilon(__FILE__, __LINE__, \
|
||||
#value, value, reference)) return; } while(0)
|
||||
|
|
Loading…
Reference in New Issue