diff --git a/build/agalib_bagn.vcxproj b/build/agalib_bagn.vcxproj index 699a1e492..bb4a54cd1 100644 --- a/build/agalib_bagn.vcxproj +++ b/build/agalib_bagn.vcxproj @@ -150,6 +150,7 @@ + @@ -159,6 +160,25 @@ + + + + + + + + + Document + + + + + + + + + Document + diff --git a/build/agalib_bagn.vcxproj.filters b/build/agalib_bagn.vcxproj.filters index eb20d57ca..a025edf29 100644 --- a/build/agalib_bagn.vcxproj.filters +++ b/build/agalib_bagn.vcxproj.filters @@ -9,6 +9,12 @@ {802516e0-0fad-4adc-9351-cb63ed3be1f3} h + + {5cdfbcc0-288a-474d-a613-0eb94ef6e15d} + + + {f1f7f6cc-f74e-47b2-81d1-a368da609b8b} + @@ -38,6 +44,9 @@ Mask + + Mask + @@ -61,5 +70,18 @@ Headers + + Headers + + + + + Trr + + + + + Dir + \ No newline at end of file diff --git a/build/agalib_rep.vcxproj b/build/agalib_rep.vcxproj index 6e961379c..da726ba20 100644 --- a/build/agalib_rep.vcxproj +++ b/build/agalib_rep.vcxproj @@ -145,6 +145,9 @@ Designer + + Designer + Designer diff --git a/build/agalib_rep.vcxproj.filters b/build/agalib_rep.vcxproj.filters index 3e898df3b..fba1ac313 100644 --- a/build/agalib_rep.vcxproj.filters +++ b/build/agalib_rep.vcxproj.filters @@ -16,6 +16,9 @@ Reports + + Reports + diff --git a/libraries/crossguid/.gitattributes b/libraries/crossguid/.gitattributes new file mode 100644 index 000000000..1ff0c4230 --- /dev/null +++ b/libraries/crossguid/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/libraries/crossguid/.gitignore b/libraries/crossguid/.gitignore new file mode 100644 index 000000000..2c5e80188 --- /dev/null +++ b/libraries/crossguid/.gitignore @@ -0,0 +1,7 @@ +build/ +*.tmp +*.bak +*.swp +android/libs/ +android/obj/ +cmake_build diff --git a/libraries/crossguid/.travis.yml b/libraries/crossguid/.travis.yml new file mode 100644 index 000000000..24813d8e5 --- /dev/null +++ b/libraries/crossguid/.travis.yml @@ -0,0 +1,69 @@ +sudo: required +language: cpp +dist: trusty + +matrix: + include: + + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 + env: + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + + - os: osx + osx_image: xcode9.2 + compiler: clang + +before_install: + # Make sure we set correct env for platform + - eval "$(MATRIX_EVAL)" + + # Workaround for Travis CI macOS bug (https://github.com/travis-ci/travis-ci/issues/6307) + # See https://github.com/searchivarius/nmslib/pull/259 + - | + if [ "${TRAVIS_OS_NAME}" == "osx" ]; then + command curl -sSL https://rvm.io/mpapis.asc | gpg --import -; + rvm get head || true + fi + +script: + - export CHECKOUT_PATH=`pwd`; + - echo "ROOT_PATH= $ROOT_PATH" + - echo "CHECKOUT_PATH= $CHECKOUT_PATH" + + ####################################################################################### + # Install a recent CMake (unless already installed on OS X) + ####################################################################################### + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + CMAKE_URL="http://www.cmake.org/files/v3.10/cmake-3.10.2-Linux-x86_64.tar.gz" + mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake + export PATH=${DEPS_DIR}/cmake/bin:${PATH} + else + brew upgrade cmake || echo "suppress failures in order to ignore warnings" + brew link --overwrite cmake + fi + - cmake --version + + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + sudo apt-get install uuid-dev + fi + ####################################################################################### + # Build the library + ####################################################################################### + - | + cd "${CHECKOUT_PATH}" + mkdir build + cd build + cmake .. -DCMAKE_BUILD_TYPE="Debug" + sudo make install + ####################################################################################### + # Run the tests + ####################################################################################### + - ./crossguid-test diff --git a/libraries/crossguid/.vscode/.cmaketools.json b/libraries/crossguid/.vscode/.cmaketools.json new file mode 100644 index 000000000..e8a5846b2 --- /dev/null +++ b/libraries/crossguid/.vscode/.cmaketools.json @@ -0,0 +1,5 @@ +{ + "variant": null, + "activeEnvironments": [], + "codeModel": null +} \ No newline at end of file diff --git a/libraries/crossguid/CMakeLists.txt b/libraries/crossguid/CMakeLists.txt new file mode 100644 index 000000000..76b5a62cf --- /dev/null +++ b/libraries/crossguid/CMakeLists.txt @@ -0,0 +1,97 @@ +cmake_minimum_required(VERSION 3.5.1) +project(CrossGuid VERSION 0.2.3) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") + +option(CROSSGUID_TESTS "Build test runner" ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_DEBUG_POSTFIX "-dgb") + +# Set the build type if not set +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +endif() + +add_library(crossguid ${CMAKE_CURRENT_SOURCE_DIR}/src/guid.cpp) +set_property(TARGET crossguid PROPERTY POSITION_INDEPENDENT_CODE ON) +target_include_directories(crossguid PUBLIC + $ + $) + +if(WIN32) + target_compile_definitions(crossguid PRIVATE GUID_WINDOWS) +elseif(APPLE) + find_library(CFLIB CoreFoundation) + target_link_libraries(crossguid ${CFLIB}) + target_compile_definitions(crossguid PRIVATE GUID_CFUUID) +elseif(ANDROID) + # GUID_ANDROID is used in the headers, so make PUBLIC + target_compile_definitions(crossguid PUBLIC GUID_ANDROID) +else() + find_package(Libuuid REQUIRED) + if (NOT LIBUUID_FOUND) + message(FATAL_ERROR + "You might need to run 'sudo apt-get install uuid-dev' or similar") + endif() + target_include_directories(crossguid PRIVATE ${LIBUUID_INCLUDE_DIR}) + target_link_libraries(crossguid ${LIBUUID_LIBRARY}) + target_compile_definitions(crossguid PRIVATE GUID_LIBUUID) +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(WARNINGS "-Werror" "-Wall") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(WARNINGS "-Werror" "-Wall") +elseif(MSVC) + set(WARNINGS "/WX" "/W4") +endif() +target_compile_options(crossguid PRIVATE ${WARNINGS}) + +set_target_properties(crossguid + PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + +if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + include(GNUInstallDirs) + + set(CROSSGUID_INC_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}") + set(CROSSGUID_RUNTIME_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") + set(CROSSGUID_LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") + set(CROSSGUID_ARCHIVE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") + set(CROSSGUID_FRAMEWORK_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") + + set(CROSSGUID_CMAKE_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/crossguid/cmake") + set(CROSSGUID_ADDITIONAL_FILES_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/crossguid") + + # Install target + install(TARGETS crossguid EXPORT crossguidTargets + RUNTIME DESTINATION ${CROSSGUID_RUNTIME_INSTALL_DIR} + LIBRARY DESTINATION ${CROSSGUID_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${CROSSGUID_ARCHIVE_INSTALL_DIR} + FRAMEWORK DESTINATION ${CROSSGUID_FRAMEWORK_INSTALL_DIR}) + + # Install headers + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" + DESTINATION ${CROSSGUID_INC_INSTALL_DIR}) + + # Make cmake config files for all targets + install(EXPORT crossguidTargets + DESTINATION ${CROSSGUID_CMAKE_CONFIG_INSTALL_DIR} + FILE crossguid-config.cmake) + + # Install readme and license + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" "${CMAKE_CURRENT_SOURCE_DIR}/README.md" + DESTINATION ${CROSSGUID_ADDITIONAL_FILES_INSTALL_DIR}) + + configure_file(crossguid.pc.in ${PROJECT_BINARY_DIR}/crossguid.pc @ONLY) + install(FILES ${PROJECT_BINARY_DIR}/crossguid.pc DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig) +endif() + +if (CROSSGUID_TESTS) + add_executable(crossguid-test test/TestMain.cpp test/Test.cpp) + target_link_libraries(crossguid-test crossguid) +endif() diff --git a/libraries/crossguid/LICENSE b/libraries/crossguid/LICENSE new file mode 100644 index 000000000..f1f0afe57 --- /dev/null +++ b/libraries/crossguid/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Graeme Hill (http://graemehill.ca) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/libraries/crossguid/README.md b/libraries/crossguid/README.md new file mode 100644 index 000000000..44c6164c3 --- /dev/null +++ b/libraries/crossguid/README.md @@ -0,0 +1,225 @@ +# CrossGuid [![Build Status](https://travis-ci.org/graeme-hill/crossguid.svg?branch=master)](https://travis-ci.org/graeme-hill/crossguid) + +CrossGuid is a minimal, cross platform, C++ GUID library. It uses the best +native GUID/UUID generator on the given platform and has a generic class for +parsing, stringifying, and comparing IDs. The guid generation technique is +determined by your platform: + +## Linux + +On linux you can use `libuuid` which is pretty standard. On distros like Ubuntu +it is available by default but to use it you need the header files so you have +to do: + + sudo apt-get install uuid-dev + +## Mac/iOS + +On Mac or iOS you can use `CFUUIDCreate` from `CoreFoundation`. Since it's a +plain C function you don't even need to compile as Objective-C++. + +## Windows + +On Windows we just use the the built-in function `CoCreateGuid`. CMake can +generate a Visual Studio project if that's your thing. + +## Android + +The Android version uses a handle to a `JNIEnv` object to invoke the +`randomUUID()` function on `java.util.UUID` from C++. The Android specific code +is all in the `android/` subdirectory. If you have an emulator already running, +then you can run the `android.sh` script in the root directory. It has the +following requirements: + +- Android emulator is already running (or you have physical device connected). +- You're using bash. +- adb is in your path. +- You have an Android sdk setup including `ANDROID_HOME` environment variable. + +## Versions + +This is version 0.2 of CrossGuid. If you all already using CrossGuid and your code +uses `GuidGenerator` then you are using version 0.1. Differences in version 0.2: + +- Put everything inside the namespace `xg` instead of using the global + namespace. +- Removed `GuidGenerator` class and replaced with the free function + `xg::newGuid`. This is the way I originally wanted it to work but since Android + is a special snowflake requiring state (`JNIEnv *`) I introduced the + `GuidGenerator` class specifically so that there would be somewhere to store + the `JNIEnv *` when running on Android. However, this basically meant + complicating the library for the sake of one platform. In version 0.2 the goal is + to design for the normal platforms and let Android be weird. In Android you just + need to run `xg::initJni(JNIEnv *)` before you create any guids. The `JNIEnv *` + is just stored as a global variable. +- Added CMake build system. Instead of different scripts for each platform you + can just run cmake and it should handle each platform (except Android which + again is special). +- Actual guid bytes are stored in `std::array` instead of + `std::vector`. +- More error checking (like if you try to create a guid with invalid number of + bytes). + +If you're happily using version 0.1 then there's not really any reason to +change. + +## Compiling + +Just do the normal cmake thing: + +``` +mkdir build +cd build +cmake .. +make install +``` + +## Running tests + +After compiling as described above you should get two files: `libcrossguid.a` (the +static library) and `crossguid-test` (the test runner). So to run the tests just do: + +``` +./crossguid-test +``` + +## Basic usage + +### Creating guids + +Create a new random guid: + +```cpp +#include +... +auto g = xg::newGuid(); +``` + +**NOTE:** On Android you need to call `xg::initJni(JNIEnv *)` first so that it +is possible for `xg::newGuid()` to call back into java libraries. `initJni` +only needs to be called once when the process starts. + +Create a new zero guid: + +```cpp +xg::Guid g; +``` + +Create from a string: + +```cpp +xg::Guid g("c405c66c-ccbb-4ffd-9b62-c286c0fd7a3b"); +``` + +### Checking validity + +If you have some string value and you need to check whether it is a valid guid +then you can simply attempt to construct the guid: + +```cpp +xg::Guid g("bad-guid-string"); +if (!g.isValid()) +{ + // do stuff +} +``` + +If the guid string is not valid then all bytes are set to zero and `isValid()` +returns `false`. + +### Converting guid to string + +First of all, there is normally no reason to convert a guid to a string except +for in debugging or when serializing for API calls or whatever. You should +definitely avoid storing guids as strings or using strings for any +computations. If you do need to convert a guid to a string, then you can +utilize strings because the `<<` operator is overloaded. To print a guid to +`std::cout`: + +```cpp +void doGuidStuff() +{ + auto myGuid = xg::newGuid(); + std::cout << "Here is a guid: " << myGuid << std::endl; +} +``` + +Or to store a guid in a `std::string`: + +```cpp +void doGuidStuff() +{ + auto myGuid = xg::newGuid(); + std::stringstream stream; + stream << myGuid; + auto guidString = stream.str(); +} +``` + +There is also a `str()` function that returns a `std::string`: + +```cpp +std::string guidStr = xg::newGuid().str(); +``` + +### Creating a guid from raw bytes + +It's unlikely that you will need this, but this is done within the library +internally to construct a `Guid` object from the raw data given by the system's +built-in guid generation function. There are two key constructors for this: + +```cpp +Guid(std::array &bytes); +``` + +and + +```cpp +Guid(const unsigned char * bytes); +``` + +When possible prefer the `std::array` constructor because it is safer. If you +pass in an incorrectly sized C array then bad things will happen. + +### Comparing guids + +`==` and `!=` are implemented, so the following works as expected: + +```cpp +void doGuidStuff() +{ + auto guid1 = xg::newGuid(); + auto guid2 = xg::newGuid(); + + auto guidsAreEqual = guid1 == guid2; + auto guidsAreNotEqual = guid1 != guid2; +} +``` + +### Hashing guids + +Guids can be used directly in containers requireing `std::hash` such as `std::map,`std::unordered_map` etc. + +## License + +The MIT License (MIT) + +Copyright (c) 2014 Graeme Hill (http://graemehill.ca) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/libraries/crossguid/android.sh b/libraries/crossguid/android.sh new file mode 100644 index 000000000..bb26e9abc --- /dev/null +++ b/libraries/crossguid/android.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +export LC_NUMERIC="en_US.UTF-8" + +pushd android +./gradlew clean assembleDebug +adb uninstall ca.graemehill.crossguid.testapp || { exit 1; } +adb install app/build/outputs/apk/debug/app-debug.apk || { exit 1; } +adb shell am start -n ca.graemehill.crossguid.testapp/ca.graemehill.crossguid.testapp.MainActivity +popd diff --git a/libraries/crossguid/android/.gitignore b/libraries/crossguid/android/.gitignore new file mode 100644 index 000000000..0f6ca2d36 --- /dev/null +++ b/libraries/crossguid/android/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild +.idea diff --git a/libraries/crossguid/android/ant.properties b/libraries/crossguid/android/ant.properties new file mode 100644 index 000000000..b0971e891 --- /dev/null +++ b/libraries/crossguid/android/ant.properties @@ -0,0 +1,17 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked into Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + diff --git a/libraries/crossguid/android/app/.gitignore b/libraries/crossguid/android/app/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/libraries/crossguid/android/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/libraries/crossguid/android/app/CMakeLists.txt b/libraries/crossguid/android/app/CMakeLists.txt new file mode 100644 index 000000000..11da3478d --- /dev/null +++ b/libraries/crossguid/android/app/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.4.1) + +set(LIB_NAME crossguidtest) +set(XG_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +set(XG_TEST_DIR ${XG_DIR}/test) + +add_library(${LIB_NAME} SHARED src/main/cpp/jnitest.cpp ${XG_TEST_DIR}/Test.cpp) + +target_include_directories(${LIB_NAME} PRIVATE + ${XG_DIR} + ${XG_TEST_DIR}) + +target_compile_definitions(${LIB_NAME} PRIVATE GUID_ANDROID) + +set(XG_TESTS OFF CACHE BOOL "disable tests") +add_subdirectory(${XG_DIR} ${XG_DIR}/cmake_build) + +target_link_libraries(${LIB_NAME} xg) \ No newline at end of file diff --git a/libraries/crossguid/android/app/build.gradle b/libraries/crossguid/android/app/build.gradle new file mode 100644 index 000000000..e715d3ab6 --- /dev/null +++ b/libraries/crossguid/android/app/build.gradle @@ -0,0 +1,39 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 25 + buildToolsVersion "26.0.0" + defaultConfig { + applicationId "ca.graemehill.crossguid.testapp" + minSdkVersion 14 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + externalNativeBuild { + cmake { + cppFlags "-std=c++11 -frtti -fexceptions" + arguments "-DANDROID_TOOLCHAIN=clang" + } + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.0', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + implementation 'com.android.support:appcompat-v7:25.4.0' + testImplementation 'junit:junit:4.12' +} diff --git a/libraries/crossguid/android/app/proguard-rules.pro b/libraries/crossguid/android/app/proguard-rules.pro new file mode 100644 index 000000000..98587bebd --- /dev/null +++ b/libraries/crossguid/android/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/Cellar/android-sdk/24.4.1_1/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/libraries/crossguid/android/app/src/main/AndroidManifest.xml b/libraries/crossguid/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..c1a1e865f --- /dev/null +++ b/libraries/crossguid/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/libraries/crossguid/android/app/src/main/cpp/jnitest.cpp b/libraries/crossguid/android/app/src/main/cpp/jnitest.cpp new file mode 100644 index 000000000..abb115438 --- /dev/null +++ b/libraries/crossguid/android/app/src/main/cpp/jnitest.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +#include "Test.hpp" + +JavaVM *&javaVM() { + static JavaVM *jvm; + return jvm; +} + +extern "C" +{ + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void * /* reserved */) { + javaVM() = jvm; + return JNI_VERSION_1_6; +} + +JNIEXPORT jstring JNICALL +Java_ca_graemehill_crossguid_testapp_MainActivity_test( + JNIEnv *env, jobject /*thiz*/) +{ + std::stringstream resultStream; + xg::initJni(env); + test(resultStream); + return env->NewStringUTF(resultStream.str().c_str()); +} + +JNIEXPORT jstring JNICALL +Java_ca_graemehill_crossguid_testapp_MainActivity_newGuid( + JNIEnv *env, jobject /*thiz*/) { + return env->NewStringUTF(xg::newGuid(env).str().c_str()); +} + +JNIEXPORT jstring JNICALL +Java_ca_graemehill_crossguid_testapp_MainActivity_createGuidFromNativeThread( + JNIEnv *env, jobject /*thiz*/) { + + // there is no promise<> in armeabi of ndk + // so ugly atomic_bool wait solution + std::atomic_bool ready { false }; + std::string guid; + + std::thread([&ready, &guid](){ + JNIEnv *threadEnv; + javaVM()->AttachCurrentThread(&threadEnv, NULL); + guid = xg::newGuid(threadEnv); + javaVM()->DetachCurrentThread(); + + ready = true; + }).detach(); + + while (!ready); + return env->NewStringUTF(guid.c_str()); +} + +} diff --git a/libraries/crossguid/android/app/src/main/java/ca/graemehill/crossguid/testapp/MainActivity.java b/libraries/crossguid/android/app/src/main/java/ca/graemehill/crossguid/testapp/MainActivity.java new file mode 100644 index 000000000..4a285a28e --- /dev/null +++ b/libraries/crossguid/android/app/src/main/java/ca/graemehill/crossguid/testapp/MainActivity.java @@ -0,0 +1,64 @@ +package ca.graemehill.crossguid.testapp; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; + +import java.util.concurrent.CountDownLatch; + +public class MainActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + final TextView textView = (TextView)findViewById(R.id.mainTextView); + textView.setText(test()); + + final TextView javaThreadView = (TextView)findViewById(R.id.javaThreadView); + javaThreadView.setText(createGuidFromJavaThread()); + + final TextView nativeThreadView = (TextView)findViewById(R.id.nativeThreadView); + nativeThreadView.setText(createGuidFromNativeThread()); + } + + public native String test(); + + private static class StringCapture { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public String createGuidFromJavaThread() { + final CountDownLatch created = new CountDownLatch(1); + final StringCapture result = new StringCapture(); + new Thread(new Runnable() { + @Override + public void run() { + result.setValue(newGuid()); + created.countDown(); + } + }).start(); + try { + created.await(); + } catch (InterruptedException e) { + return "Could not get value: " + e.getMessage(); + } + return result.getValue(); + } + + public native String newGuid(); + + public native String createGuidFromNativeThread(); + static { + System.loadLibrary("crossguidtest"); + } +} diff --git a/libraries/crossguid/android/app/src/main/res/layout/main.xml b/libraries/crossguid/android/app/src/main/res/layout/main.xml new file mode 100644 index 000000000..8ba850adf --- /dev/null +++ b/libraries/crossguid/android/app/src/main/res/layout/main.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/libraries/crossguid/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..550730310 Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/libraries/crossguid/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..8fab6a3a5 Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/libraries/crossguid/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..6bc7fcd6f Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/libraries/crossguid/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..1eecc0e7d Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/libraries/crossguid/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..ec87dcebe Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/libraries/crossguid/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..05ca079ca Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/libraries/crossguid/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..6f67f21ba Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/libraries/crossguid/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..8bac0f274 Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/libraries/crossguid/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..0327e13fa Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/libraries/crossguid/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/libraries/crossguid/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..bacd3e758 Binary files /dev/null and b/libraries/crossguid/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/libraries/crossguid/android/app/src/main/res/values/colors.xml b/libraries/crossguid/android/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..3ab3e9cbc --- /dev/null +++ b/libraries/crossguid/android/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/libraries/crossguid/android/app/src/main/res/values/strings.xml b/libraries/crossguid/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..4b0fb85ea --- /dev/null +++ b/libraries/crossguid/android/app/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + TestApp + GUID created from Native Thread + GUID created from Java Thread + Test results + diff --git a/libraries/crossguid/android/app/src/main/res/values/styles.xml b/libraries/crossguid/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..5885930df --- /dev/null +++ b/libraries/crossguid/android/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/libraries/crossguid/android/build.gradle b/libraries/crossguid/android/build.gradle new file mode 100644 index 000000000..82ff27452 --- /dev/null +++ b/libraries/crossguid/android/build.gradle @@ -0,0 +1,26 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.0.0-alpha3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + maven { url 'https://maven.google.com' } + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/libraries/crossguid/android/build.xml b/libraries/crossguid/android/build.xml new file mode 100644 index 000000000..576f8b987 --- /dev/null +++ b/libraries/crossguid/android/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/crossguid/android/gradle.properties b/libraries/crossguid/android/gradle.properties new file mode 100644 index 000000000..69b4e93ce --- /dev/null +++ b/libraries/crossguid/android/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +android.enableAapt2=false diff --git a/libraries/crossguid/android/gradle/wrapper/gradle-wrapper.jar b/libraries/crossguid/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..13372aef5 Binary files /dev/null and b/libraries/crossguid/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/libraries/crossguid/android/gradle/wrapper/gradle-wrapper.properties b/libraries/crossguid/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4714bbc5b --- /dev/null +++ b/libraries/crossguid/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Aug 03 09:39:40 MSK 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-milestone-1-all.zip diff --git a/libraries/crossguid/android/gradlew b/libraries/crossguid/android/gradlew new file mode 100644 index 000000000..4a9961344 --- /dev/null +++ b/libraries/crossguid/android/gradlew @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" +LC_NUMERIC="en_US.UTF-8" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/libraries/crossguid/android/gradlew.bat b/libraries/crossguid/android/gradlew.bat new file mode 100644 index 000000000..8a0b282aa --- /dev/null +++ b/libraries/crossguid/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/libraries/crossguid/android/proguard-project.txt b/libraries/crossguid/android/proguard-project.txt new file mode 100644 index 000000000..f2fe1559a --- /dev/null +++ b/libraries/crossguid/android/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/libraries/crossguid/android/project.properties b/libraries/crossguid/android/project.properties new file mode 100644 index 000000000..4ab125693 --- /dev/null +++ b/libraries/crossguid/android/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 diff --git a/libraries/crossguid/android/settings.gradle b/libraries/crossguid/android/settings.gradle new file mode 100644 index 000000000..e7b4def49 --- /dev/null +++ b/libraries/crossguid/android/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/libraries/crossguid/cmake/FindLibuuid.cmake b/libraries/crossguid/cmake/FindLibuuid.cmake new file mode 100644 index 000000000..58fa33041 --- /dev/null +++ b/libraries/crossguid/cmake/FindLibuuid.cmake @@ -0,0 +1,43 @@ +find_package(PkgConfig) + +pkg_check_modules(PKG_LIBUUID QUIET uuid) + +set(LIBUUID_DEFINITIONS ${PKG_LIBUUID_CFLAGS_OTHER}) +set(LIBUUID_VERSION ${PKG_LIBUUID_VERSION}) + +find_path(LIBUUID_INCLUDE_DIR + NAMES uuid/uuid.h + HINTS ${PKG_LIBUUID_INCLUDE_DIRS} +) +find_library(LIBUUID_LIBRARY + NAMES uuid + HINTS ${PKG_LIBUUID_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibUUID + FOUND_VAR + LIBUUID_FOUND + REQUIRED_VARS + LIBUUID_LIBRARY + LIBUUID_INCLUDE_DIR + VERSION_VAR + LIBUUID_VERSION +) + +if(LIBUUID_FOUND AND NOT TARGET LibUUID::UUID) + add_library(LibUUID::UUID UNKNOWN IMPORTED) + set_target_properties(LibUUID::UUID PROPERTIES + IMPORTED_LOCATION "${LIBUUID_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${LIBUUID_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBUUID_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(LIBUUID_INCLUDE_DIR LIBUUID_LIBRARY) + +include(FeatureSummary) +set_package_properties(LIBUUID PROPERTIES + URL "http://www.kernel.org/pub/linux/utils/util-linux/" + DESCRIPTION "uuid library in util-linux" +) diff --git a/libraries/crossguid/crossguid.pc.in b/libraries/crossguid/crossguid.pc.in new file mode 100644 index 000000000..efcfb114a --- /dev/null +++ b/libraries/crossguid/crossguid.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ + +Name: crossguid +Description: Lightweight cross platform C++ GUID/UUID library +URL: https://github.com/graeme-hill/crossguid +Version: @PROJECT_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lcrossguid diff --git a/libraries/crossguid/include/crossguid/guid.hpp b/libraries/crossguid/include/crossguid/guid.hpp new file mode 100644 index 000000000..61e0f177d --- /dev/null +++ b/libraries/crossguid/include/crossguid/guid.hpp @@ -0,0 +1,149 @@ +/* +The MIT License (MIT) + +Copyright (c) 2014 Graeme Hill (http://graemehill.ca) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +#ifdef GUID_ANDROID +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define BEGIN_XG_NAMESPACE namespace xg { +#define END_XG_NAMESPACE } + +BEGIN_XG_NAMESPACE + +// Class to represent a GUID/UUID. Each instance acts as a wrapper around a +// 16 byte value that can be passed around by value. It also supports +// conversion to string (via the stream operator <<) and conversion from a +// string via constructor. +class Guid +{ +public: + explicit Guid(const std::array &bytes); + explicit Guid(std::array &&bytes); + + explicit Guid(std::string_view fromString); + Guid(); + + Guid(const Guid &other) = default; + Guid &operator=(const Guid &other) = default; + Guid(Guid &&other) = default; + Guid &operator=(Guid &&other) = default; + + bool operator==(const Guid &other) const; + bool operator!=(const Guid &other) const; + + std::string str() const; + operator std::string() const; + const std::array& bytes() const; + void swap(Guid &other); + bool isValid() const; + +private: + void zeroify(); + + // actual data + std::array _bytes; + + // make the << operator a friend so it can access _bytes + friend std::ostream &operator<<(std::ostream &s, const Guid &guid); + friend bool operator<(const Guid &lhs, const Guid &rhs); +}; + +Guid newGuid(); + +#ifdef GUID_ANDROID +struct AndroidGuidInfo +{ + static AndroidGuidInfo fromJniEnv(JNIEnv *env); + + JNIEnv *env; + jclass uuidClass; + jmethodID newGuidMethod; + jmethodID mostSignificantBitsMethod; + jmethodID leastSignificantBitsMethod; + std::thread::id initThreadId; +}; + +extern AndroidGuidInfo androidInfo; + +void initJni(JNIEnv *env); + +// overloading for multi-threaded calls +Guid newGuid(JNIEnv *env); +#endif + +namespace details +{ + template struct hash; + + template + struct hash : public std::hash + { + using std::hash::hash; + }; + + + template + struct hash + { + inline std::size_t operator()(const T& v, const Rest&... rest) { + std::size_t seed = hash{}(rest...); + seed ^= hash{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + }; +} + +END_XG_NAMESPACE + +namespace std +{ + // Template specialization for std::swap() -- + // See guid.cpp for the function definition + template <> + void swap(xg::Guid &guid0, xg::Guid &guid1) noexcept; + + // Specialization for std::hash -- this implementation + // uses std::hash on the stringification of the guid + // to calculate the hash + template <> + struct hash + { + std::size_t operator()(xg::Guid const &guid) const + { + const uint64_t* p = reinterpret_cast(guid.bytes().data()); + return xg::details::hash{}(p[0], p[1]); + } + }; +} diff --git a/libraries/crossguid/src/guid.cpp b/libraries/crossguid/src/guid.cpp new file mode 100644 index 000000000..3061a9ba9 --- /dev/null +++ b/libraries/crossguid/src/guid.cpp @@ -0,0 +1,403 @@ +/* +The MIT License (MIT) + +Copyright (c) 2014 Graeme Hill (http://graemehill.ca) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include "crossguid/guid.hpp" + +#ifdef GUID_LIBUUID +#include +#endif + +#ifdef GUID_CFUUID +#include +#endif + +#ifdef GUID_WINDOWS +#include +#endif + +#ifdef GUID_ANDROID +#include +#include +#endif + +BEGIN_XG_NAMESPACE + +#ifdef GUID_ANDROID +AndroidGuidInfo androidInfo; + +AndroidGuidInfo AndroidGuidInfo::fromJniEnv(JNIEnv *env) +{ + AndroidGuidInfo info; + info.env = env; + auto localUuidClass = env->FindClass("java/util/UUID"); + info.uuidClass = (jclass)env->NewGlobalRef(localUuidClass); + env->DeleteLocalRef(localUuidClass); + info.newGuidMethod = env->GetStaticMethodID( + info.uuidClass, "randomUUID", "()Ljava/util/UUID;"); + info.mostSignificantBitsMethod = env->GetMethodID( + info.uuidClass, "getMostSignificantBits", "()J"); + info.leastSignificantBitsMethod = env->GetMethodID( + info.uuidClass, "getLeastSignificantBits", "()J"); + info.initThreadId = std::this_thread::get_id(); + return info; +} + +void initJni(JNIEnv *env) +{ + androidInfo = AndroidGuidInfo::fromJniEnv(env); +} +#endif + +// overload << so that it's easy to convert to a string +std::ostream &operator<<(std::ostream &s, const Guid &guid) +{ + std::ios_base::fmtflags f(s.flags()); // politely don't leave the ostream in hex mode + s << std::hex << std::setfill('0') + << std::setw(2) << (int)guid._bytes[0] + << std::setw(2) << (int)guid._bytes[1] + << std::setw(2) << (int)guid._bytes[2] + << std::setw(2) << (int)guid._bytes[3] + << "-" + << std::setw(2) << (int)guid._bytes[4] + << std::setw(2) << (int)guid._bytes[5] + << "-" + << std::setw(2) << (int)guid._bytes[6] + << std::setw(2) << (int)guid._bytes[7] + << "-" + << std::setw(2) << (int)guid._bytes[8] + << std::setw(2) << (int)guid._bytes[9] + << "-" + << std::setw(2) << (int)guid._bytes[10] + << std::setw(2) << (int)guid._bytes[11] + << std::setw(2) << (int)guid._bytes[12] + << std::setw(2) << (int)guid._bytes[13] + << std::setw(2) << (int)guid._bytes[14] + << std::setw(2) << (int)guid._bytes[15]; + s.flags(f); + return s; +} + +bool operator<(const xg::Guid &lhs, const xg::Guid &rhs) +{ + return lhs.bytes() < rhs.bytes(); +} + +bool Guid::isValid() const +{ + xg::Guid empty; + return *this != empty; +} + +// convert to string using std::snprintf() and std::string +std::string Guid::str() const +{ + char one[10], two[6], three[6], four[6], five[14]; + + snprintf(one, 10, "%02x%02x%02x%02x", + _bytes[0], _bytes[1], _bytes[2], _bytes[3]); + snprintf(two, 6, "%02x%02x", + _bytes[4], _bytes[5]); + snprintf(three, 6, "%02x%02x", + _bytes[6], _bytes[7]); + snprintf(four, 6, "%02x%02x", + _bytes[8], _bytes[9]); + snprintf(five, 14, "%02x%02x%02x%02x%02x%02x", + _bytes[10], _bytes[11], _bytes[12], _bytes[13], _bytes[14], _bytes[15]); + const std::string sep("-"); + std::string out(one); + + out += sep + two; + out += sep + three; + out += sep + four; + out += sep + five; + + return out; +} + +// conversion operator for std::string +Guid::operator std::string() const +{ + return str(); +} + +// Access underlying bytes +const std::array& Guid::bytes() const +{ + return _bytes; +} + +// create a guid from vector of bytes +Guid::Guid(const std::array &bytes) : _bytes(bytes) +{ } + +// create a guid from vector of bytes +Guid::Guid(std::array &&bytes) : _bytes(std::move(bytes)) +{ } + +// converts a single hex char to a number (0 - 15) +unsigned char hexDigitToChar(char ch) +{ + // 0-9 + if (ch > 47 && ch < 58) + return ch - 48; + + // a-f + if (ch > 96 && ch < 103) + return ch - 87; + + // A-F + if (ch > 64 && ch < 71) + return ch - 55; + + return 0; +} + +bool isValidHexChar(char ch) +{ + // 0-9 + if (ch > 47 && ch < 58) + return true; + + // a-f + if (ch > 96 && ch < 103) + return true; + + // A-F + if (ch > 64 && ch < 71) + return true; + + return false; +} + +// converts the two hexadecimal characters to an unsigned char (a byte) +unsigned char hexPairToChar(char a, char b) +{ + return hexDigitToChar(a) * 16 + hexDigitToChar(b); +} + +// create a guid from string +Guid::Guid(std::string_view fromString) +{ + char charOne = '\0'; + char charTwo = '\0'; + bool lookingForFirstChar = true; + unsigned nextByte = 0; + + for (const char &ch : fromString) + { + if (ch == '-') + continue; + + if (nextByte >= 16 || !isValidHexChar(ch)) + { + // Invalid string so bail + zeroify(); + return; + } + + if (lookingForFirstChar) + { + charOne = ch; + lookingForFirstChar = false; + } + else + { + charTwo = ch; + auto byte = hexPairToChar(charOne, charTwo); + _bytes[nextByte++] = byte; + lookingForFirstChar = true; + } + } + + // if there were fewer than 16 bytes in the string then guid is bad + if (nextByte < 16) + { + zeroify(); + return; + } +} + +// create empty guid +Guid::Guid() : _bytes{ {0} } +{ } + +// set all bytes to zero +void Guid::zeroify() +{ + std::fill(_bytes.begin(), _bytes.end(), static_cast(0)); +} + +// overload equality operator +bool Guid::operator==(const Guid &other) const +{ + return _bytes == other._bytes; +} + +// overload inequality operator +bool Guid::operator!=(const Guid &other) const +{ + return !((*this) == other); +} + +// member swap function +void Guid::swap(Guid &other) +{ + _bytes.swap(other._bytes); +} + +// This is the linux friendly implementation, but it could work on other +// systems that have libuuid available +#ifdef GUID_LIBUUID +Guid newGuid() +{ + std::array data; + static_assert(std::is_same::value, "Wrong type!"); + uuid_generate(data.data()); + return Guid{std::move(data)}; +} +#endif + +// this is the mac and ios version +#ifdef GUID_CFUUID +Guid newGuid() +{ + auto newId = CFUUIDCreate(NULL); + auto bytes = CFUUIDGetUUIDBytes(newId); + CFRelease(newId); + + std::array byteArray = + {{ + bytes.byte0, + bytes.byte1, + bytes.byte2, + bytes.byte3, + bytes.byte4, + bytes.byte5, + bytes.byte6, + bytes.byte7, + bytes.byte8, + bytes.byte9, + bytes.byte10, + bytes.byte11, + bytes.byte12, + bytes.byte13, + bytes.byte14, + bytes.byte15 + }}; + return Guid{std::move(byteArray)}; +} +#endif + +// obviously this is the windows version +#ifdef GUID_WINDOWS +Guid newGuid() +{ + GUID newId; + CoCreateGuid(&newId); + + std::array bytes = + { + (unsigned char)((newId.Data1 >> 24) & 0xFF), + (unsigned char)((newId.Data1 >> 16) & 0xFF), + (unsigned char)((newId.Data1 >> 8) & 0xFF), + (unsigned char)((newId.Data1) & 0xff), + + (unsigned char)((newId.Data2 >> 8) & 0xFF), + (unsigned char)((newId.Data2) & 0xff), + + (unsigned char)((newId.Data3 >> 8) & 0xFF), + (unsigned char)((newId.Data3) & 0xFF), + + (unsigned char)newId.Data4[0], + (unsigned char)newId.Data4[1], + (unsigned char)newId.Data4[2], + (unsigned char)newId.Data4[3], + (unsigned char)newId.Data4[4], + (unsigned char)newId.Data4[5], + (unsigned char)newId.Data4[6], + (unsigned char)newId.Data4[7] + }; + + return Guid{std::move(bytes)}; +} +#endif + +// android version that uses a call to a java api +#ifdef GUID_ANDROID +Guid newGuid(JNIEnv *env) +{ + assert(env != androidInfo.env || std::this_thread::get_id() == androidInfo.initThreadId); + + jobject javaUuid = env->CallStaticObjectMethod( + androidInfo.uuidClass, androidInfo.newGuidMethod); + jlong mostSignificant = env->CallLongMethod(javaUuid, + androidInfo.mostSignificantBitsMethod); + jlong leastSignificant = env->CallLongMethod(javaUuid, + androidInfo.leastSignificantBitsMethod); + + std::array bytes = + { + (unsigned char)((mostSignificant >> 56) & 0xFF), + (unsigned char)((mostSignificant >> 48) & 0xFF), + (unsigned char)((mostSignificant >> 40) & 0xFF), + (unsigned char)((mostSignificant >> 32) & 0xFF), + (unsigned char)((mostSignificant >> 24) & 0xFF), + (unsigned char)((mostSignificant >> 16) & 0xFF), + (unsigned char)((mostSignificant >> 8) & 0xFF), + (unsigned char)((mostSignificant) & 0xFF), + (unsigned char)((leastSignificant >> 56) & 0xFF), + (unsigned char)((leastSignificant >> 48) & 0xFF), + (unsigned char)((leastSignificant >> 40) & 0xFF), + (unsigned char)((leastSignificant >> 32) & 0xFF), + (unsigned char)((leastSignificant >> 24) & 0xFF), + (unsigned char)((leastSignificant >> 16) & 0xFF), + (unsigned char)((leastSignificant >> 8) & 0xFF), + (unsigned char)((leastSignificant) & 0xFF) + }; + + env->DeleteLocalRef(javaUuid); + + return Guid{std::move(bytes)}; +} + +Guid newGuid() +{ + return newGuid(androidInfo.env); +} +#endif + + +END_XG_NAMESPACE + +// Specialization for std::swap() -- +// call member swap function of lhs, passing rhs +namespace std +{ + template <> + void swap(xg::Guid &lhs, xg::Guid &rhs) noexcept + { + lhs.swap(rhs); + } +} diff --git a/libraries/crossguid/test/Test.cpp b/libraries/crossguid/test/Test.cpp new file mode 100644 index 000000000..6fd47bc60 --- /dev/null +++ b/libraries/crossguid/test/Test.cpp @@ -0,0 +1,184 @@ +#include "Test.hpp" + +int test(std::ostream &outStream) +{ + int failed = 0; + + /************************************************************************* + * HAPPY PATH TESTS + *************************************************************************/ + + auto r1 = xg::newGuid(); + auto r2 = xg::newGuid(); + auto r3 = xg::newGuid(); + + outStream << r1 << std::endl << r2 << std::endl << r3 << std::endl; + + xg::Guid s1("7bcd757f-5b10-4f9b-af69-1a1f226f3b3e"); + xg::Guid s2("16d1bd03-09a5-47d3-944b-5e326fd52d27"); + xg::Guid s3("fdaba646-e07e-49de-9529-4499a5580c75"); + xg::Guid s4("7bcd757f-5b10-4f9b-af69-1a1f226f3b3e"); + xg::Guid s5("7bcd757f-5b10-4f9b-af69-1a1f226f3b31"); + + if (r1 == r2 || r1 == r3 || r2 == r3) + { + outStream << "FAIL - not all random guids are different" << std::endl; + failed++; + } + + if (s1 == s2) + { + outStream << "FAIL - s1 and s2 should be different" << std::endl; + failed++; + } + + if (s1 != s4) + { + outStream << "FAIL - s1 and s4 should be equal" << std::endl; + failed++; + } + + if (s4 < s5) { + outStream << "FAIL - s5 should should less than s4" << std::endl; + failed++; + } + + std::stringstream ss1; + ss1 << s1; + if (ss1.str() != "7bcd757f-5b10-4f9b-af69-1a1f226f3b3e") + { + outStream << "FAIL - string from s1 stream is wrong" << std::endl; + outStream << "--> " << ss1.str() << std::endl; + failed++; + } + + if (s1.str() != "7bcd757f-5b10-4f9b-af69-1a1f226f3b3e") + { + outStream << "FAIL - string from s1.str() is wrong" << std::endl; + outStream << "--> " << s1.str() << std::endl; + failed++; + } + + std::stringstream ss2; + ss2 << s2; + if (ss2.str() != "16d1bd03-09a5-47d3-944b-5e326fd52d27") + { + outStream << "FAIL - string generated from s2 is wrong" << std::endl; + outStream << "--> " << ss2.str() << std::endl; + return 1; + } + + std::stringstream ss3; + ss3 << s3; + if (ss3.str() != "fdaba646-e07e-49de-9529-4499a5580c75") + { + outStream << "FAIL - string generated from s3 is wrong" << std::endl; + outStream << "--> " << ss3.str() << std::endl; + failed++; + } + + auto swap1 = xg::newGuid(); + auto swap2 = xg::newGuid(); + auto swap3 = swap1; + auto swap4 = swap2; + + if (swap1 != swap3 || swap2 != swap4 || swap1 == swap2) + { + outStream << "FAIL - swap guids have bad initial state" << std::endl; + failed++; + } + + swap1.swap(swap2); + + if (swap1 != swap4 || swap2 != swap3 || swap1 == swap2) + { + outStream << "FAIL - swap didn't swap" << std::endl; + failed++; + } + + { + std::unordered_map m = {{s1, 1}, {s2, 2}}; + auto it1 = m.find(s1); + auto it2 = m.find(s2); + if(!( it1 != m.end() && it1->first == s1 && it1->second == 1 && it2 != m.end() && it2->first == s2 && it2->second == 2)) + { + outStream << "FAIL - map/hash failed!" << std::endl; + failed++; + } + auto it3 = m.find(s3); + if(it3 != m.end()) + { + outStream << "FAIL - map/hash failed!" << std::endl; + failed++; + } + } + std::array bytes = + {{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xdd + }}; + xg::Guid guidFromBytes(bytes); + xg::Guid guidFromString("0102030405060708090a0b0c0d0e0fdd"); + if (guidFromBytes != guidFromString) + { + outStream << "FAIL - String/bytes make different guids" << std::endl; + failed++; + } + + if(!std::equal(guidFromBytes.bytes().begin(), guidFromBytes.bytes().end(), bytes.begin())) + { + outStream << "FAIL - array returned from bytes() is wrong" << std::endl; + failed++; + } + + /************************************************************************* + * ERROR HANDLING + *************************************************************************/ + + xg::Guid empty; + xg::Guid twoTooFew("7bcd757f-5b10-4f9b-af69-1a1f226f3b"); + if (twoTooFew != empty || twoTooFew.isValid()) + { + outStream << "FAIL - Guid from two too few chars" << std::endl; + failed++; + } + + xg::Guid oneTooFew("16d1bd03-09a5-47d3-944b-5e326fd52d2"); + if (oneTooFew != empty || oneTooFew.isValid()) + { + outStream << "FAIL - Guid from one too few chars" << std::endl; + failed++; + } + + xg::Guid twoTooMany("7bcd757f-5b10-4f9b-af69-1a1f226f3beeff"); + if (twoTooMany != empty || twoTooMany.isValid()) + { + outStream << "FAIL - Guid from two too many chars" << std::endl; + failed++; + } + + xg::Guid oneTooMany("16d1bd03-09a5-47d3-944b-5e326fd52d27a"); + if (oneTooMany != empty || oneTooMany.isValid()) + { + outStream << "FAIL - Guid from one too many chars" << std::endl; + failed++; + } + + xg::Guid badString("!!bad-guid-string!!"); + if (badString != empty || badString.isValid()) + { + outStream << "FAIL - Guid from bad string" << std::endl; + failed++; + } + + if (failed == 0) + { + outStream << "All tests passed!" << std::endl; + return 0; + } + else + { + outStream << failed << " tests failed." << std::endl; + return 1; + } +} diff --git a/libraries/crossguid/test/Test.hpp b/libraries/crossguid/test/Test.hpp new file mode 100644 index 000000000..aa8b411a7 --- /dev/null +++ b/libraries/crossguid/test/Test.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include + +int test(std::ostream &outStream); diff --git a/libraries/crossguid/test/TestMain.cpp b/libraries/crossguid/test/TestMain.cpp new file mode 100644 index 000000000..2ef5c1572 --- /dev/null +++ b/libraries/crossguid/test/TestMain.cpp @@ -0,0 +1,7 @@ +#include "Test.hpp" +#include + +int main() +{ + return test(std::cout); +}