#include "catch.hpp" #include #include "language_packages.h" #include "AppPath.h" #include "Application.h" #include "ApplicationSettings.h" #include "FileSystem.h" #include "IndexerCommandCustom.h" #include "ProjectSettings.h" #include "SourceGroupCustomCommand.h" #include "SourceGroupSettingsCustomCommand.h" #include "TextAccess.h" #include "Version.h" #include "utilityPathDetection.h" #include "utilityString.h" #if BUILD_CXX_LANGUAGE_PACKAGE # include "IndexerCommandCxx.h" # include "SourceGroupCxxCdb.h" # include "SourceGroupCxxCodeblocks.h" # include "SourceGroupCxxEmpty.h" # include "SourceGroupSettingsCEmpty.h" # include "SourceGroupSettingsCppEmpty.h" # include "SourceGroupSettingsCxxCdb.h" # include "SourceGroupSettingsCxxCodeblocks.h" #endif // BUILD_CXX_LANGUAGE_PACKAGE #if BUILD_JAVA_LANGUAGE_PACKAGE # include "IndexerCommandJava.h" # include "JavaEnvironmentFactory.h" # include "SourceGroupJavaEmpty.h" # include "SourceGroupJavaGradle.h" # include "SourceGroupJavaMaven.h" # include "SourceGroupSettingsJavaEmpty.h" # include "SourceGroupSettingsJavaGradle.h" # include "SourceGroupSettingsJavaMaven.h" # include "utilityJava.h" #endif // BUILD_JAVA_LANGUAGE_PACKAGE #define REQUIRE_MESSAGE(msg, cond) \ do \ { \ INFO(msg); \ REQUIRE(cond); \ } while ((void)0, 0) namespace { const bool updateExpectedOutput = false; static FilePath getInputDirectoryPath(const std::wstring& projectName) { return FilePath(L"data/SourceGroupTestSuite/" + projectName + L"/input") .makeAbsolute() .makeCanonical(); } static FilePath getOutputDirectoryPath(const std::wstring& projectName) { return FilePath(L"data/SourceGroupTestSuite/" + projectName + L"/expected_output") .makeAbsolute() .makeCanonical(); } #if BUILD_JAVA_LANGUAGE_PACKAGE std::string setupJavaEnvironmentFactory() { if (!JavaEnvironmentFactory::getInstance()) { std::string errorString; # ifdef _WIN32 const std::string separator = ";"; # else const std::string separator = ":"; # endif std::string classPath = ""; { const std::vector jarNames = utility::getRequiredJarNames(); for (size_t i = 0; i < jarNames.size(); i++) { if (i != 0) { classPath += separator; } classPath += FilePath(L"../app/data/java/lib/").concatenate(jarNames[i]).str(); } } JavaEnvironmentFactory::createInstance(classPath, errorString); return errorString; } return ""; } #endif // BUILD_JAVA_LANGUAGE_PACKAGE #if BUILD_CXX_LANGUAGE_PACKAGE std::wstring indexerCommandCxxToString( std::shared_ptr indexerCommand, const FilePath& baseDirectory) { std::wstring result; result += L"SourceFilePath: \"" + indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n"; for (const FilePath& indexedPath: indexerCommand->getIndexedPaths()) { result += L"\tIndexedPath: \"" + indexedPath.getRelativeTo(baseDirectory).wstr() + L"\"\n"; } for (std::wstring compilerFlag: indexerCommand->getCompilerFlags()) { FilePath flagAsPath(compilerFlag); if (flagAsPath.exists()) { compilerFlag = flagAsPath.getRelativeTo(baseDirectory).wstr(); } result += L"\tCompilerFlag: \"" + compilerFlag + L"\"\n"; } for (const FilePathFilter& filter: indexerCommand->getExcludeFilters()) { result += L"\tExcludeFilter: \"" + filter.wstr() + L"\"\n"; } return result; } #endif // BUILD_CXX_LANGUAGE_PACKAGE #if BUILD_JAVA_LANGUAGE_PACKAGE std::wstring indexerCommandJavaToString( std::shared_ptr indexerCommand, const FilePath& baseDirectory) { std::wstring result; result += L"SourceFilePath: \"" + indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n"; result += L"\tLanguageStandard: \"" + indexerCommand->getLanguageStandard() + L"\"\n"; for (const FilePath& classPathItem: indexerCommand->getClassPath()) { result += L"\tClassPathItem: \"" + classPathItem.getRelativeTo(baseDirectory).wstr() + L"\"\n"; } return result; } #endif // BUILD_JAVA_LANGUAGE_PACKAGE std::wstring indexerCommandCustomToString( std::shared_ptr indexerCommand, const FilePath& baseDirectory) { std::wstring result; result += L"SourceFilePath: \"" + indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n"; result += L"\tCustom Command: \"" + indexerCommand->getCustomCommand() + L"\"\n"; return result; } std::wstring indexerCommandToString( std::shared_ptr indexerCommand, const FilePath& baseDirectory) { if (indexerCommand) { #if BUILD_CXX_LANGUAGE_PACKAGE if (std::shared_ptr indexerCommandCxx = std::dynamic_pointer_cast(indexerCommand)) { return indexerCommandCxxToString(indexerCommandCxx, baseDirectory); } #endif // BUILD_CXX_LANGUAGE_PACKAGE #if BUILD_JAVA_LANGUAGE_PACKAGE if (std::shared_ptr indexerCommandJava = std::dynamic_pointer_cast(indexerCommand)) { return indexerCommandJavaToString(indexerCommandJava, baseDirectory); } #endif // BUILD_JAVA_LANGUAGE_PACKAGE if (std::shared_ptr indexerCommandCustom = std::dynamic_pointer_cast(indexerCommand)) { return indexerCommandCustomToString(indexerCommandCustom, baseDirectory); } return L"Unsupported indexer command type: " + utility::decodeFromUtf8( indexerCommandTypeToString(indexerCommand->getIndexerCommandType())); } return L"No IndexerCommand provided."; } std::shared_ptr generateExpectedOutput( std::wstring projectName, std::shared_ptr sourceGroup) { const FilePath projectDataRoot = getInputDirectoryPath(projectName).makeAbsolute(); RefreshInfo info; info.filesToIndex = sourceGroup->getAllSourceFilePaths(); std::vector> indexerCommands = sourceGroup->getIndexerCommands( info); std::sort( indexerCommands.begin(), indexerCommands.end(), [](std::shared_ptr a, std::shared_ptr b) { return a->getSourceFilePath().wstr() < b->getSourceFilePath().wstr(); }); std::wstring outputString; for (std::shared_ptr indexerCommand: indexerCommands) { outputString += indexerCommandToString(indexerCommand, projectDataRoot); } return TextAccess::createFromString(utility::encodeToUtf8(outputString)); } void generateAndCompareExpectedOutput( std::wstring projectName, std::shared_ptr sourceGroup) { const std::shared_ptr output = generateExpectedOutput(projectName, sourceGroup); #ifdef WIN32 const std::wstring expectedOutputFileName = L"output_windows.txt"; #else const std::wstring expectedOutputFileName = L"output_unix.txt"; #endif const FilePath expectedOutputFilePath = getOutputDirectoryPath(projectName).concatenate(expectedOutputFileName); if (updateExpectedOutput || !expectedOutputFilePath.exists()) { std::ofstream expectedOutputFile; expectedOutputFile.open(expectedOutputFilePath.str()); expectedOutputFile << output->getText(); expectedOutputFile.close(); } else { const std::shared_ptr expectedOutput = TextAccess::createFromFile( expectedOutputFilePath); REQUIRE_MESSAGE( ("Output does not match the expected line count for project \"" + utility::encodeToUtf8(projectName) + "\".") .c_str(), expectedOutput->getLineCount() == output->getLineCount()); if (expectedOutput->getLineCount() == output->getLineCount()) { for (size_t i = 1; i <= expectedOutput->getLineCount(); i++) { REQUIRE(expectedOutput->getLine(i) == output->getLine(i)); } } } } } // namespace #if BUILD_JAVA_LANGUAGE_PACKAGE TEST_CASE("finds all jar dependencies") { for (const std::wstring& jarName: utility::getRequiredJarNames()) { FilePath jarPath = FilePath(L"../app/data/java/lib/").concatenate(jarName); REQUIRE_MESSAGE("Jar dependency path does not exist: " + jarPath.str(), jarPath.exists()); } } TEST_CASE("can setup environment factory") { std::vector javaPaths = utility::getJavaRuntimePathDetector()->getPaths(); if (!javaPaths.empty()) { ApplicationSettings::getInstance()->setJavaPath(javaPaths[0]); } const std::string errorString = setupJavaEnvironmentFactory(); REQUIRE("" == errorString); // if this one fails, maybe your java_path in the test settings is wrong. REQUIRE(JavaEnvironmentFactory::getInstance().use_count() >= 1); } #endif // BUILD_JAVA_LANGUAGE_PACKAGE TEST_CASE("can create application instance") { // required to query in SourceGroup for dialog view... this is not a very elegant solution. // should be refactored to pass dialog view to SourceGroup on creation. Application::createInstance(Version(), nullptr, nullptr); REQUIRE(Application::getInstance().use_count() >= 1); } #if BUILD_CXX_LANGUAGE_PACKAGE TEST_CASE("source group cxx c empty generates expected output") { const std::wstring projectName = L"cxx_c_empty"; ProjectSettings projectSettings; projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); sourceGroupSettings->setSourcePaths({getInputDirectoryPath(projectName).concatenate(L"src")}); sourceGroupSettings->setSourceExtensions({L".c"}); sourceGroupSettings->setExcludeFilterStrings({L"**/excluded/**"}); sourceGroupSettings->setTargetOptionsEnabled(true); sourceGroupSettings->setTargetArch(L"test_arch"); sourceGroupSettings->setTargetVendor(L"test_vendor"); sourceGroupSettings->setTargetSys(L"test_sys"); sourceGroupSettings->setTargetAbi(L"test_abi"); sourceGroupSettings->setCStandard(L"c11"); sourceGroupSettings->setHeaderSearchPaths( {getInputDirectoryPath(projectName).concatenate(L"header_search/local")}); sourceGroupSettings->setFrameworkSearchPaths( {getInputDirectoryPath(projectName).concatenate(L"framework_search/local")}); sourceGroupSettings->setCompilerFlags({L"-local-flag"}); std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); applicationSettings->setHeaderSearchPaths({FilePath(L"test/header/search/path")}); applicationSettings->setFrameworkSearchPaths({FilePath(L"test/framework/search/path")}); generateAndCompareExpectedOutput( projectName, std::make_shared(sourceGroupSettings)); applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); } TEST_CASE("source group cxx cpp empty generates expected output") { const std::wstring projectName = L"cxx_cpp_empty"; ProjectSettings projectSettings; projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); sourceGroupSettings->setSourcePaths({getInputDirectoryPath(projectName).concatenate(L"/src")}); sourceGroupSettings->setSourceExtensions({L".cpp"}); sourceGroupSettings->setExcludeFilterStrings({L"**/excluded/**"}); sourceGroupSettings->setTargetOptionsEnabled(true); sourceGroupSettings->setTargetArch(L"test_arch"); sourceGroupSettings->setTargetVendor(L"test_vendor"); sourceGroupSettings->setTargetSys(L"test_sys"); sourceGroupSettings->setTargetAbi(L"test_abi"); sourceGroupSettings->setCppStandard(L"c++11"); sourceGroupSettings->setHeaderSearchPaths( {getInputDirectoryPath(projectName).concatenate(L"header_search/local")}); sourceGroupSettings->setFrameworkSearchPaths( {getInputDirectoryPath(projectName).concatenate(L"framework_search/local")}); sourceGroupSettings->setCompilerFlags({L"-local-flag"}); std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); applicationSettings->setHeaderSearchPaths({FilePath(L"test/header/search/path")}); applicationSettings->setFrameworkSearchPaths({FilePath(L"test/framework/search/path")}); generateAndCompareExpectedOutput( projectName, std::make_shared(sourceGroupSettings)); applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); } TEST_CASE("source group cxx codeblocks generates expected output") { const std::wstring projectName = L"cxx_codeblocks"; const FilePath cbpPath = getInputDirectoryPath(projectName).concatenate(L"project.cbp"); const FilePath sourceCbpPath = getInputDirectoryPath(projectName).concatenate(L"project.cbp.in"); FileSystem::remove(cbpPath); { std::ofstream fileStream; fileStream.open(cbpPath.str(), std::ios::app); fileStream << utility::replace( TextAccess::createFromFile(sourceCbpPath)->getText(), "", getInputDirectoryPath(projectName).concatenate(L"src").getAbsolute().str()); fileStream.close(); } ProjectSettings projectSettings; projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); sourceGroupSettings->setCodeblocksProjectPath(cbpPath); sourceGroupSettings->setCppStandard(L"c++11"); sourceGroupSettings->setCStandard(L"c11"); sourceGroupSettings->setExcludeFilterStrings({L"**/excluded/**"}); sourceGroupSettings->setIndexedHeaderPaths({FilePath(L"test/indexed/header/path")}); sourceGroupSettings->setSourceExtensions({L".cpp", L".c"}); sourceGroupSettings->setHeaderSearchPaths( {getInputDirectoryPath(projectName).concatenate(L"header_search/local")}); sourceGroupSettings->setFrameworkSearchPaths( {getInputDirectoryPath(projectName).concatenate(L"framework_search/local")}); sourceGroupSettings->setCompilerFlags({L"-local-flag"}); std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); applicationSettings->setHeaderSearchPaths({FilePath(L"test/header/search/path")}); applicationSettings->setFrameworkSearchPaths({FilePath(L"test/framework/search/path")}); generateAndCompareExpectedOutput( projectName, std::make_shared(sourceGroupSettings)); applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); FileSystem::remove(cbpPath); } TEST_CASE("source group cxx cdb generates expected output") { const std::wstring projectName = L"cxx_cdb"; ProjectSettings projectSettings; projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); sourceGroupSettings->setIndexedHeaderPaths({FilePath(L"test/indexed/header/path")}); sourceGroupSettings->setCompilationDatabasePath( getInputDirectoryPath(projectName).concatenate(L"compile_commands.json")); sourceGroupSettings->setExcludeFilterStrings({L"**/excluded/**"}); sourceGroupSettings->setHeaderSearchPaths( {getInputDirectoryPath(projectName).concatenate(L"header_search/local")}); sourceGroupSettings->setFrameworkSearchPaths( {getInputDirectoryPath(projectName).concatenate(L"framework_search/local")}); sourceGroupSettings->setCompilerFlags({L"-local-flag"}); std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); std::vector storedHeaderSearchPaths = applicationSettings->getHeaderSearchPaths(); std::vector storedFrameworkSearchPaths = applicationSettings->getFrameworkSearchPaths(); applicationSettings->setHeaderSearchPaths({FilePath(L"test/header/search/path")}); applicationSettings->setFrameworkSearchPaths({FilePath(L"test/framework/search/path")}); generateAndCompareExpectedOutput( projectName, std::make_shared(sourceGroupSettings)); applicationSettings->setHeaderSearchPaths(storedHeaderSearchPaths); applicationSettings->setFrameworkSearchPaths(storedFrameworkSearchPaths); } #endif // BUILD_CXX_LANGUAGE_PACKAGE #if BUILD_JAVA_LANGUAGE_PACKAGE TEST_CASE("sourcegroup java empty generates expected output") { const std::wstring projectName = L"java_empty"; ProjectSettings projectSettings; projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); sourceGroupSettings->setSourceExtensions({L".java"}); sourceGroupSettings->setExcludeFilterStrings({L"**/Foo.java"}); sourceGroupSettings->setJavaStandard({L"10"}); sourceGroupSettings->setSourcePaths({getInputDirectoryPath(projectName).concatenate(L"src")}); sourceGroupSettings->setUseJreSystemLibrary(true); sourceGroupSettings->setClasspath( {getInputDirectoryPath(projectName).concatenate(L"lib/dependency.jar"), getInputDirectoryPath(projectName).concatenate(L"classpath_dir")}); std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); applicationSettings->setJreSystemLibraryPaths({FilePath(L"test/jre/system/library/path.jar")}); generateAndCompareExpectedOutput( projectName, std::make_shared(sourceGroupSettings)); applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); } TEST_CASE("sourcegroup java gradle generates expected output") { # ifndef __linux__ const std::wstring projectName = L"java_gradle"; ProjectSettings projectSettings; projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); sourceGroupSettings->setSourceExtensions({L".java"}); sourceGroupSettings->setExcludeFilterStrings({L"**/HelloWorld.java"}); sourceGroupSettings->setJavaStandard({L"10"}); sourceGroupSettings->setGradleProjectFilePath( {getInputDirectoryPath(projectName).concatenate(L"build.gradle")}); sourceGroupSettings->setShouldIndexGradleTests(true); std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); const FilePath storedAppPath = AppPath::getAppPath(); AppPath::setAppPath(storedAppPath.getConcatenated(L"../app").makeAbsolute()); std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); applicationSettings->setJreSystemLibraryPaths({FilePath(L"test/jre/system/library/path.jar")}); generateAndCompareExpectedOutput( projectName, std::make_shared(sourceGroupSettings)); applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); AppPath::setAppPath(storedAppPath); # endif } TEST_CASE("sourcegroup java maven generates expected output") { std::vector mavenPaths = utility::getMavenExecutablePathDetector()->getPaths(); REQUIRE(!mavenPaths.empty()); if (!mavenPaths.empty()) { ApplicationSettings::getInstance()->setMavenPath(mavenPaths.front()); } const std::wstring projectName = L"java_maven"; ProjectSettings projectSettings; projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); sourceGroupSettings->setSourceExtensions({L".java"}); sourceGroupSettings->setExcludeFilterStrings({L"**/Foo.java"}); sourceGroupSettings->setJavaStandard({L"10"}); sourceGroupSettings->setMavenProjectFilePath( {getInputDirectoryPath(projectName).concatenate(L"my-app/pom.xml")}); sourceGroupSettings->setShouldIndexMavenTests(true); std::shared_ptr applicationSettings = ApplicationSettings::getInstance(); std::vector storedJreSystemLibraryPaths = applicationSettings->getJreSystemLibraryPaths(); applicationSettings->setJreSystemLibraryPaths({FilePath(L"test/jre/system/library/path.jar")}); generateAndCompareExpectedOutput( projectName, std::make_shared(sourceGroupSettings)); applicationSettings->setJreSystemLibraryPaths(storedJreSystemLibraryPaths); } #endif // BUILD_JAVA_LANGUAGE_PACKAGE // fixme: test python source group here TEST_CASE("source group custom command generates expected output") { const std::wstring projectName = L"custom_command"; ProjectSettings projectSettings; projectSettings.setProjectFilePath(L"non_existent_project", getInputDirectoryPath(projectName)); std::shared_ptr sourceGroupSettings = std::make_shared("fake_id", &projectSettings); sourceGroupSettings->setCustomCommand(L"echo \"Hello World\""); sourceGroupSettings->setSourcePaths({getInputDirectoryPath(projectName).concatenate(L"/src")}); sourceGroupSettings->setSourceExtensions({L".txt"}); sourceGroupSettings->setExcludeFilterStrings({L"**/excluded/**"}); generateAndCompareExpectedOutput( projectName, std::make_shared(sourceGroupSettings)); } TEST_CASE("can destroy application instance") { Application::destroyInstance(); REQUIRE(0 == Application::getInstance().use_count()); }