diff --git a/.travis.yml b/.travis.yml index 52942e4d..1c3ed166 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,13 +3,13 @@ compiler: - gcc before_install: + # Add a PPA for Boost 1.55. + - sudo add-apt-repository ppa:boost-latest/ppa -y - sudo apt-get update -qq install: # Currently inside the cloned repo path. - # Need Boost 1.54+, which isn't in the 12.04 repositories - install from a PPA. - - sudo add-apt-repository ppa:boost-latest/ppa -y - - sudo apt-get update -qq + # Install Boost. - sudo apt-get install libboost-log1.55-dev libboost-date-time1.55-dev libboost-thread1.55-dev libboost-filesystem1.55-dev libboost-locale1.55-dev libboost-iostreams1.55-dev # Install Alphanum - cd ../.. @@ -54,7 +54,11 @@ install: before_script: - mkdir build - cd build + # Fetch plugins to test with. + - wget https://github.com/WrinklyNinja/testing-plugins/archive/master.zip + - unzip master.zip + - mv testing-plugins-master/* ./ # Travis machines are 64 bit, and the deps use dynamic linking. - - cmake .. -DPROJECT_ARCH=64 -DPROJECT_STATIC_RUNTIME=OFF -DBUILD_SHARED_LIBS=OFF + - cmake .. -DPROJECT_ARCH=64 -DPROJECT_STATIC_RUNTIME=OFF -DBUILD_SHARED_LIBS=OFF -DGTEST_ROOT=../../gtest-1.7.0 -script: make loot64 +script: make tests && ./tests diff --git a/CMakeLists.txt b/CMakeLists.txt index 231ec74f..7ffe9664 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,12 @@ set (Boost_USE_STATIC_LIBS ${PROJECT_STATIC_RUNTIME}) set (Boost_USE_MULTITHREADED ON) set (Boost_USE_STATIC_RUNTIME ${PROJECT_STATIC_RUNTIME}) +IF (NOT Boost_USE_STATIC_LIBS) + add_definitions(-DBOOST_LOG_DYN_LINK) +ENDIF () + find_package(Boost REQUIRED COMPONENTS log log_setup regex locale thread date_time chrono filesystem system iostreams) +find_package(GTest) set (LOOT_SRC "${CMAKE_SOURCE_DIR}/src/backend/metadata.cpp" "${CMAKE_SOURCE_DIR}/src/backend/game.cpp" @@ -88,7 +93,12 @@ set (LOOT_API_SRC ${LOOT_SRC} set (LOOT_API_HEADERS ${LOOT_HEADERS} "${CMAKE_SOURCE_DIR}/src/api/api.h") -source_group("Header Files" FILES ${LOOT_HEADERS} ${LOOT_GUI_HEADERS} ${LOOT_API_HEADERS}) +set (LOOT_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/tests/main.cpp") + +set (LOOT_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/fixtures.h" + "${CMAKE_SOURCE_DIR}/src/tests/api/api.h") + +source_group("Header Files" FILES ${LOOT_HEADERS} ${LOOT_GUI_HEADERS} ${LOOT_API_HEADERS} ${LOOT_TESTS_HEADERS}) # Include source and library directories. include_directories ("${CMAKE_SOURCE_DIR}/src" @@ -98,7 +108,8 @@ include_directories ("${CMAKE_SOURCE_DIR}/src" "${LIBGIT2_ROOT}/include" ${CEF_ROOT} ${LIBESPM_ROOT} - ${Boost_INCLUDE_DIRS}) + ${Boost_INCLUDE_DIRS} + ${GTEST_INCLUDE_DIRS}) link_directories ("${LIBLOADORDER_ROOT}/build" "${LIBGIT2_ROOT}/build" @@ -113,12 +124,7 @@ link_directories ("${LIBLOADORDER_ROOT}/build" # Settings when compiling for Windows. Since it's a Windows-only app this is always true, but useful to check for copy/paste into other projects. IF (CMAKE_SYSTEM_NAME MATCHES "Windows") - add_definitions (-DUNICODE -D_UNICODE -DNDEBUG -DLIBLO_STATIC -DWIN32 -D_WINDOWS) - IF (BUILD_SHARED_LIBS) - add_definitions (-DLOOT_EXPORT) - ELSE () - add_definitions (-DLOOT_STATIC) - ENDIF () + add_definitions (-DUNICODE -D_UNICODE -DLIBLO_STATIC) ENDIF () # GCC and MinGW settings. @@ -144,8 +150,8 @@ IF (MINGW) set (LOOT_LIBS ${LOOT_LIBS} version - ws2_32 - shlwapi) + ws2_32 + shlwapi) set (LOOT_GUI_LIBS ${LOOT_LIBS} cef_sandbox libcef @@ -165,13 +171,12 @@ ELSEIF (MSVC) ENDFOREACH() ENDIF () - set (CMAKE_EXE_LINKER_FLAGS "/SUBSYSTEM:WINDOWS /LARGEADDRESSAWARE") set (LOOT_LIBS git2 - libyaml-cppmt - version - loadorder${PROJECT_ARCH} - ws2_32 - shlwapi) + libyaml-cppmt + version + loadorder${PROJECT_ARCH} + ws2_32 + shlwapi) set (LOOT_GUI_LIBS ${LOOT_LIBS} cef_sandbox libcef @@ -182,17 +187,46 @@ ENDIF () ############################## -# Actual Building +# Define Targets ############################## # Build API. add_library (loot${PROJECT_ARCH} ${LOOT_API_SRC} ${LOOT_API_HEADERS}) target_link_libraries (loot${PROJECT_ARCH} ${Boost_LIBRARIES} ${LOOT_LIBS}) +IF (${GTEST_FOUND}) + # Build tests. + add_executable(tests ${LOOT_TESTS_SRC} ${LOOT_TESTS_HEADERS}) + target_link_libraries(tests loot${PROJECT_ARCH} ${Boost_LIBRARIES} ${LOOT_LIBS} ${GTEST_BOTH_LIBRARIES}) +ENDIF () + # Build application. add_executable (LOOT ${LOOT_GUI_SRC} ${LOOT_GUI_HEADERS}) target_link_libraries (LOOT ${Boost_LIBRARIES} ${LOOT_GUI_LIBS}) + +############################## +# Set Target-Specific Flags +############################## + +IF (MSVC) + set_target_properties (LOOT PROPERTIES CMAKE_EXE_LINKER_FLAGS "/SUBSYSTEM:WINDOWS /LARGEADDRESSAWARE") +ENDIF () + + +IF (CMAKE_SYSTEM_NAME MATCHES "Windows") + IF (BUILD_SHARED_LIBS) + set_target_properties (loot${PROJECT_ARCH} PROPERTIES COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS} LOOT_EXPORT") + ELSE () + set_target_properties (loot${PROJECT_ARCH} PROPERTIES COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS} LOOT_STATIC") + ENDIF () +ENDIF () + + +############################## +# Post-Build Steps +############################## + add_custom_command( TARGET LOOT POST_BUILD diff --git a/README.md b/README.md index 66f21645..abafdf0f 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ LOOT uses [CMake](http://cmake.org) to generate build files, and requires the fo * [Alphanum](http://www.davekoelle.com/files/alphanum.hpp) * [Boost](http://www.boost.org) v1.56.0 * [Chromium Embedded Framework](https://code.google.com/p/chromiumembedded/) branch 2062 +* [Google Test](https://code.google.com/p/googletest/) v1.7: Required to build the LOOT API's tests, but not the API itself or the LOOT application. * [Libespm](http://github.com/WrinklyNinja/libespm) * [Libgit2](http://libgit2.github.com/) v0.21.1 * [Libloadorder](http://github.com/WrinklyNinja/libloadorder) diff --git a/src/tests/api/api.h b/src/tests/api/api.h new file mode 100644 index 00000000..53781f4e --- /dev/null +++ b/src/tests/api/api.h @@ -0,0 +1,135 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014 WrinklyNinja + +This file is part of LOOT. + +LOOT is free software: you can redistribute +it and/or modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +LOOT is distributed in the hope that it will +be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LOOT. If not, see +. +*/ + +#ifndef LOOT_TEST_API +#define LOOT_TEST_API + +#include "../../api/api.h" +#include "tests/fixtures.h" + +TEST(GetVersion, HandlesNullInput) { + unsigned int vMajor, vMinor, vPatch; + EXPECT_EQ(loot_error_invalid_args, loot_get_version(&vMajor, NULL, NULL)); + EXPECT_EQ(loot_error_invalid_args, loot_get_version(NULL, &vMinor, NULL)); + EXPECT_EQ(loot_error_invalid_args, loot_get_version(NULL, NULL, &vPatch)); + EXPECT_EQ(loot_error_invalid_args, loot_get_version(NULL, NULL, NULL)); +} + +TEST(GetVersion, HandlesValidInput) { + unsigned int vMajor, vMinor, vPatch; + EXPECT_EQ(loot_ok, loot_get_version(&vMajor, &vMinor, &vPatch)); +} + +TEST(IsCompatible, HandlesCompatibleVersion) { + unsigned int vMajor, vMinor, vPatch; + EXPECT_EQ(loot_ok, loot_get_version(&vMajor, &vMinor, &vPatch)); + + EXPECT_TRUE(loot_is_compatible(vMajor, vMinor, vPatch)); + // Test somewhat arbitrary variations. + EXPECT_TRUE(loot_is_compatible(vMajor, vMinor + 1, vPatch + 1)); + if (vMinor > 0 && vPatch > 0) + EXPECT_TRUE(loot_is_compatible(vMajor, vMinor - 1, vPatch - 1)); +} + +TEST(IsCompatible, HandlesIncompatibleVersion) { + unsigned int vMajor, vMinor, vPatch; + EXPECT_EQ(loot_ok, loot_get_version(&vMajor, &vMinor, &vPatch)); + + EXPECT_FALSE(loot_is_compatible(vMajor + 1, vMinor, vPatch)); + // Test somewhat arbitrary variations. + EXPECT_FALSE(loot_is_compatible(vMajor + 1, vMinor + 1, vPatch + 1)); + if (vMinor > 0 && vPatch > 0) + EXPECT_FALSE(loot_is_compatible(vMajor + 1, vMinor - 1, vPatch - 1)); +} + +TEST(GetErrorMessage, HandlesInputCorrectly) { + EXPECT_EQ(loot_error_invalid_args, loot_get_error_message(NULL)); + + const char * error; + EXPECT_EQ(loot_ok, loot_get_error_message(&error)); + ASSERT_STREQ("Null message pointer passed.", error); +} + +TEST(Cleanup, CleansUpAfterError) { + // First generate an error. + EXPECT_EQ(loot_error_invalid_args, loot_get_error_message(NULL)); + + // Check that the error message is non-null. + const char * error; + EXPECT_EQ(loot_ok, loot_get_error_message(&error)); + ASSERT_STREQ("Null message pointer passed.", error); + + ASSERT_NO_THROW(loot_cleanup()); + + // Now check that the error message pointer is null. + error = nullptr; + EXPECT_EQ(loot_ok, loot_get_error_message(&error)); + EXPECT_EQ(nullptr, error); +} + +TEST(Cleanup, HandlesNoError) { + ASSERT_NO_THROW(loot_cleanup()); + + const char * error = nullptr; + EXPECT_EQ(loot_ok, loot_get_error_message(&error)); + EXPECT_EQ(nullptr, error); +} + +TEST_F(OblivionTest, CreateDbHandlesValidInputs) { + EXPECT_EQ(loot_ok, loot_create_db(&db, loot_game_tes4, dataPath.parent_path().string().c_str(), localPath.string().c_str())); + ASSERT_NO_THROW(loot_destroy_db(db)); + db = nullptr; + + // Also test absolute paths. + boost::filesystem::path game = boost::filesystem::current_path() / dataPath.parent_path(); + boost::filesystem::path local = boost::filesystem::current_path() / localPath; + EXPECT_EQ(loot_ok, loot_create_db(&db, loot_game_tes4, game.string().c_str(), local.string().c_str())); +} + +TEST_F(OblivionTest, CreateDbHandlesInvalidHandleInput) { + EXPECT_EQ(loot_error_invalid_args, loot_create_db(NULL, loot_game_tes4, dataPath.parent_path().string().c_str(), localPath.string().c_str())); +} + +TEST_F(OblivionTest, CreateDbHandlesInvalidGameType) { + EXPECT_EQ(loot_error_invalid_args, loot_create_db(&db, UINT_MAX, dataPath.parent_path().string().c_str(), localPath.string().c_str())); +} + +TEST_F(OblivionTest, CreateDbHandlesInvalidGamePathInput) { + EXPECT_EQ(loot_error_invalid_args, loot_create_db(&db, loot_game_tes4, missingPath.string().c_str(), localPath.string().c_str())); +} + +TEST_F(OblivionTest, CreateDbHandlesInvalidLocalPathInput) { + EXPECT_EQ(loot_error_invalid_args, loot_create_db(&db, loot_game_tes4, dataPath.parent_path().string().c_str(), missingPath.string().c_str())); +} + +#ifdef _WIN32 +TEST_F(OblivionTest, CreateDbHandlesNullLocalPath) { + EXPECT_EQ(loot_ok, loot_create_db(&db, loot_game_tes4, dataPath.parent_path().string().c_str(), NULL)); +} +#endif + +TEST(GameHandleDestroyTest, HandledNullInput) { + ASSERT_NO_THROW(loot_destroy_db(NULL)); +} +#endif diff --git a/src/tests/fixtures.h b/src/tests/fixtures.h new file mode 100644 index 00000000..179a61f0 --- /dev/null +++ b/src/tests/fixtures.h @@ -0,0 +1,228 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2013-2014 WrinklyNinja + +This file is part of LOOT. + +LOOT is free software: you can redistribute +it and/or modify it under the terms of the GNU General Public License +as published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +LOOT is distributed in the hope that it will +be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LOOT. If not, see +. +*/ + +#ifndef LOOT_TEST_FIXTURES +#define LOOT_TEST_FIXTURES + +#include "backend/streams.h" + +#include + +#ifdef __GNUC__ // Workaround for GCC linking error. +#pragma message("GCC detected: Defining BOOST_NO_CXX11_SCOPED_ENUMS and BOOST_NO_SCOPED_ENUMS to avoid linking errors for boost::filesystem::copy_file().") +#define BOOST_NO_CXX11_SCOPED_ENUMS +#define BOOST_NO_SCOPED_ENUMS // For older versions. +#endif +#include + +class GameTest : public ::testing::Test { +protected: + GameTest(const boost::filesystem::path& gameDataPath, const boost::filesystem::path& gameLocalPath) + : dataPath(gameDataPath), localPath(gameLocalPath), missingPath("./missing"), db(nullptr) {} + + inline virtual void SetUp() { + ASSERT_NO_THROW(boost::filesystem::create_directories(localPath)); + ASSERT_TRUE(boost::filesystem::exists(localPath)); + + ASSERT_FALSE(boost::filesystem::exists(missingPath)); + + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank.esm")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Different.esm")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Master Dependent.esm")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Different Master Dependent.esm")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank.esp")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Different.esp")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Master Dependent.esp")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Different Master Dependent.esp")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Plugin Dependent.esp")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Different Plugin Dependent.esp")); + + ASSERT_FALSE(boost::filesystem::exists(dataPath / "Blank.esm.missing")); + ASSERT_FALSE(boost::filesystem::exists(dataPath / "Blank.esp.missing")); + + // Ghost a plugin. + ASSERT_FALSE(boost::filesystem::exists(dataPath / "Blank - Master Dependent.esm.ghost")); + ASSERT_NO_THROW(boost::filesystem::rename(dataPath / "Blank - Master Dependent.esm", dataPath / "Blank - Master Dependent.esm.ghost")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Master Dependent.esm.ghost")); + + // Write out an empty file. + loot::ofstream out(dataPath / "EmptyFile.esm"); + out.close(); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "EmptyFile.esm")); + + // Write out an non-empty, non-plugin file. + out.open(dataPath / "NotAPlugin.esm"); + out << "This isn't a valid plugin file."; + out.close(); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "NotAPlugin.esm")); + } + + inline virtual void TearDown() { + // Unghost the ghosted plugin. + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Blank - Master Dependent.esm.ghost")); + ASSERT_NO_THROW(boost::filesystem::rename(dataPath / "Blank - Master Dependent.esm.ghost", dataPath / "Blank - Master Dependent.esm")); + ASSERT_FALSE(boost::filesystem::exists(dataPath / "Blank - Master Dependent.esm.ghost")); + + // Delete generated files. + ASSERT_NO_THROW(boost::filesystem::remove(dataPath / "EmptyFile.esm")); + ASSERT_NO_THROW(boost::filesystem::remove(dataPath / "NotAPlugin.esm")); + ASSERT_FALSE(boost::filesystem::exists(dataPath / "EmptyFile.esm")); + ASSERT_FALSE(boost::filesystem::exists(dataPath / "NotAPlugin.esm")); + + ASSERT_NO_THROW(loot_destroy_db(db)); + } + + const boost::filesystem::path dataPath; + const boost::filesystem::path localPath; + const boost::filesystem::path missingPath; + + loot_db db; +}; + +class OblivionTest : public GameTest { +protected: + OblivionTest() : GameTest("./Oblivion/Data", "./local/Oblivion") {} + + inline virtual void SetUp() { + GameTest::SetUp(); + + // LOOT expects the game master file to be present, so mock it. + ASSERT_FALSE(boost::filesystem::exists(dataPath / "Oblivion.esm")); + ASSERT_NO_THROW(boost::filesystem::copy_file(dataPath / "Blank.esm", dataPath / "Oblivion.esm")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Oblivion.esm")); + + // Oblivion's load order is decided through timestamps, so reset them to a known order before each test. + std::list loadOrder = { + "Oblivion.esm", + "Blank.esm", + "Blank - Different.esm", + "Blank - Master Dependent.esm", // Ghosted + "Blank - Different Master Dependent.esm", + "Blank.esp", + "Blank - Different.esp", + "Blank - Master Dependent.esp", + "Blank - Different Master Dependent.esp", + "Blank - Plugin Dependent.esp", + "Blank - Different Plugin Dependent.esp" + }; + time_t modificationTime = time(NULL); // Current time. + for (const auto &plugin : loadOrder) { + if (boost::filesystem::exists(dataPath / boost::filesystem::path(plugin + ".ghost"))) { + boost::filesystem::last_write_time(dataPath / boost::filesystem::path(plugin + ".ghost"), modificationTime); + } + else { + boost::filesystem::last_write_time(dataPath / plugin, modificationTime); + } + modificationTime += 60; + } + + // Set Oblivion's active plugins to a known list before running the test. + loot::ofstream activePlugins(localPath / "plugins.txt"); + activePlugins + << "Oblivion.esm" << std::endl + << "Blank.esm" << std::endl; + activePlugins.close(); + } + + inline virtual void TearDown() { + GameTest::TearDown(); + + // Delete the mock Oblivion.esm. + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Oblivion.esm")); + ASSERT_NO_THROW(boost::filesystem::remove(dataPath / "Oblivion.esm")); + ASSERT_FALSE(boost::filesystem::exists(dataPath / "Oblivion.esm")); + + // Delete existing plugins.txt. + ASSERT_NO_THROW(boost::filesystem::remove(localPath / "plugins.txt")); + }; +}; + +class OblivionAPIOperationsTest : public OblivionTest { +protected: + inline virtual void SetUp() { + OblivionTest::SetUp(); + + ASSERT_EQ(loot_ok, loot_create_db(&db, loot_game_tes4, dataPath.parent_path().string().c_str(), localPath.string().c_str())); + } +}; + +class SkyrimTest : public GameTest { +protected: + SkyrimTest() : GameTest("./Skyrim/Data", "./local/Skyrim") {} + + inline virtual void SetUp() { + GameTest::SetUp(); + + // Can't change Skyrim's main master file, so mock it. + ASSERT_FALSE(boost::filesystem::exists(dataPath / "Skyrim.esm")); + ASSERT_NO_THROW(boost::filesystem::copy_file(dataPath / "Blank.esm", dataPath / "Skyrim.esm")); + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Skyrim.esm")); + + // Set Skyrim's load order to a known list before running the test. + loot::ofstream loadOrder(localPath / "loadorder.txt"); + loadOrder + << "Skyrim.esm" << std::endl + << "Blank.esm" << std::endl + << "Blank - Different.esm" << std::endl + << "Blank - Master Dependent.esm" << std::endl // Ghosted + << "Blank - Different Master Dependent.esm" << std::endl + << "Blank.esp" << std::endl + << "Blank - Different.esp" << std::endl + << "Blank - Master Dependent.esp" << std::endl + << "Blank - Different Master Dependent.esp" << std::endl + << "Blank - Plugin Dependent.esp" << std::endl + << "Blank - Different Plugin Dependent.esp" << std::endl; + loadOrder.close(); + + // Set Skyrim's active plugins to a known list before running the test. + loot::ofstream activePlugins(localPath / "plugins.txt"); + activePlugins + << "Blank.esm" << std::endl; + activePlugins.close(); + } + + inline virtual void TearDown() { + GameTest::TearDown(); + + // Delete the mock Skyrim.esm. + ASSERT_TRUE(boost::filesystem::exists(dataPath / "Skyrim.esm")); + ASSERT_NO_THROW(boost::filesystem::remove(dataPath / "Skyrim.esm")); + ASSERT_FALSE(boost::filesystem::exists(dataPath / "Skyrim.esm")); + + // Delete existing plugins.txt and loadorder.txt. + ASSERT_NO_THROW(boost::filesystem::remove(localPath / "plugins.txt")); + ASSERT_NO_THROW(boost::filesystem::remove(localPath / "loadorder.txt")); + }; +}; + +class SkyrimAPIOperationsTest : public SkyrimTest { +protected: + inline virtual void SetUp() { + SkyrimTest::SetUp(); + + ASSERT_EQ(loot_ok, loot_create_db(&db, loot_game_tes5, dataPath.parent_path().string().c_str(), localPath.string().c_str())); + } +}; + +#endif \ No newline at end of file diff --git a/src/tests/main.cpp b/src/tests/main.cpp new file mode 100644 index 00000000..a5824db2 --- /dev/null +++ b/src/tests/main.cpp @@ -0,0 +1,30 @@ +/* LOOT + + A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and + Fallout: New Vegas. + + Copyright (C) 2014 WrinklyNinja + + This file is part of LOOT. + + LOOT is free software: you can redistribute + it and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + LOOT is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LOOT. If not, see + . + */ + +#include "tests/api/api.h" + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}