refine tests for SourcetrailDBWriter interface

This commit is contained in:
mlangkabel
2018-12-11 12:36:26 +01:00
parent 4d5b97b7e6
commit 722772bd9a
5 changed files with 495 additions and 61 deletions
+4
View File
@@ -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<NameElement> nameElements;
};
std::string serializeNameHierarchyToDatabaseString(const NameHierarchy& nameHierarchy);
std::string serializeNameHierarchyToJson(const NameHierarchy& nameHierarchy);
NameHierarchy deserializeNameHierarchyFromJson(const std::string& serializedNameHierarchy);
}
-1
View File
@@ -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();
+20
View File
@@ -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;
+4 -21
View File
@@ -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)
{
+467 -39
View File
@@ -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<DatabaseStorage> 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<DatabaseStorage> 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<StorageNode> nodes = storage->getAll<StorageNode>();
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<StorageNode> nodes = storage->getAll<StorageNode>();
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<StorageSymbol> symbols = storage->getAll<StorageSymbol>();
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<StorageNode> nodes = storage->getAll<StorageNode>();
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<StorageSourceLocation> sourceLocations;
SECTION("writer records symbol token location")
{
std::vector<StorageNode> nodes = storage->getAll<StorageNode>();
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<StorageNode> nodes = storage->getAll<StorageNode>();
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<StorageSymbol> symbols = storage->getAll<StorageSymbol>();
REQUIRE(symbols.size() == 1);
REQUIRE(symbols.front().definitionKind == definitionKindToInt(DEFINITION_EXPLICIT));
sourceLocations = storage->getAll<StorageSourceLocation>();
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<StorageNode> nodes = storage->getAll<StorageNode>();
REQUIRE(nodes.size() == 1);
REQUIRE(nodes.front().nodeKind == symbolKindToNodeKind(SYMBOL_CLASS));
sourceLocations = storage->getAll<StorageSourceLocation>();
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<StorageSourceLocation>();
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<StorageOccurrence> occurrences = storage->getAll<StorageOccurrence>();
REQUIRE(occurrences.size() == 1);
REQUIRE(occurrences.front().elementId == idSymbol1);
REQUIRE(occurrences.front().sourceLocationId == sourceLocations.front().id);
const std::vector<StorageFile> files = storage->getAll<StorageFile>();
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<DatabaseStorage> 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<StorageEdge> edges = storage->getAll<StorageEdge>();
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<StorageEdge> edges = storage->getAll<StorageEdge>();
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<StorageSourceLocation> sourceLocations = storage->getAll<StorageSourceLocation>();
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<StorageOccurrence> occurrences = storage->getAll<StorageOccurrence>();
REQUIRE(occurrences.size() == 1);
REQUIRE(occurrences.front().elementId == idReference1);
REQUIRE(occurrences.front().sourceLocationId == sourceLocations.front().id);
const std::vector<StorageFile> files = storage->getAll<StorageFile>();
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<DatabaseStorage> 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<StorageFile> files = storage->getAll<StorageFile>();
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<StorageFile> files = storage->getAll<StorageFile>();
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<DatabaseStorage> 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<StorageLocalSymbol> localSymbols = storage->getAll<StorageLocalSymbol>();
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<StorageLocalSymbol> localSymbols = storage->getAll<StorageLocalSymbol>();
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<StorageSourceLocation> sourceLocations = storage->getAll<StorageSourceLocation>();
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<StorageOccurrence> occurrences = storage->getAll<StorageOccurrence>();
REQUIRE(occurrences.size() == 1);
REQUIRE(occurrences.front().elementId == idLocalSymbol1);
REQUIRE(occurrences.front().sourceLocationId == sourceLocations.front().id);
const std::vector<StorageFile> files = storage->getAll<StorageFile>();
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<DatabaseStorage> 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<StorageSourceLocation> sourceLocations = storage->getAll<StorageSourceLocation>();
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<StorageFile> files = storage->getAll<StorageFile>();
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<StorageSourceLocation> sourceLocations = storage->getAll<StorageSourceLocation>();
REQUIRE(sourceLocations.size() == 1);
}
writer.close();
REQUIRE(writer.getLastError() == "");
}
TEST_CASE("Testing SourcetrailDBWriter records errors")
{
const std::string databasePath = "testing.db";
std::shared_ptr<DatabaseStorage> 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<StorageError> errors = storage->getAll<StorageError>();
REQUIRE(errors.size() == 1);
REQUIRE(errors.front().message == message);
REQUIRE(errors.front().fatal == fatal);
const std::vector<StorageSourceLocation> sourceLocations = storage->getAll<StorageSourceLocation>();
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<StorageOccurrence> occurrences = storage->getAll<StorageOccurrence>();
REQUIRE(occurrences.size() == 1);
REQUIRE(occurrences.front().elementId == errors.front().id);
REQUIRE(occurrences.front().sourceLocationId == sourceLocations.front().id);
const std::vector<StorageFile> files = storage->getAll<StorageFile>();
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<StorageError> errors = storage->getAll<StorageError>();
REQUIRE(errors.size() == 1);
}
writer.close();
REQUIRE(writer.getLastError() == "");
}
}