From 722772bd9a895ee4c012db25ba0152a76fb1674e Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Tue, 11 Dec 2018 12:36:26 +0100 Subject: [PATCH] refine tests for SourcetrailDBWriter interface --- core/include/NameHierarchy.h | 4 + core/include/SourcetrailDBWriter.h | 1 - core/src/NameHierarchy.cpp | 20 ++ core/src/SourcetrailDBWriter.cpp | 25 +- core/test/test.cpp | 506 ++++++++++++++++++++++++++--- 5 files changed, 495 insertions(+), 61 deletions(-) diff --git a/core/include/NameHierarchy.h b/core/include/NameHierarchy.h index 4a8f2ae..2d782b1 100644 --- a/core/include/NameHierarchy.h +++ b/core/include/NameHierarchy.h @@ -26,6 +26,9 @@ namespace sourcetrail { /** * Struct that represents an entire name of a symbol. + * + * TODO: explain prefix and postfix + * TODO: alway provide prefix and postfix of all name elements to make them unique. Example from C++ (2 foo::bar (foo has different signature) */ struct NameHierarchy { @@ -33,6 +36,7 @@ namespace sourcetrail std::vector nameElements; }; + std::string serializeNameHierarchyToDatabaseString(const NameHierarchy& nameHierarchy); std::string serializeNameHierarchyToJson(const NameHierarchy& nameHierarchy); NameHierarchy deserializeNameHierarchyFromJson(const std::string& serializedNameHierarchy); } diff --git a/core/include/SourcetrailDBWriter.h b/core/include/SourcetrailDBWriter.h index 7db0b27..c18e48e 100644 --- a/core/include/SourcetrailDBWriter.h +++ b/core/include/SourcetrailDBWriter.h @@ -87,7 +87,6 @@ namespace sourcetrail bool recordError(const std::string& message, bool fatal, const SourceRange& location); private: - static std::string serializeNameHierarchy(const NameHierarchy& nameHierarchy); void openDatabase(); void closeDatabase(); void setupDatabaseTables(); diff --git a/core/src/NameHierarchy.cpp b/core/src/NameHierarchy.cpp index 3fa96b2..9f15366 100644 --- a/core/src/NameHierarchy.cpp +++ b/core/src/NameHierarchy.cpp @@ -20,6 +20,26 @@ namespace sourcetrail { + std::string serializeNameHierarchyToDatabaseString(const NameHierarchy& nameHierarchy) + { + static std::string META_DELIMITER = "\tm"; + static std::string NAME_DELIMITER = "\tn"; + static std::string PARTS_DELIMITER = "\ts"; + static std::string SIGNATURE_DELIMITER = "\tp"; + + std::string serialized = nameHierarchy.nameDelimiter + META_DELIMITER; + for (size_t i = 0; i < nameHierarchy.nameElements.size(); i++) + { + if (i != 0) + { + serialized += NAME_DELIMITER; + } + const NameElement& nameElement = nameHierarchy.nameElements[i]; + serialized += nameElement.name + PARTS_DELIMITER + nameElement.prefix + SIGNATURE_DELIMITER + nameElement.postfix; + } + return serialized; + } + std::string serializeNameHierarchyToJson(const NameHierarchy& nameHierarchy) { typedef nlohmann::json json; diff --git a/core/src/SourcetrailDBWriter.cpp b/core/src/SourcetrailDBWriter.cpp index 9565c09..e237cae 100644 --- a/core/src/SourcetrailDBWriter.cpp +++ b/core/src/SourcetrailDBWriter.cpp @@ -571,26 +571,6 @@ namespace sourcetrail // --- Private Interface --- - std::string SourcetrailDBWriter::serializeNameHierarchy(const NameHierarchy& nameHierarchy) - { - static std::string META_DELIMITER = "\tm"; - static std::string NAME_DELIMITER = "\tn"; - static std::string PARTS_DELIMITER = "\ts"; - static std::string SIGNATURE_DELIMITER = "\tp"; - - std::string serialized = nameHierarchy.nameDelimiter + META_DELIMITER; - for (size_t i = 0; i < nameHierarchy.nameElements.size(); i++) - { - if (i != 0) - { - serialized += NAME_DELIMITER; - } - const NameElement& nameElement = nameHierarchy.nameElements[i]; - serialized += nameElement.name + PARTS_DELIMITER + nameElement.prefix + SIGNATURE_DELIMITER + nameElement.postfix; - } - return serialized; - } - void SourcetrailDBWriter::openDatabase() { if (m_storage) @@ -672,7 +652,10 @@ namespace sourcetrail { currentNameHierarchy.nameElements.push_back(nameHierarchy.nameElements[i]); - int nodeId = m_storage->addNode(StorageNodeData(nodeKindToInt(NODE_UNKNOWN), serializeNameHierarchy(currentNameHierarchy))); + int nodeId = m_storage->addNode(StorageNodeData( + nodeKindToInt(NODE_UNKNOWN), + serializeNameHierarchyToDatabaseString(currentNameHierarchy) + )); if (parentNodeId != 0) { diff --git a/core/test/test.cpp b/core/test/test.cpp index c2e95ae..e1e9ece 100644 --- a/core/test/test.cpp +++ b/core/test/test.cpp @@ -18,25 +18,26 @@ #include "catch/catch.hpp" -#include "SourcetrailDBWriter.h" #include "DatabaseStorage.h" +#include "NodeKind.h" +#include "SourcetrailDBWriter.h" namespace sourcetrail { - TEST_CASE("Testing SourcetrailDBWriter") + TEST_CASE("Testing SourcetrailDBWriter performs general operations") { const std::string databasePath = "testing.db"; std::shared_ptr storage = DatabaseStorage::openDatabase(databasePath); SourcetrailDBWriter writer; - REQUIRE(writer.getLastError().empty()); + REQUIRE(writer.getLastError() == ""); writer.open(databasePath); - REQUIRE(writer.getLastError().empty()); + REQUIRE(writer.getLastError() == ""); writer.clear(); - REQUIRE(writer.getLastError().empty()); + REQUIRE(writer.getLastError() == ""); SECTION("writer is compatible after clearing") { @@ -48,53 +49,480 @@ namespace sourcetrail REQUIRE(writer.isEmpty()); } - SECTION("writer can record symbol") + writer.close(); + REQUIRE(writer.getLastError() == ""); + } + + TEST_CASE("Testing SourcetrailDBWriter records nodes") + { + const std::string databasePath = "testing.db"; + + std::shared_ptr storage = DatabaseStorage::openDatabase(databasePath); + + SourcetrailDBWriter writer; + REQUIRE(writer.getLastError() == ""); + + writer.open(databasePath); + REQUIRE(writer.getLastError() == ""); + + writer.clear(); + REQUIRE(writer.getLastError() == ""); + + const NameHierarchy nameSymbol1({ "." ,{ { "void", "foo", "()" } } }); + const int idSymbol1 = writer.recordSymbol(nameSymbol1); + REQUIRE(idSymbol1 != 0); + REQUIRE(writer.getLastError() == ""); + + SECTION("database contains node after recording simple symbol") { - NameHierarchy nameSymbol1({ "." , {{ "void", "foo", "()" } } }); - int idSymbol1 = writer.recordSymbol(nameSymbol1); - REQUIRE(idSymbol1 != 0); - REQUIRE(writer.getLastError().empty()); + const std::vector nodes = storage->getAll(); + REQUIRE(nodes.size() == 1); + REQUIRE(nodes.front().id == idSymbol1); + REQUIRE(nodes.front().serializedName == serializeNameHierarchyToDatabaseString(nameSymbol1)); + REQUIRE(nodes.front().nodeKind == NODE_UNKNOWN); + } - SECTION("database contains node after recording plane symbol") + SECTION("writer does not record node for symbol twice") + { + const int secondIdSymbol1 = writer.recordSymbol(nameSymbol1); + REQUIRE(secondIdSymbol1 == idSymbol1); + REQUIRE(writer.getLastError() == ""); + + const std::vector nodes = storage->getAll(); + REQUIRE(nodes.size() == 1); + } + + SECTION("writer records \"explicit\" symbol definition kind") + { + const bool success = writer.recordSymbolDefinitionKind(idSymbol1, DEFINITION_EXPLICIT); + REQUIRE(success); + REQUIRE(writer.getLastError() == ""); + + const std::vector symbols = storage->getAll(); + REQUIRE(symbols.size() == 1); + REQUIRE(symbols.front().definitionKind == definitionKindToInt(DEFINITION_EXPLICIT)); + } + + SECTION("writer records \"class\" symbol kind") + { + const bool success = writer.recordSymbolKind(idSymbol1, SYMBOL_CLASS); + REQUIRE(success); + REQUIRE(writer.getLastError() == ""); + + const std::vector nodes = storage->getAll(); + REQUIRE(nodes.size() == 1); + REQUIRE(nodes.front().nodeKind == symbolKindToNodeKind(SYMBOL_CLASS)); + } + + SECTION("writer records symbol locations") + { + const std::string filePath = "path/to/non_existing_file.cpp"; + const int startLine = 1; + const int startCol = 2; + const int endLine = 3; + const int endCol = 4; + + std::vector sourceLocations; + + SECTION("writer records symbol token location") { - std::vector nodes = storage->getAll(); - REQUIRE(nodes.size() == 1); - } - - SECTION("writer does not record symbol twice") - { - int secondIdSymbol1 = writer.recordSymbol(nameSymbol1); - REQUIRE(secondIdSymbol1 == idSymbol1); - REQUIRE(writer.getLastError().empty()); - - std::vector nodes = storage->getAll(); - REQUIRE(nodes.size() == 1); - } - - SECTION("writer records \"explicit\" symbol definition kind") - { - bool success = writer.recordSymbolDefinitionKind(idSymbol1, DEFINITION_EXPLICIT); + const bool success = writer.recordSymbolLocation( + idSymbol1, + SourceRange({ filePath, startLine, startCol, endLine, endCol }) + ); REQUIRE(success); - REQUIRE(writer.getLastError().empty()); + REQUIRE(writer.getLastError() == ""); - std::vector symbols = storage->getAll(); - REQUIRE(symbols.size() == 1); - REQUIRE(symbols.front().definitionKind == definitionKindToInt(DEFINITION_EXPLICIT)); + sourceLocations = storage->getAll(); + REQUIRE(sourceLocations.size() == 1); + REQUIRE(sourceLocations.front().locationKind == locationKindToInt(LOCATION_TOKEN)); } - SECTION("writer records \"class\" symbol kind") + SECTION("writer records symbol scope location") { - bool success = writer.recordSymbolKind(idSymbol1, SYMBOL_CLASS); + const bool success = writer.recordSymbolScopeLocation( + idSymbol1, + SourceRange({ filePath, startLine, startCol, endLine, endCol }) + ); REQUIRE(success); - REQUIRE(writer.getLastError().empty()); + REQUIRE(writer.getLastError() == ""); - std::vector nodes = storage->getAll(); - REQUIRE(nodes.size() == 1); - REQUIRE(nodes.front().nodeKind == symbolKindToNodeKind(SYMBOL_CLASS)); + sourceLocations = storage->getAll(); + REQUIRE(sourceLocations.size() == 1); + REQUIRE(sourceLocations.front().locationKind == locationKindToInt(LOCATION_SCOPE)); } + + SECTION("writer records symbol signature location") + { + const bool success = writer.recordSymbolSignatureLocation( + idSymbol1, + SourceRange({ filePath, startLine, startCol, endLine, endCol }) + ); + REQUIRE(success); + REQUIRE(writer.getLastError() == ""); + + sourceLocations = storage->getAll(); + REQUIRE(sourceLocations.size() == 1); + REQUIRE(sourceLocations.front().locationKind == locationKindToInt(LOCATION_SIGNATURE)); + } + + REQUIRE(sourceLocations.front().startLineNumber == startLine); + REQUIRE(sourceLocations.front().startColumnNumber == startCol); + REQUIRE(sourceLocations.front().endLineNumber == endLine); + REQUIRE(sourceLocations.front().endColumnNumber == endCol); + + const std::vector occurrences = storage->getAll(); + REQUIRE(occurrences.size() == 1); + REQUIRE(occurrences.front().elementId == idSymbol1); + REQUIRE(occurrences.front().sourceLocationId == sourceLocations.front().id); + + const std::vector files = storage->getAll(); + REQUIRE(files.size() == 1); + REQUIRE(files.front().filePath == filePath); + REQUIRE(sourceLocations.front().fileNodeId == files.front().id); } writer.close(); - REQUIRE(writer.getLastError().empty()); + REQUIRE(writer.getLastError() == ""); + } + + TEST_CASE("Testing SourcetrailDBWriter records edges") + { + const std::string databasePath = "testing.db"; + + std::shared_ptr storage = DatabaseStorage::openDatabase(databasePath); + + SourcetrailDBWriter writer; + REQUIRE(writer.getLastError() == ""); + + writer.open(databasePath); + REQUIRE(writer.getLastError() == ""); + + writer.clear(); + REQUIRE(writer.getLastError() == ""); + + const NameHierarchy nameSymbol1({ "." ,{ { "void", "foo", "()" } } }); + const int idSymbol1 = writer.recordSymbol(nameSymbol1); + REQUIRE(idSymbol1 != 0); + REQUIRE(writer.getLastError() == ""); + + const NameHierarchy nameSymbol2({ "." ,{ { "void", "bar", "()" } } }); + const int idSymbol2 = writer.recordSymbol(nameSymbol2); + REQUIRE(idSymbol2 != 0); + REQUIRE(writer.getLastError() == ""); + + const ReferenceKind kindReference1 = REFERENCE_CALL; + const int idReference1 = writer.recordReference(idSymbol1, idSymbol2, kindReference1); + REQUIRE(idReference1 != 0); + REQUIRE(writer.getLastError() == ""); + + SECTION("database contains edge after recording simple reference") + { + const std::vector edges = storage->getAll(); + REQUIRE(edges.size() == 1); + REQUIRE(edges.front().id == idReference1); + REQUIRE(edges.front().sourceNodeId == idSymbol1); + REQUIRE(edges.front().targetNodeId == idSymbol2); + REQUIRE(edges.front().edgeKind == edgeKindToInt(referenceKindToEdgeKind(kindReference1))); + } + + SECTION("writer does not record edge for reference twice") + { + const int secondIdReference1 = writer.recordReference(idSymbol1, idSymbol2, REFERENCE_CALL); + REQUIRE(secondIdReference1 == idReference1); + REQUIRE(writer.getLastError() == ""); + + const std::vector edges = storage->getAll(); + REQUIRE(edges.size() == 1); + } + + SECTION("writer records reference location") + { + const std::string filePath = "path/to/non_existing_file.cpp"; + const int startLine = 1; + const int startCol = 2; + const int endLine = 3; + const int endCol = 4; + + const bool success = writer.recordReferenceLocation( + idReference1, + SourceRange({ filePath, startLine, startCol, endLine, endCol }) + ); + REQUIRE(success); + REQUIRE(writer.getLastError() == ""); + + const std::vector sourceLocations = storage->getAll(); + REQUIRE(sourceLocations.size() == 1); + REQUIRE(sourceLocations.front().locationKind == locationKindToInt(LOCATION_TOKEN)); + REQUIRE(sourceLocations.front().startLineNumber == startLine); + REQUIRE(sourceLocations.front().startColumnNumber == startCol); + REQUIRE(sourceLocations.front().endLineNumber == endLine); + REQUIRE(sourceLocations.front().endColumnNumber == endCol); + + const std::vector occurrences = storage->getAll(); + REQUIRE(occurrences.size() == 1); + REQUIRE(occurrences.front().elementId == idReference1); + REQUIRE(occurrences.front().sourceLocationId == sourceLocations.front().id); + + const std::vector files = storage->getAll(); + REQUIRE(files.size() == 1); + REQUIRE(files.front().filePath == filePath); + REQUIRE(sourceLocations.front().fileNodeId == files.front().id); + } + + writer.close(); + REQUIRE(writer.getLastError() == ""); + } + + TEST_CASE("Testing SourcetrailDBWriter records file") + { + const std::string databasePath = "testing.db"; + + std::shared_ptr storage = DatabaseStorage::openDatabase(databasePath); + + SourcetrailDBWriter writer; + REQUIRE(writer.getLastError() == ""); + + writer.open(databasePath); + REQUIRE(writer.getLastError() == ""); + + writer.clear(); + REQUIRE(writer.getLastError() == ""); + + const std::string filePath = "path/to/non_existing_file.cpp"; + const int idFile1 = writer.recordFile(filePath); + REQUIRE(idFile1 != 0); + REQUIRE(writer.getLastError() == ""); + + SECTION("database contains file after recording file") + { + const std::vector files = storage->getAll(); + REQUIRE(files.size() == 1); + REQUIRE(files.front().id == idFile1); + REQUIRE(files.front().filePath == filePath); + } + + SECTION("writer does not record file twice") + { + const int secondIdFile1 = writer.recordFile(filePath); + REQUIRE(secondIdFile1 == idFile1); + REQUIRE(writer.getLastError() == ""); + + const std::vector files = storage->getAll(); + REQUIRE(files.size() == 1); + } + + writer.close(); + REQUIRE(writer.getLastError() == ""); + } + + TEST_CASE("Testing SourcetrailDBWriter records local symbols") + { + const std::string databasePath = "testing.db"; + + std::shared_ptr storage = DatabaseStorage::openDatabase(databasePath); + + SourcetrailDBWriter writer; + REQUIRE(writer.getLastError() == ""); + + writer.open(databasePath); + REQUIRE(writer.getLastError() == ""); + + writer.clear(); + REQUIRE(writer.getLastError() == ""); + + const std::string localSymbolName1 = "foo"; + const int idLocalSymbol1 = writer.recordLocalSymbol(localSymbolName1); + REQUIRE(idLocalSymbol1 != 0); + REQUIRE(writer.getLastError() == ""); + + SECTION("database contains local symbol after recording local symbol") + { + const std::vector localSymbols = storage->getAll(); + REQUIRE(localSymbols.size() == 1); + REQUIRE(localSymbols.front().id == idLocalSymbol1); + REQUIRE(localSymbols.front().name == localSymbolName1); + } + + SECTION("writer does not record local symbol twice") + { + const int secondIdLocalSymbol1 = writer.recordLocalSymbol(localSymbolName1); + REQUIRE(secondIdLocalSymbol1 == idLocalSymbol1); + REQUIRE(writer.getLastError() == ""); + + const std::vector localSymbols = storage->getAll(); + REQUIRE(localSymbols.size() == 1); + } + + SECTION("writer records local symbol location") + { + const std::string filePath = "path/to/non_existing_file.cpp"; + const int startLine = 1; + const int startCol = 2; + const int endLine = 3; + const int endCol = 4; + + const bool success = writer.recordLocalSymbolLocation( + idLocalSymbol1, + SourceRange({ filePath, startLine, startCol, endLine, endCol }) + ); + REQUIRE(success); + REQUIRE(writer.getLastError() == ""); + + const std::vector sourceLocations = storage->getAll(); + REQUIRE(sourceLocations.size() == 1); + REQUIRE(sourceLocations.front().locationKind == locationKindToInt(LOCATION_LOCAL_SYMBOL)); + REQUIRE(sourceLocations.front().startLineNumber == startLine); + REQUIRE(sourceLocations.front().startColumnNumber == startCol); + REQUIRE(sourceLocations.front().endLineNumber == endLine); + REQUIRE(sourceLocations.front().endColumnNumber == endCol); + + const std::vector occurrences = storage->getAll(); + REQUIRE(occurrences.size() == 1); + REQUIRE(occurrences.front().elementId == idLocalSymbol1); + REQUIRE(occurrences.front().sourceLocationId == sourceLocations.front().id); + + const std::vector files = storage->getAll(); + REQUIRE(files.size() == 1); + REQUIRE(files.front().filePath == filePath); + REQUIRE(sourceLocations.front().fileNodeId == files.front().id); + } + + writer.close(); + REQUIRE(writer.getLastError() == ""); + } + + TEST_CASE("Testing SourcetrailDBWriter records comment locations") + { + const std::string databasePath = "testing.db"; + + std::shared_ptr storage = DatabaseStorage::openDatabase(databasePath); + + SourcetrailDBWriter writer; + REQUIRE(writer.getLastError() == ""); + + writer.open(databasePath); + REQUIRE(writer.getLastError() == ""); + + writer.clear(); + REQUIRE(writer.getLastError() == ""); + + const std::string filePath = "path/to/non_existing_file.cpp"; + const int startLine = 1; + const int startCol = 2; + const int endLine = 3; + const int endCol = 4; + + const bool success1 = writer.recordCommentLocation( + SourceRange({ filePath, startLine, startCol, endLine, endCol }) + ); + REQUIRE(success1); + REQUIRE(writer.getLastError() == ""); + + SECTION("database contains comment location after recording local symbol") + { + const std::vector sourceLocations = storage->getAll(); + REQUIRE(sourceLocations.size() == 1); + REQUIRE(sourceLocations.front().locationKind == locationKindToInt(LOCATION_COMMENT)); + REQUIRE(sourceLocations.front().startLineNumber == startLine); + REQUIRE(sourceLocations.front().startColumnNumber == startCol); + REQUIRE(sourceLocations.front().endLineNumber == endLine); + REQUIRE(sourceLocations.front().endColumnNumber == endCol); + + const std::vector files = storage->getAll(); + REQUIRE(files.size() == 1); + REQUIRE(files.front().filePath == filePath); + REQUIRE(sourceLocations.front().fileNodeId == files.front().id); + } + + SECTION("writer does not record comment location twice") + { + const bool success2 = writer.recordCommentLocation( + SourceRange({ filePath, startLine, startCol, endLine, endCol }) + ); + REQUIRE(success2); + REQUIRE(writer.getLastError() == ""); + + const std::vector sourceLocations = storage->getAll(); + REQUIRE(sourceLocations.size() == 1); + } + + writer.close(); + REQUIRE(writer.getLastError() == ""); + } + + TEST_CASE("Testing SourcetrailDBWriter records errors") + { + const std::string databasePath = "testing.db"; + + std::shared_ptr storage = DatabaseStorage::openDatabase(databasePath); + + SourcetrailDBWriter writer; + REQUIRE(writer.getLastError() == ""); + + writer.open(databasePath); + REQUIRE(writer.getLastError() == ""); + + writer.clear(); + REQUIRE(writer.getLastError() == ""); + + const std::string message = "This is a very serious test error message."; + const bool fatal = false; + const std::string filePath = "path/to/non_existing_file.cpp"; + const int startLine = 1; + const int startCol = 2; + const int endLine = 3; + const int endCol = 4; + + const bool success1 = writer.recordError( + message, + fatal, + SourceRange({ filePath, startLine, startCol, endLine, endCol }) + ); + REQUIRE(success1); + REQUIRE(writer.getLastError() == ""); + + SECTION("database contains error after recording error") + { + const std::vector errors = storage->getAll(); + REQUIRE(errors.size() == 1); + REQUIRE(errors.front().message == message); + REQUIRE(errors.front().fatal == fatal); + + const std::vector sourceLocations = storage->getAll(); + REQUIRE(sourceLocations.size() == 1); + REQUIRE(sourceLocations.front().locationKind == locationKindToInt(LOCATION_ERROR)); + REQUIRE(sourceLocations.front().startLineNumber == startLine); + REQUIRE(sourceLocations.front().startColumnNumber == startCol); + REQUIRE(sourceLocations.front().endLineNumber == endLine); + REQUIRE(sourceLocations.front().endColumnNumber == endCol); + + const std::vector occurrences = storage->getAll(); + REQUIRE(occurrences.size() == 1); + REQUIRE(occurrences.front().elementId == errors.front().id); + REQUIRE(occurrences.front().sourceLocationId == sourceLocations.front().id); + + const std::vector files = storage->getAll(); + REQUIRE(files.size() == 1); + REQUIRE(files.front().filePath == filePath); + REQUIRE(sourceLocations.front().fileNodeId == files.front().id); + } + + SECTION("writer does not record error twice") + { + const bool success2 = writer.recordError( + message, + fatal, + SourceRange({ filePath, startLine, startCol, endLine, endCol }) + ); + REQUIRE(success2); + REQUIRE(writer.getLastError() == ""); + + const std::vector errors = storage->getAll(); + REQUIRE(errors.size() == 1); + } + + writer.close(); + REQUIRE(writer.getLastError() == ""); } }