From 3f62168ed0b088eec3cb2903f03966f7d501f564 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Thu, 11 Dec 2014 15:26:50 +0100 Subject: moved to CharReader everywhere --- test/core/BufferedCharReaderTest.cpp | 185 -------- test/core/CodeTokenizerTest.cpp | 26 +- test/core/LoggerTest.cpp | 74 --- test/core/RegistryTest.cpp | 2 +- test/core/TokenizerTest.cpp | 14 +- test/core/UtilsTest.cpp | 43 -- test/core/common/CharReaderTest.cpp | 821 ++++++++++++++++++++++++++++++++ test/core/common/LoggerTest.cpp | 74 +++ test/core/common/UtilsTest.cpp | 43 ++ test/core/common/VariantReaderTest.cpp | 345 ++++++++++++++ test/core/common/VariantTest.cpp | 141 ++++++ test/core/utils/CharReaderTest.cpp | 823 --------------------------------- test/core/variant/ReaderTest.cpp | 345 -------------- test/core/variant/VariantTest.cpp | 141 ------ 14 files changed, 1443 insertions(+), 1634 deletions(-) delete mode 100644 test/core/BufferedCharReaderTest.cpp delete mode 100644 test/core/LoggerTest.cpp delete mode 100644 test/core/UtilsTest.cpp create mode 100644 test/core/common/CharReaderTest.cpp create mode 100644 test/core/common/LoggerTest.cpp create mode 100644 test/core/common/UtilsTest.cpp create mode 100644 test/core/common/VariantReaderTest.cpp create mode 100644 test/core/common/VariantTest.cpp delete mode 100644 test/core/utils/CharReaderTest.cpp delete mode 100644 test/core/variant/ReaderTest.cpp delete mode 100644 test/core/variant/VariantTest.cpp (limited to 'test/core') diff --git a/test/core/BufferedCharReaderTest.cpp b/test/core/BufferedCharReaderTest.cpp deleted file mode 100644 index b3498f7..0000000 --- a/test/core/BufferedCharReaderTest.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program 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. - - This program 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 this program. If not, see . -*/ - -#include -#include - -#include "gtest/gtest.h" - -#include - -namespace ousia{ - -TEST(BufferedCharReaderTest, SimpleReadTest) -{ - std::string testStr{"this is a test"}; - char c; - - // Feed a test string into the reader - BufferedCharReader reader{testStr}; - - // Try to read the test string - std::string res; - while (!reader.atEnd()) { - ASSERT_TRUE(reader.read(&c)); - res.append(&c, 1); - } - - // The two strings must equal - ASSERT_STREQ(testStr.c_str(), res.c_str()) ; - - // We must now be at line 1, column 15 - ASSERT_EQ(1, reader.getLine()); - ASSERT_EQ(testStr.size() + 1, reader.getColumn()); - - // If we call either read or peek, false is returned - ASSERT_FALSE(reader.read(&c)); - ASSERT_FALSE(reader.peek(&c)); -} - -TEST(BufferedCharReaderTest, SimplePeekTest) -{ - std::string testStr{"this is a test"}; - char c; - - // Feed a test string into the reader - BufferedCharReader reader{testStr}; - - // Try to read the test string - std::string res; - while (reader.peek(&c)) { - res.append(&c, 1); - } - - // Peeking does not trigger the "atEnd" flag - ASSERT_FALSE(reader.atEnd()); - - // The two strings must equal - ASSERT_STREQ(testStr.c_str(), res.c_str()); - - // We must now be at line 1, column 1 and NOT at the end of the stream - ASSERT_EQ(1, reader.getLine()); - ASSERT_EQ(1, reader.getColumn()); - ASSERT_FALSE(reader.atEnd()); - - // If we consume the peek, we must be at line 1, column 15 and we should be - // at the end of the stream - reader.consumePeek(); - ASSERT_EQ(1, reader.getLine()); - ASSERT_EQ(testStr.size() + 1, reader.getColumn()); - ASSERT_TRUE(reader.atEnd()); - - // If we call either read or peek, false is returned - ASSERT_FALSE(reader.read(&c)); - ASSERT_FALSE(reader.peek(&c)); -} - -TEST(BufferedCharReaderTest, SplittedPeakTest) -{ - std::string testStr{"this is a test"}; - char c; - - // Feed a test string into the reader - BufferedCharReader reader; - - // Try to peek the test string, feed char after char into the reader - std::string res; - for (unsigned int i = 0; i < testStr.length(); i++) { - reader.feed(std::string(&testStr[i], 1)); - while (reader.peek(&c)) { - res.append(&c, 1); - } - } - reader.close(); - - // Consume the peeked data - ASSERT_FALSE(reader.atEnd()); - reader.consumePeek(); - ASSERT_TRUE(reader.atEnd()); - - // The two strings must equal - ASSERT_STREQ(testStr.c_str(), res.c_str()) ; - - // We must now be at line 1, column 15 - ASSERT_EQ(1, reader.getLine()); - ASSERT_EQ(testStr.size() + 1, reader.getColumn()); - - // If we call either read or peek, false is returned - ASSERT_FALSE(reader.read(&c)); - ASSERT_FALSE(reader.peek(&c)); -} - -TEST(BufferedCharReaderTest, RowColumnCounterTest) -{ - // Feed a test string into the reader - BufferedCharReader reader{"1\n\r2\n3\r\n\n4"}; - - // We should currently be in line 1, column 1 - ASSERT_EQ(1, reader.getLine()); - ASSERT_EQ(1, reader.getColumn()); - - // Read two characters - char c; - for (int i = 0; i < 2; i++) reader.read(&c); - ASSERT_EQ(2, reader.getLine()); - ASSERT_EQ(1, reader.getColumn()); - - // Read two characters - for (int i = 0; i < 2; i++) reader.read(&c); - ASSERT_EQ(3, reader.getLine()); - ASSERT_EQ(1, reader.getColumn()); - - // Read three characters - for (int i = 0; i < 3; i++) reader.read(&c); - ASSERT_EQ(5, reader.getLine()); - ASSERT_EQ(1, reader.getColumn()); -} - -TEST(BufferedCharReaderTest, LinebreakSubstitutionTest) -{ - // Feed a test string into the reader - BufferedCharReader reader{"this\n\ris\n\rjust\na test\r\n\rtest\n\r"}; - - // Read all characters from the test string - std::string res; - char c; - while (reader.read(&c)) { - res.append(&c, 1); - } - - // Test for equality - ASSERT_STREQ("this\nis\njust\na test\n\ntest\n", res.c_str()); -} - -TEST(BufferedCharReaderTest, RowColumnCounterUTF8Test) -{ - // Feed a test string with some umlauts into the reader - BufferedCharReader reader{"\x61\xc3\x96\xc3\x84\xc3\x9c\xc3\x9f"}; - - // Read all bytes - char c; - while (reader.read(&c)); - - // The sequence above equals 5 UTF-8 characters (so after reading all the - // cursor is at position 6) - ASSERT_EQ(1, reader.getLine()); - ASSERT_EQ(6, reader.getColumn()); -} - -} - diff --git a/test/core/CodeTokenizerTest.cpp b/test/core/CodeTokenizerTest.cpp index 1432564..4d11622 100644 --- a/test/core/CodeTokenizerTest.cpp +++ b/test/core/CodeTokenizerTest.cpp @@ -32,15 +32,15 @@ static const int CURLY_CLOSE = 41; TEST(CodeTokenizer, testTokenizer) { - BufferedCharReader reader; - reader.feed("/**\n"); // 1 - reader.feed(" * Some Block Comment\n"); // 2 - reader.feed(" */\n"); // 3 - reader.feed("var my_string = 'My \\'String\\'';\n"); // 4 - reader.feed("// and a line comment\n"); // 5 - reader.feed("var my_obj = { a = 4;}"); // 6 - // 123456789012345678901234567890123456789 - // 0 1 2 3 + CharReader reader{ + "/**\n" // 1 + " * Some Block Comment\n" // 2 + " */\n" // 3 + "var my_string = 'My \\'String\\'';\n" // 4 + "// and a line comment\n" // 5 + "var my_obj = { a = 4;}"}; // 6 + // 123456789012345678901234567890123456789 + // 0 1 2 3 TokenTreeNode root{{{"/*", 1}, {"*/", 2}, {"//", 3}, @@ -68,10 +68,10 @@ TEST(CodeTokenizer, testTokenizer) {STRING, "My 'String'", 17, 4, 32, 4}, {TOKEN_TEXT, ";", 32, 4, 33, 4}, {LINEBREAK, "\n", 33, 4, 1, 5}, - //this is slightly counter-intuitive but makes sense if you think about - //it: As a line comment is ended by a line break the line break is - //technically still a part of the line comment and thus the ending - //is in the next line. + // this is slightly counter-intuitive but makes sense if you think about + // it: As a line comment is ended by a line break the line break is + // technically still a part of the line comment and thus the ending + // is in the next line. {LINE_COMMENT, " and a line comment", 1, 5, 1, 6}, {TOKEN_TEXT, "var", 1, 6, 4, 6}, {TOKEN_TEXT, "my_obj", 5, 6, 11, 6}, diff --git a/test/core/LoggerTest.cpp b/test/core/LoggerTest.cpp deleted file mode 100644 index abb76de..0000000 --- a/test/core/LoggerTest.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program 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. - - This program 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 this program. If not, see . -*/ - -#include - -#include - -#include - -namespace ousia { - -struct Pos { - int line, column; - Pos(int line, int column) : line(line), column(column){}; - int getLine() const { return line; } - int getColumn() const { return column; } -}; - -TEST(TerminalLogger, log) -{ - // Test for manual visual expection only -- no assertions - TerminalLogger logger{std::cerr, true}; - logger.pushFilename("test.odp"); - - logger.debug("This is a test debug message", 10, 20); - logger.debug("This is a test debug message with no column", 10); - logger.debug("This is a test debug message with no line"); - logger.debug("This is a test debug message with no file", ""); - logger.debug("This is a test debug message with no file but a line", "", - 10); - logger.debug( - "This is a test debug message with no file but a line and a column", "", - 10, 20); - logger.note("This is a test note", 10, 20); - logger.warning("This is a test warning", 10, 20); - logger.error("This is a test error", 10, 20); - logger.fatalError("This is a test fatal error!", 10, 20); - - try { - throw LoggableException{"An exception"}; - } - catch (const LoggableException &ex) { - logger.log(ex); - } - - try { - throw LoggableException{"An exception at position", Pos(10, 20)}; - } - catch (const LoggableException &ex) { - logger.log(ex); - } - - logger.logAt(Severity::ERROR, "This is a positioned log message", - Pos(10, 20)); - logger.debugAt("This is a positioned debug message", Pos(10, 20)); - logger.noteAt("This is a positioned log error", Pos(10, 20)); -} -} - diff --git a/test/core/RegistryTest.cpp b/test/core/RegistryTest.cpp index e06011a..22365f2 100644 --- a/test/core/RegistryTest.cpp +++ b/test/core/RegistryTest.cpp @@ -22,7 +22,7 @@ #include -#include +#include namespace ousia { diff --git a/test/core/TokenizerTest.cpp b/test/core/TokenizerTest.cpp index da6b578..2b80662 100644 --- a/test/core/TokenizerTest.cpp +++ b/test/core/TokenizerTest.cpp @@ -18,7 +18,7 @@ #include -#include +#include #include @@ -65,10 +65,9 @@ TEST(Tokenizer, testTokenization) { TokenTreeNode root{{{"/", 1}, {"/*", 2}, {"*/", 3}}}; - BufferedCharReader reader; - reader.feed("Test/Test /* Block Comment */"); - // 12345678901234567890123456789 - // 0 1 2 + CharReader reader{"Test/Test /* Block Comment */"}; + // 12345678901234567890123456789 + // 0 1 2 std::vector expected = { {TOKEN_TEXT, "Test", 1, 1, 5, 1}, @@ -97,10 +96,7 @@ TEST(Tokenizer, testIncompleteTokens) { TokenTreeNode root{{{"ab", 1}, {"c", 2}}}; - BufferedCharReader reader; - reader.feed("ac"); - // 1234567890 - // 0 1 + CharReader reader{"ac"}; std::vector expected = { {TOKEN_TEXT, "a", 1, 1, 2, 1}, diff --git a/test/core/UtilsTest.cpp b/test/core/UtilsTest.cpp deleted file mode 100644 index 0a7d2a3..0000000 --- a/test/core/UtilsTest.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program 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. - - This program 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 this program. If not, see . -*/ - -#include - -#include - -namespace ousia { - -TEST(Utils, isIdentifier) -{ - ASSERT_TRUE(Utils::isIdentifier("test")); - ASSERT_TRUE(Utils::isIdentifier("t0-_est")); - ASSERT_TRUE(Utils::isIdentifier("_t0-_EST")); - ASSERT_FALSE(Utils::isIdentifier("-t0-_EST")); - ASSERT_FALSE(Utils::isIdentifier("0t-_EST")); -} - -TEST(Utils, trim) -{ - ASSERT_EQ("hello world", Utils::trim("\t hello world \n\r\t")); - ASSERT_EQ("hello world", Utils::trim("hello world \n\r\t")); - ASSERT_EQ("hello world", Utils::trim(" hello world")); - ASSERT_EQ("hello world", Utils::trim("hello world")); -} - -} - diff --git a/test/core/common/CharReaderTest.cpp b/test/core/common/CharReaderTest.cpp new file mode 100644 index 0000000..06b9d45 --- /dev/null +++ b/test/core/common/CharReaderTest.cpp @@ -0,0 +1,821 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see . +*/ + +#include +#include +#include + +#include "gtest/gtest.h" + +#include + +namespace ousia { + +/* Test data */ + +// Generates some pseudo-random data +// (inspired by "Numerical Recipes, Third Edition", Chapter 7.17) +static std::vector generateData(size_t len) +{ + const uint32_t B1 = 17; + const uint32_t B2 = 15; + const uint32_t B3 = 5; + uint32_t v = 0xF3A99148; + std::vector res; + for (size_t i = 0; i < len; i++) { + while (true) { + // Advance the random seed + v = v ^ (v >> B1); + v = v ^ (v << B2); + v = v ^ (v >> B3); + + // Replace \n and \r in order to avoid line break processing by the + // CharReader + char c = v & 0xFF; + if (c != '\n' && c != '\r') { + res.push_back(c); + break; + } + } + } + return res; +} + +// For performance tests only +// static constexpr size_t DATA_LENGTH = 16 * 1024 * 1024 + 795; +static constexpr size_t DATA_LENGTH = 256 * 1024 + 795; +static const std::vector DATA = generateData(DATA_LENGTH); + +/* Buffer Test */ + +TEST(Buffer, simpleRead) +{ + std::string testStr{"this is a test"}; + + // Create buffer with the test string + char c; + Buffer buf{testStr}; + + // Create a read cursor + Buffer::CursorId cursor = buf.createCursor(); + + // We're not at the end of the stream + ASSERT_FALSE(buf.atEnd(cursor)); + + // The cursor must be at zero + ASSERT_EQ(0U, buf.offset(cursor)); + + // Try to read the test string + std::string res; + while (buf.read(cursor, c)) { + res.append(&c, 1); + } + + // The cursor must be at the end + ASSERT_TRUE(buf.atEnd(cursor)); + + // The cursor must be one byond the last byte + ASSERT_EQ(testStr.size(), buf.offset(cursor)); + + // The two strings must equal + ASSERT_EQ(testStr, res); + + buf.deleteCursor(cursor); +} + +TEST(Buffer, cursorManagement) +{ + Buffer buf{""}; + + Buffer::CursorId c1 = buf.createCursor(); + Buffer::CursorId c2 = buf.createCursor(); + Buffer::CursorId c3 = buf.createCursor(); + + ASSERT_EQ(0U, c1); + ASSERT_EQ(1U, c2); + ASSERT_EQ(2U, c3); + + buf.deleteCursor(c2); + Buffer::CursorId c4 = buf.createCursor(); + ASSERT_EQ(1U, c4); + + buf.deleteCursor(c1); + buf.deleteCursor(c3); + buf.deleteCursor(c4); +} + +TEST(Buffer, twoCursors) +{ + std::string testStr{"this is a test"}; + + // Create buffer with the test string + char c; + Buffer buf{testStr}; + + // Create two read cursors + Buffer::CursorId cur1 = buf.createCursor(); + Buffer::CursorId cur2 = buf.createCursor(); + + ASSERT_FALSE(buf.atEnd(cur1)); + ASSERT_FALSE(buf.atEnd(cur2)); + + // Try to read the test string with the first cursor + std::string res1; + while (buf.read(cur1, c)) { + res1.append(&c, 1); + } + + // The first cursor must be at the end + ASSERT_TRUE(buf.atEnd(cur1)); + ASSERT_FALSE(buf.atEnd(cur2)); + + // Try to read the test string with the second cursor + std::string res2; + while (buf.read(cur2, c)) { + res2.append(&c, 1); + } + + // The first cursor must be at the end + ASSERT_TRUE(buf.atEnd(cur1)); + ASSERT_TRUE(buf.atEnd(cur2)); + + // The two strings must equal + ASSERT_EQ(testStr, res1); + ASSERT_EQ(testStr, res2); + + buf.deleteCursor(cur1); + buf.deleteCursor(cur2); +} + +TEST(Buffer, copyCursors) +{ + std::string testStr{"test1 test2 test3"}; + + // Create buffer with the test string + char c; + Buffer buf{testStr}; + + // Create two read cursors + Buffer::CursorId cur1 = buf.createCursor(); + Buffer::CursorId cur2 = buf.createCursor(); + + ASSERT_FALSE(buf.atEnd(cur1)); + ASSERT_FALSE(buf.atEnd(cur2)); + + // Read the first six characters with cursor one + std::string res1; + for (int i = 0; i < 6; i++) { + if (buf.read(cur1, c)) { + res1.append(&c, 1); + } + } + ASSERT_EQ("test1 ", res1); + ASSERT_FALSE(buf.atEnd(cur1)); + + // Copy cur1 to cur2, free cur1 + buf.copyCursor(cur1, cur2); + buf.deleteCursor(cur1); + + std::string res2; + for (int i = 0; i < 6; i++) { + if (buf.read(cur2, c)) { + res2.append(&c, 1); + } + } + ASSERT_EQ("test2 ", res2); + ASSERT_FALSE(buf.atEnd(cur2)); + + // Create a new cursor as copy of cur2 + Buffer::CursorId cur3 = buf.createCursor(cur2); + std::string res3; + for (int i = 0; i < 6; i++) { + if (buf.read(cur3, c)) { + res3.append(&c, 1); + } + } + ASSERT_EQ("test3", res3); + + ASSERT_TRUE(buf.atEnd(cur3)); + + buf.deleteCursor(cur1); + buf.deleteCursor(cur2); + buf.deleteCursor(cur3); +} + +TEST(Buffer, moveCursor) +{ + std::string testStr{"test1 test2 test3"}; + + // Create buffer with the test string + char c; + Buffer buf{testStr}; + Buffer::CursorId cursor = buf.createCursor(); + + // Read the first six characters with cursor one + { + std::string res; + for (int i = 0; i < 6; i++) { + if (buf.read(cursor, c)) { + res.append(&c, 1); + } + } + ASSERT_EQ("test1 ", res); + } + + // Move six bytes backward + ASSERT_EQ(-6, buf.moveCursor(cursor, -6)); + { + std::string res; + for (int i = 0; i < 6; i++) { + if (buf.read(cursor, c)) { + res.append(&c, 1); + } + } + ASSERT_EQ("test1 ", res); + } + + // Move more than six bytes backward + ASSERT_EQ(-6, buf.moveCursor(cursor, -1000)); + { + std::string res; + for (int i = 0; i < 6; i++) { + if (buf.read(cursor, c)) { + res.append(&c, 1); + } + } + ASSERT_EQ("test1 ", res); + } + + // Move six bytes forward + ASSERT_EQ(6, buf.moveCursor(cursor, 6)); + { + std::string res; + for (int i = 0; i < 6; i++) { + if (buf.read(cursor, c)) { + res.append(&c, 1); + } + } + ASSERT_EQ("test3", res); + } + + buf.deleteCursor(cursor); +} + +struct VectorReadState { + size_t offs; + const std::vector &data; + + VectorReadState(const std::vector &data) : offs(0), data(data) {} +}; + +static size_t readFromVector(char *buf, size_t size, void *userData) +{ + VectorReadState &state = *(static_cast(userData)); + size_t tar = std::min(state.offs + size, state.data.size()); + for (size_t i = state.offs; i < tar; i++) { + *buf = state.data[i]; + buf++; + } + size_t res = tar - state.offs; + state.offs = tar; + return res; +} + +TEST(Buffer, simpleStream) +{ + VectorReadState state(DATA); + + Buffer buf{readFromVector, &state}; + Buffer::CursorId cursor = buf.createCursor(); + + char c; + std::vector res; + while (buf.read(cursor, c)) { + res.push_back(c); + } + + // We must be at the end of the buffer and the cursor offset must be set + // correctly + ASSERT_TRUE(buf.atEnd(cursor)); + ASSERT_EQ(DATA_LENGTH, buf.offset(cursor)); + + // The read data and the original data must be equal + ASSERT_EQ(DATA, res); + + buf.deleteCursor(cursor); +} + +TEST(Buffer, streamTwoCursors) +{ + VectorReadState state(DATA); + + Buffer buf{readFromVector, &state}; + Buffer::CursorId cur1 = buf.createCursor(); + Buffer::CursorId cur2 = buf.createCursor(); + + char c; + + std::vector res1; + while (buf.read(cur1, c)) { + res1.push_back(c); + } + + ASSERT_TRUE(buf.atEnd(cur1)); + ASSERT_FALSE(buf.atEnd(cur2)); + ASSERT_EQ(DATA_LENGTH, buf.offset(cur1)); + ASSERT_EQ(0U, buf.offset(cur2)); + + std::vector res2; + while (buf.read(cur2, c)) { + res2.push_back(c); + } + + ASSERT_TRUE(buf.atEnd(cur1)); + ASSERT_TRUE(buf.atEnd(cur2)); + ASSERT_EQ(DATA_LENGTH, buf.offset(cur1)); + ASSERT_EQ(DATA_LENGTH, buf.offset(cur2)); + + // The read data and the original data must be equal + ASSERT_EQ(DATA, res1); + ASSERT_EQ(DATA, res2); + + buf.deleteCursor(cur1); + buf.deleteCursor(cur2); +} + +TEST(Buffer, streamTwoCursorsMovingInterleaved) +{ + VectorReadState state(DATA); + + Buffer buf{readFromVector, &state}; + Buffer::CursorId cur1 = buf.createCursor(); + Buffer::CursorId cur2 = buf.createCursor(); + + char c; + + std::vector res1; + std::vector res2; + while (!buf.atEnd(cur1) || !buf.atEnd(cur2)) { + for (int i = 0; i < 100; i++) { + if (buf.read(cur1, c)) { + res1.push_back(c); + } + } + for (int i = 0; i < 120; i++) { + if (buf.read(cur2, c)) { + res2.push_back(c); + } + } + + // Move cur2 120 bytes backward and read the content again + res2.resize(res2.size() - 120); + ASSERT_EQ(-120, buf.moveCursor(cur2, -120)); + for (int i = 0; i < 120; i++) { + if (buf.read(cur2, c)) { + res2.push_back(c); + } + } + + // Move cur1 60 bytes forward and backward + buf.moveCursor(cur1, -buf.moveCursor(cur1, 60)); + + // Make sure the cursor position is correct + ASSERT_EQ(res1.size(), buf.offset(cur1)); + ASSERT_EQ(res2.size(), buf.offset(cur2)); + } + + ASSERT_EQ(DATA_LENGTH, buf.offset(cur1)); + ASSERT_EQ(DATA_LENGTH, buf.offset(cur2)); + + // The read data and the original data must be equal + ASSERT_EQ(DATA, res1); + ASSERT_EQ(DATA, res2); + + buf.deleteCursor(cur1); + buf.deleteCursor(cur2); +} + +TEST(Buffer, streamMoveForward) +{ + VectorReadState state(DATA); + + std::vector partialData; + partialData.resize(100); + std::copy(DATA.end() - partialData.size(), DATA.end(), partialData.begin()); + + Buffer buf{readFromVector, &state}; + Buffer::CursorId cursor = buf.createCursor(); + ASSERT_EQ(ssize_t(DATA_LENGTH) - 100, + buf.moveCursor(cursor, DATA_LENGTH - 100)); + + char c; + std::vector res; + while (buf.read(cursor, c)) { + res.push_back(c); + } + ASSERT_EQ(partialData, res); + + buf.deleteCursor(cursor); +} + +/* CharReader Test */ + +TEST(CharReader, simpleRead) +{ + std::string testStr{"this is a test"}; + char c; + + // Feed a test string into the reader + CharReader reader{testStr}; + + // Try to read the test string + std::string res; + while (!reader.atEnd()) { + ASSERT_TRUE(reader.read(c)); + res.append(&c, 1); + } + + // The two strings must equal + ASSERT_EQ(testStr, res); + + // We must now be at line 1, column 15 + ASSERT_EQ(1U, reader.getLine()); + ASSERT_EQ(testStr.size() + 1, reader.getColumn()); + + // If we call either read or peek, false is returned + ASSERT_FALSE(reader.read(c)); + ASSERT_FALSE(reader.peek(c)); +} + +TEST(CharReader, simplePeek) +{ + std::string testStr{"this is a test"}; + char c; + + // Feed a test string into the reader + CharReader reader{testStr}; + + // Try to read the test string + std::string res; + while (reader.peek(c)) { + res.append(&c, 1); + } + + // Peeking does not trigger the "atEnd" flag + ASSERT_FALSE(reader.atEnd()); + + // The two strings must equal + ASSERT_EQ(testStr, res); + + // We must now be at line 1, column 1 and NOT at the end of the stream + ASSERT_EQ(1U, reader.getLine()); + ASSERT_EQ(1U, reader.getColumn()); + ASSERT_FALSE(reader.atEnd()); + + // If we consume the peek, we must be at line 1, column 15 and we should be + // at the end of the stream + reader.consumePeek(); + ASSERT_EQ(1U, reader.getLine()); + ASSERT_EQ(testStr.size() + 1, reader.getColumn()); + ASSERT_TRUE(reader.atEnd()); + + // If we call either read or peek, false is returned + ASSERT_FALSE(reader.read(c)); + ASSERT_FALSE(reader.peek(c)); +} + +TEST(CharReader, rowColumnCounter) +{ + // Feed a test string into the reader + CharReader reader{"1\n\r2\n3\r\n\n4"}; + + // We should currently be in line 1, column 1 + ASSERT_EQ(1U, reader.getLine()); + ASSERT_EQ(1U, reader.getColumn()); + + // Read two characters + char c; + for (int i = 0; i < 2; i++) + reader.read(c); + ASSERT_EQ(2U, reader.getLine()); + ASSERT_EQ(1U, reader.getColumn()); + + // Read two characters + for (int i = 0; i < 2; i++) + reader.read(c); + ASSERT_EQ(3U, reader.getLine()); + ASSERT_EQ(1U, reader.getColumn()); + + // Read three characters + for (int i = 0; i < 3; i++) + reader.read(c); + ASSERT_EQ(5U, reader.getLine()); + ASSERT_EQ(1U, reader.getColumn()); +} + +TEST(CharReader, rowColumnCounterTest) +{ + // Feed a test string into the reader + CharReader reader{"1\n\r2\n3\r\n\n4", 4, 10}; + + // We should currently be in line 1, column 1 + ASSERT_EQ(4U, reader.getLine()); + ASSERT_EQ(10U, reader.getColumn()); + + // Read two characters + char c; + for (int i = 0; i < 2; i++) + reader.read(c); + ASSERT_EQ(5U, reader.getLine()); + ASSERT_EQ(1U, reader.getColumn()); + + // Read two characters + for (int i = 0; i < 2; i++) + reader.read(c); + ASSERT_EQ(6U, reader.getLine()); + ASSERT_EQ(1U, reader.getColumn()); + + // Read three characters + for (int i = 0; i < 3; i++) + reader.read(c); + ASSERT_EQ(8U, reader.getLine()); + ASSERT_EQ(1U, reader.getColumn()); +} + +TEST(CharReader, linebreakSubstitution) +{ + // Feed a test string into the reader and read all characters back + CharReader reader{"this\n\ris\n\rjust\na test\r\n\rtest\n\r"}; + std::string res; + char c; + while (reader.read(c)) { + res.append(&c, 1); + } + + // Test for equality + ASSERT_EQ("this\nis\njust\na test\n\ntest\n", res); +} + +TEST(CharReader, rowColumnCounterUTF8) +{ + // Feed a test string with some umlauts into the reader + CharReader reader{"\x61\xc3\x96\xc3\x84\xc3\x9c\xc3\x9f"}; + + // Read all bytes + char c; + while (reader.read(c)) { + // Do nothing + } + + // The sequence above equals 5 UTF-8 characters (so after reading all the + // cursor is at position 6) + ASSERT_EQ(1U, reader.getLine()); + ASSERT_EQ(6U, reader.getColumn()); +} + +TEST(CharReader, stream) +{ + // Copy the test data to a string stream + std::stringstream ss; + std::copy(DATA.begin(), DATA.end(), std::ostream_iterator(ss)); + + // Read the data back from the stream + std::vector res; + char c; + CharReader reader{ss}; + while (reader.read(c)) { + res.push_back(c); + } + ASSERT_EQ(DATA_LENGTH, res.size()); + ASSERT_EQ(DATA, res); +} + +TEST(CharReader, fork) +{ + std::string testStr{"first line\n\n\rsecond line\n\rlast line"}; + // 0123456789 0 123456789012 3456789012 + // 0 1 2 3 + + char c; + CharReader reader{testStr}; + + // Read a few characters + for (int i = 0; i < 4; i++) + reader.read(c); + + // Peek a few characters + for (int i = 4; i < 7; i++) + reader.peek(c); + + // Fork the reader + { + CharReaderFork fork = reader.fork(); + + ASSERT_EQ(1U, fork.getLine()); + ASSERT_EQ(5U, fork.getColumn()); + + fork.peek(c); + ASSERT_EQ('i', c); + + fork.read(c); + ASSERT_EQ('t', c); + + ASSERT_EQ(1U, fork.getLine()); + ASSERT_EQ(6U, fork.getColumn()); + + ASSERT_EQ(1U, reader.getLine()); + ASSERT_EQ(5U, reader.getColumn()); + + reader.read(c); + reader.read(c); + ASSERT_EQ(' ', c); + + fork.commit(); + } + ASSERT_EQ(1U, reader.getLine()); + ASSERT_EQ(6U, reader.getColumn()); +} + +TEST(CharReaderTest, context) +{ + std::string testStr{"first line\n\n\rsecond line\n\rlast line"}; + // 0123456789 0 123456789012 3456789012 + // 0 1 2 3 + + // Retrieval at beginning of stream + { + CharReader reader{testStr}; + CharReader::Context ctx = reader.getContext(80); + ASSERT_EQ("first line", ctx.line); + ASSERT_EQ(0U, ctx.relPos); + ASSERT_FALSE(ctx.truncatedStart); + ASSERT_FALSE(ctx.truncatedEnd); + } + + // Retrieval in middle of line + { + CharReader reader{testStr}; + CharReader::Context ctx = reader.getContext(80); + + char c; + for (int i = 0; i < 5; i++) + reader.read(c); + + ASSERT_EQ("first line", ctx.line); + ASSERT_EQ(0U, ctx.relPos); + ASSERT_FALSE(ctx.truncatedStart); + ASSERT_FALSE(ctx.truncatedEnd); + } + + // Retrieval in whitespace sequence + { + CharReader reader{testStr}; + + char c; + for (int i = 0; i < 11; i++) + reader.read(c); + + CharReader::Context ctx = reader.getContext(80); + ASSERT_EQ("first line", ctx.line); + ASSERT_EQ(10U, ctx.relPos); + ASSERT_FALSE(ctx.truncatedStart); + ASSERT_FALSE(ctx.truncatedEnd); + } + + // Truncation of text + { + CharReader reader{testStr}; + + char c; + for (int i = 0; i < 5; i++) + reader.read(c); + + CharReader::Context ctx = reader.getContext(3); + ASSERT_EQ("t l", ctx.line); + ASSERT_EQ(1U, ctx.relPos); + ASSERT_TRUE(ctx.truncatedStart); + ASSERT_TRUE(ctx.truncatedEnd); + } + + // Second line + { + CharReader reader{testStr}; + + char c; + for (int i = 0; i < 12; i++) + reader.read(c); + + CharReader::Context ctx = reader.getContext(80); + ASSERT_EQ("second line", ctx.line); + ASSERT_EQ(0U, ctx.relPos); + ASSERT_FALSE(ctx.truncatedStart); + ASSERT_FALSE(ctx.truncatedEnd); + } + + // End of second line + { + CharReader reader{testStr}; + + char c; + for (int i = 0; i < 23; i++) + reader.read(c); + + CharReader::Context ctx = reader.getContext(80); + ASSERT_EQ("second line", ctx.line); + ASSERT_EQ(11U, ctx.relPos); + ASSERT_FALSE(ctx.truncatedStart); + ASSERT_FALSE(ctx.truncatedEnd); + } + + // Last line + { + CharReader reader{testStr}; + + char c; + for (int i = 0; i < 24; i++) + reader.read(c); + + CharReader::Context ctx = reader.getContext(80); + ASSERT_EQ("last line", ctx.line); + ASSERT_EQ(0U, ctx.relPos); + ASSERT_FALSE(ctx.truncatedStart); + ASSERT_FALSE(ctx.truncatedEnd); + } + + // Middle of last line + { + CharReader reader{testStr}; + + char c; + for (int i = 0; i < 28; i++) + reader.read(c); + + CharReader::Context ctx = reader.getContext(80); + ASSERT_EQ("last line", ctx.line); + ASSERT_EQ(4U, ctx.relPos); + ASSERT_FALSE(ctx.truncatedStart); + ASSERT_FALSE(ctx.truncatedEnd); + } + + // Middle of last line truncated + { + CharReader reader{testStr}; + + char c; + for (int i = 0; i < 28; i++) + reader.read(c); + + CharReader::Context ctx = reader.getContext(3); + ASSERT_EQ("t l", ctx.line); + ASSERT_EQ(1U, ctx.relPos); + ASSERT_TRUE(ctx.truncatedStart); + ASSERT_TRUE(ctx.truncatedEnd); + } + + // End of stream + { + CharReader reader{testStr}; + + char c; + for (int i = 0; i < 100; i++) + reader.read(c); + + CharReader::Context ctx = reader.getContext(80); + ASSERT_EQ("last line", ctx.line); + ASSERT_EQ(9U, ctx.relPos); + ASSERT_FALSE(ctx.truncatedStart); + ASSERT_FALSE(ctx.truncatedEnd); + } + + // End of stream truncated + { + CharReader reader{testStr}; + + char c; + for (int i = 0; i < 100; i++) + reader.read(c); + + CharReader::Context ctx = reader.getContext(4); + ASSERT_EQ("line", ctx.line); + ASSERT_EQ(4U, ctx.relPos); + ASSERT_TRUE(ctx.truncatedStart); + ASSERT_FALSE(ctx.truncatedEnd); + } +} +} + diff --git a/test/core/common/LoggerTest.cpp b/test/core/common/LoggerTest.cpp new file mode 100644 index 0000000..54c67f9 --- /dev/null +++ b/test/core/common/LoggerTest.cpp @@ -0,0 +1,74 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see . +*/ + +#include + +#include + +#include + +namespace ousia { + +struct Pos { + int line, column; + Pos(int line, int column) : line(line), column(column){}; + int getLine() const { return line; } + int getColumn() const { return column; } +}; + +TEST(TerminalLogger, log) +{ + // Test for manual visual expection only -- no assertions + TerminalLogger logger{std::cerr, true}; + logger.pushFilename("test.odp"); + + logger.debug("This is a test debug message", 10, 20); + logger.debug("This is a test debug message with no column", 10); + logger.debug("This is a test debug message with no line"); + logger.debug("This is a test debug message with no file", ""); + logger.debug("This is a test debug message with no file but a line", "", + 10); + logger.debug( + "This is a test debug message with no file but a line and a column", "", + 10, 20); + logger.note("This is a test note", 10, 20); + logger.warning("This is a test warning", 10, 20); + logger.error("This is a test error", 10, 20); + logger.fatalError("This is a test fatal error!", 10, 20); + + try { + throw LoggableException{"An exception"}; + } + catch (const LoggableException &ex) { + logger.log(ex); + } + + try { + throw LoggableException{"An exception at position", Pos(10, 20)}; + } + catch (const LoggableException &ex) { + logger.log(ex); + } + + logger.logAt(Severity::ERROR, "This is a positioned log message", + Pos(10, 20)); + logger.debugAt("This is a positioned debug message", Pos(10, 20)); + logger.noteAt("This is a positioned log error", Pos(10, 20)); +} +} + diff --git a/test/core/common/UtilsTest.cpp b/test/core/common/UtilsTest.cpp new file mode 100644 index 0000000..2858038 --- /dev/null +++ b/test/core/common/UtilsTest.cpp @@ -0,0 +1,43 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see . +*/ + +#include + +#include + +namespace ousia { + +TEST(Utils, isIdentifier) +{ + ASSERT_TRUE(Utils::isIdentifier("test")); + ASSERT_TRUE(Utils::isIdentifier("t0-_est")); + ASSERT_TRUE(Utils::isIdentifier("_t0-_EST")); + ASSERT_FALSE(Utils::isIdentifier("-t0-_EST")); + ASSERT_FALSE(Utils::isIdentifier("0t-_EST")); +} + +TEST(Utils, trim) +{ + ASSERT_EQ("hello world", Utils::trim("\t hello world \n\r\t")); + ASSERT_EQ("hello world", Utils::trim("hello world \n\r\t")); + ASSERT_EQ("hello world", Utils::trim(" hello world")); + ASSERT_EQ("hello world", Utils::trim("hello world")); +} + +} + diff --git a/test/core/common/VariantReaderTest.cpp b/test/core/common/VariantReaderTest.cpp new file mode 100644 index 0000000..d9bb74e --- /dev/null +++ b/test/core/common/VariantReaderTest.cpp @@ -0,0 +1,345 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see . +*/ + +#include +#include + +#include + +namespace ousia { +namespace variant { + +//static TerminalLogger logger{std::cerr, true}; +static Logger logger; + +TEST(Reader, readString) +{ + // Simple, double quoted string + { + CharReader reader("\"hello world\""); + auto res = VariantReader::parseString(reader, logger); + ASSERT_TRUE(res.first); + ASSERT_EQ("hello world", res.second); + } + + // Simple, double quoted string with whitespace + { + CharReader reader(" \"hello world\" "); + auto res = VariantReader::parseString(reader, logger); + ASSERT_TRUE(res.first); + ASSERT_EQ("hello world", res.second); + } + + // Simple, single quoted string + { + CharReader reader("'hello world'"); + auto res = VariantReader::parseString(reader, logger); + ASSERT_TRUE(res.first); + ASSERT_EQ("hello world", res.second); + } + + // Escape characters + { + CharReader reader("'\\'\\\"\\b\\f\\n\\r\\t\\v'"); + auto res = VariantReader::parseString(reader, logger); + ASSERT_TRUE(res.first); + ASSERT_EQ("'\"\b\f\n\r\t\v", res.second); + } +} + +TEST(Reader, parseUnescapedString) +{ + // Simple case + { + CharReader reader("hello world;"); + auto res = VariantReader::parseUnescapedString(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_EQ("hello world", res.second); + } + + // Simple case with whitespace + { + CharReader reader(" hello world ; "); + auto res = VariantReader::parseUnescapedString(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_EQ("hello world", res.second); + } + + // Linebreaks + { + CharReader reader(" hello\nworld ; "); + auto res = VariantReader::parseUnescapedString(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_EQ("hello\nworld", res.second); + } + + // End of stream + { + CharReader reader(" hello world "); + auto res = VariantReader::parseUnescapedString(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_EQ("hello world", res.second); + } +} + +static const std::unordered_set noDelim; + +TEST(Reader, parseInteger) +{ + // Valid integers + { + CharReader reader("0 "); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(0, res.second); + } + + { + CharReader reader("42 "); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(42, res.second); + } + + { + CharReader reader("-42"); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(-42, res.second); + } + + { + CharReader reader(" -0x4A2 "); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(-0x4A2, res.second); + } + + { + CharReader reader(" 0Xaffe"); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(0xAFFE, res.second); + } + + { + CharReader reader("0x7FFFFFFFFFFFFFFF"); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(0x7FFFFFFFFFFFFFFFL, res.second); + } + + { + CharReader reader("-0x7FFFFFFFFFFFFFFF"); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(-0x7FFFFFFFFFFFFFFFL, res.second); + } + + // Invalid integers + { + CharReader reader("-"); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_FALSE(res.first); + } + + { + CharReader reader("0a"); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_FALSE(res.first); + } + + { + CharReader reader("-0xag"); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_FALSE(res.first); + } + + { + CharReader reader("0x8000000000000000"); + auto res = VariantReader::parseInteger(reader, logger, noDelim); + ASSERT_FALSE(res.first); + } +} + +TEST(Reader, parseDouble) +{ + // Valid doubles + { + CharReader reader("1.25"); + auto res = VariantReader::parseDouble(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(1.25, res.second); + } + + { + CharReader reader(".25"); + auto res = VariantReader::parseDouble(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(.25, res.second); + } + + { + CharReader reader(".25e1"); + auto res = VariantReader::parseDouble(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(2.5, res.second); + } + + { + CharReader reader("-2.5e-1"); + auto res = VariantReader::parseDouble(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(-0.25, res.second); + } + + { + CharReader reader("-50e-2"); + auto res = VariantReader::parseDouble(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(-0.5, res.second); + } + + { + CharReader reader("-1."); + auto res = VariantReader::parseDouble(reader, logger, noDelim); + ASSERT_TRUE(res.first); + ASSERT_EQ(-1., res.second); + } + + { + CharReader reader("-50.e-2"); + auto res = VariantReader::parseDouble(reader, logger, {'.'}); + ASSERT_TRUE(res.first); + ASSERT_EQ(-50, res.second); + } + + // Invalid doubles + { + CharReader reader(".e1"); + auto res = VariantReader::parseDouble(reader, logger, noDelim); + ASSERT_FALSE(res.first); + } + + { + CharReader reader("0e100000"); + auto res = VariantReader::parseDouble(reader, logger, noDelim); + ASSERT_FALSE(res.first); + } +} + +TEST(Reader, parseArray) +{ + // Simple case (only primitive data types) + { + CharReader reader("[\"Hello, World\", unescaped\n string ,\n" + "1234, 0.56, true, false, null]"); + auto res = VariantReader::parseArray(reader, logger); + ASSERT_TRUE(res.first); + + // Make sure array has the correct size + ASSERT_EQ(7U, res.second.size()); + + // Check the types + ASSERT_TRUE(res.second[0].isString()); + ASSERT_TRUE(res.second[1].isString()); + ASSERT_TRUE(res.second[2].isInt()); + ASSERT_TRUE(res.second[3].isDouble()); + ASSERT_TRUE(res.second[4].isBool()); + ASSERT_TRUE(res.second[5].isBool()); + ASSERT_TRUE(res.second[6].isNull()); + + // Check the values + ASSERT_EQ("Hello, World", res.second[0].asString()); + ASSERT_EQ("unescaped\n string", res.second[1].asString()); + ASSERT_EQ(1234, res.second[2].asInt()); + ASSERT_EQ(0.56, res.second[3].asDouble()); + ASSERT_TRUE(res.second[4].asBool()); + ASSERT_FALSE(res.second[5].asBool()); + } + + // Ending with comma + { + CharReader reader("[ 'test' ,]"); + auto res = VariantReader::parseArray(reader, logger); + ASSERT_TRUE(res.first); + + // Make sure the array has the correct size + ASSERT_EQ(1U, res.second.size()); + + // Check the types + ASSERT_TRUE(res.second[0].isString()); + + // Check the values + ASSERT_EQ("test", res.second[0].asString()); + } + + // Recovery from invalid values + { + CharReader reader("[ 0invalidNumber, str, 1invalid]"); + auto res = VariantReader::parseArray(reader, logger); + ASSERT_TRUE(res.first); + + // Make sure the array has the correct size + ASSERT_EQ(3U, res.second.size()); + + // Check the types (all must be strings since the numbers are invalid) + ASSERT_TRUE(res.second[0].isString()); + ASSERT_TRUE(res.second[1].isString()); + ASSERT_TRUE(res.second[2].isString()); + + // Check the values + ASSERT_EQ("0invalidNumber", res.second[0].asString()); + ASSERT_EQ("str", res.second[1].asString()); + ASSERT_EQ("1invalid", res.second[2].asString()); + } +} + +TEST(Reader, parseGeneric) +{ + // Simple case, unescaped string + { + CharReader reader("hello world"); + auto res = VariantReader::parseGeneric(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_TRUE(res.second.isString()); + ASSERT_EQ("hello world", res.second.asString()); + } + + // Simple case, double quoted string + { + CharReader reader(" \"hello world\" "); + auto res = VariantReader::parseGeneric(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_TRUE(res.second.isString()); + ASSERT_EQ("hello world", res.second.asString()); + } + + // Simple case, single quoted string + { + CharReader reader(" 'hello world' "); + auto res = VariantReader::parseGeneric(reader, logger, {';'}); + ASSERT_TRUE(res.first); + ASSERT_TRUE(res.second.isString()); + ASSERT_EQ("hello world", res.second.asString()); + } +} + +} +} + diff --git a/test/core/common/VariantTest.cpp b/test/core/common/VariantTest.cpp new file mode 100644 index 0000000..580846e --- /dev/null +++ b/test/core/common/VariantTest.cpp @@ -0,0 +1,141 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program 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. + + This program 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 this program. If not, see . +*/ + +#include + +#include + +#include + +namespace ousia { + +TEST(Variant, nullValue) +{ + Variant v; + ASSERT_TRUE(v.isNull()); + + v = 1; + ASSERT_FALSE(v.isNull()); + + v = nullptr; + ASSERT_TRUE(v.isNull()); + + Variant v2{nullptr}; + ASSERT_TRUE(v.isNull()); +} + +TEST(Variant, booleanValue) +{ + Variant v{true}; + ASSERT_TRUE(v.isBool()); + ASSERT_TRUE(v.asBool()); + + v = false; + ASSERT_TRUE(v.isBool()); + ASSERT_FALSE(v.asBool()); + + v.setBool(true); + ASSERT_TRUE(v.isBool()); + ASSERT_TRUE(v.asBool()); + + v = nullptr; + ASSERT_FALSE(v.isBool()); +} + +TEST(Variant, intValue) +{ + Variant v{42}; + ASSERT_TRUE(v.isInt()); + ASSERT_EQ(42, v.asInt()); + + v = 43; + ASSERT_TRUE(v.isInt()); + ASSERT_EQ(43, v.asInt()); + + v = false; + ASSERT_FALSE(v.isInt()); +} + +TEST(Variant, doubleValue) +{ + Variant v{42.5}; + ASSERT_TRUE(v.isDouble()); + ASSERT_EQ(42.5, v.asDouble()); + + v = 42; + ASSERT_FALSE(v.isDouble()); + + v = 43.5; + ASSERT_TRUE(v.isDouble()); + ASSERT_EQ(43.5, v.asDouble()); +} + +TEST(Variant, stringValue) +{ + Variant v{"Hello World"}; + ASSERT_TRUE(v.isString()); + ASSERT_EQ("Hello World", v.asString()); + + v = "Goodbye World"; + ASSERT_TRUE(v.isString()); + ASSERT_EQ("Goodbye World", v.asString()); + + v = 42; + ASSERT_FALSE(v.isString()); +} + +TEST(Variant, arrayValue) +{ + const Variant v{{"test1", 42}}; + ASSERT_EQ(2, v.asArray().size()); + ASSERT_EQ("test1", v.asArray()[0].asString()); + ASSERT_EQ(42, v.asArray()[1].asInt()); +} + +TEST(Variant, mapValue) +{ + const Variant v{{{"key1", "entry1"}, {"key2", "entry2"}}}; + + auto map = v.asMap(); + ASSERT_EQ(2, map.size()); + + ASSERT_EQ("entry1", map.find("key1")->second.asString()); + ASSERT_EQ("entry2", map.find("key2")->second.asString()); + + const Variant v2{{{"key1", Variant::arrayType{1, 2}}, {"key2", "entry2"}}}; + ASSERT_EQ(2, v2.asMap().find("key1")->second.asArray()[1].asInt()); +} + +TEST(Variant, relationalOperators){ + Variant a{4}; + Variant b{4}; + + ASSERT_EQ(a,b); + + b.setInt(5); + ASSERT_TRUE(a < b); + + b.setDouble(4); + ASSERT_FALSE(a == b); + + a.setDouble(4); + ASSERT_EQ(a,b); +} + +} + diff --git a/test/core/utils/CharReaderTest.cpp b/test/core/utils/CharReaderTest.cpp deleted file mode 100644 index eb04a8e..0000000 --- a/test/core/utils/CharReaderTest.cpp +++ /dev/null @@ -1,823 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program 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. - - This program 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 this program. If not, see . -*/ - -#include -#include -#include - -#include "gtest/gtest.h" - -#include - -namespace ousia { -namespace utils { - -/* Test data */ - -// Generates some pseudo-random data -// (inspired by "Numerical Recipes, Third Edition", Chapter 7.17) -static std::vector generateData(size_t len) -{ - const uint32_t B1 = 17; - const uint32_t B2 = 15; - const uint32_t B3 = 5; - uint32_t v = 0xF3A99148; - std::vector res; - for (size_t i = 0; i < len; i++) { - while (true) { - // Advance the random seed - v = v ^ (v >> B1); - v = v ^ (v << B2); - v = v ^ (v >> B3); - - // Replace \n and \r in order to avoid line break processing by the - // CharReader - char c = v & 0xFF; - if (c != '\n' && c != '\r') { - res.push_back(c); - break; - } - } - } - return res; -} - -// For performance tests only -// static constexpr size_t DATA_LENGTH = 16 * 1024 * 1024 + 795; -static constexpr size_t DATA_LENGTH = 256 * 1024 + 795; -static const std::vector DATA = generateData(DATA_LENGTH); - -/* Buffer Test */ - -TEST(Buffer, simpleRead) -{ - std::string testStr{"this is a test"}; - - // Create buffer with the test string - char c; - Buffer buf{testStr}; - - // Create a read cursor - Buffer::CursorId cursor = buf.createCursor(); - - // We're not at the end of the stream - ASSERT_FALSE(buf.atEnd(cursor)); - - // The cursor must be at zero - ASSERT_EQ(0U, buf.offset(cursor)); - - // Try to read the test string - std::string res; - while (buf.read(cursor, c)) { - res.append(&c, 1); - } - - // The cursor must be at the end - ASSERT_TRUE(buf.atEnd(cursor)); - - // The cursor must be one byond the last byte - ASSERT_EQ(testStr.size(), buf.offset(cursor)); - - // The two strings must equal - ASSERT_EQ(testStr, res); - - buf.deleteCursor(cursor); -} - -TEST(Buffer, cursorManagement) -{ - Buffer buf{""}; - - Buffer::CursorId c1 = buf.createCursor(); - Buffer::CursorId c2 = buf.createCursor(); - Buffer::CursorId c3 = buf.createCursor(); - - ASSERT_EQ(0U, c1); - ASSERT_EQ(1U, c2); - ASSERT_EQ(2U, c3); - - buf.deleteCursor(c2); - Buffer::CursorId c4 = buf.createCursor(); - ASSERT_EQ(1U, c4); - - buf.deleteCursor(c1); - buf.deleteCursor(c3); - buf.deleteCursor(c4); -} - -TEST(Buffer, twoCursors) -{ - std::string testStr{"this is a test"}; - - // Create buffer with the test string - char c; - Buffer buf{testStr}; - - // Create two read cursors - Buffer::CursorId cur1 = buf.createCursor(); - Buffer::CursorId cur2 = buf.createCursor(); - - ASSERT_FALSE(buf.atEnd(cur1)); - ASSERT_FALSE(buf.atEnd(cur2)); - - // Try to read the test string with the first cursor - std::string res1; - while (buf.read(cur1, c)) { - res1.append(&c, 1); - } - - // The first cursor must be at the end - ASSERT_TRUE(buf.atEnd(cur1)); - ASSERT_FALSE(buf.atEnd(cur2)); - - // Try to read the test string with the second cursor - std::string res2; - while (buf.read(cur2, c)) { - res2.append(&c, 1); - } - - // The first cursor must be at the end - ASSERT_TRUE(buf.atEnd(cur1)); - ASSERT_TRUE(buf.atEnd(cur2)); - - // The two strings must equal - ASSERT_EQ(testStr, res1); - ASSERT_EQ(testStr, res2); - - buf.deleteCursor(cur1); - buf.deleteCursor(cur2); -} - -TEST(Buffer, copyCursors) -{ - std::string testStr{"test1 test2 test3"}; - - // Create buffer with the test string - char c; - Buffer buf{testStr}; - - // Create two read cursors - Buffer::CursorId cur1 = buf.createCursor(); - Buffer::CursorId cur2 = buf.createCursor(); - - ASSERT_FALSE(buf.atEnd(cur1)); - ASSERT_FALSE(buf.atEnd(cur2)); - - // Read the first six characters with cursor one - std::string res1; - for (int i = 0; i < 6; i++) { - if (buf.read(cur1, c)) { - res1.append(&c, 1); - } - } - ASSERT_EQ("test1 ", res1); - ASSERT_FALSE(buf.atEnd(cur1)); - - // Copy cur1 to cur2, free cur1 - buf.copyCursor(cur1, cur2); - buf.deleteCursor(cur1); - - std::string res2; - for (int i = 0; i < 6; i++) { - if (buf.read(cur2, c)) { - res2.append(&c, 1); - } - } - ASSERT_EQ("test2 ", res2); - ASSERT_FALSE(buf.atEnd(cur2)); - - // Create a new cursor as copy of cur2 - Buffer::CursorId cur3 = buf.createCursor(cur2); - std::string res3; - for (int i = 0; i < 6; i++) { - if (buf.read(cur3, c)) { - res3.append(&c, 1); - } - } - ASSERT_EQ("test3", res3); - - ASSERT_TRUE(buf.atEnd(cur3)); - - buf.deleteCursor(cur1); - buf.deleteCursor(cur2); - buf.deleteCursor(cur3); -} - -TEST(Buffer, moveCursor) -{ - std::string testStr{"test1 test2 test3"}; - - // Create buffer with the test string - char c; - Buffer buf{testStr}; - Buffer::CursorId cursor = buf.createCursor(); - - // Read the first six characters with cursor one - { - std::string res; - for (int i = 0; i < 6; i++) { - if (buf.read(cursor, c)) { - res.append(&c, 1); - } - } - ASSERT_EQ("test1 ", res); - } - - // Move six bytes backward - ASSERT_EQ(-6, buf.moveCursor(cursor, -6)); - { - std::string res; - for (int i = 0; i < 6; i++) { - if (buf.read(cursor, c)) { - res.append(&c, 1); - } - } - ASSERT_EQ("test1 ", res); - } - - // Move more than six bytes backward - ASSERT_EQ(-6, buf.moveCursor(cursor, -1000)); - { - std::string res; - for (int i = 0; i < 6; i++) { - if (buf.read(cursor, c)) { - res.append(&c, 1); - } - } - ASSERT_EQ("test1 ", res); - } - - // Move six bytes forward - ASSERT_EQ(6, buf.moveCursor(cursor, 6)); - { - std::string res; - for (int i = 0; i < 6; i++) { - if (buf.read(cursor, c)) { - res.append(&c, 1); - } - } - ASSERT_EQ("test3", res); - } - - buf.deleteCursor(cursor); -} - -struct VectorReadState { - size_t offs; - const std::vector &data; - - VectorReadState(const std::vector &data) : offs(0), data(data) {} -}; - -static size_t readFromVector(char *buf, size_t size, void *userData) -{ - VectorReadState &state = *(static_cast(userData)); - size_t tar = std::min(state.offs + size, state.data.size()); - for (size_t i = state.offs; i < tar; i++) { - *buf = state.data[i]; - buf++; - } - size_t res = tar - state.offs; - state.offs = tar; - return res; -} - -TEST(Buffer, simpleStream) -{ - VectorReadState state(DATA); - - Buffer buf{readFromVector, &state}; - Buffer::CursorId cursor = buf.createCursor(); - - char c; - std::vector res; - while (buf.read(cursor, c)) { - res.push_back(c); - } - - // We must be at the end of the buffer and the cursor offset must be set - // correctly - ASSERT_TRUE(buf.atEnd(cursor)); - ASSERT_EQ(DATA_LENGTH, buf.offset(cursor)); - - // The read data and the original data must be equal - ASSERT_EQ(DATA, res); - - buf.deleteCursor(cursor); -} - -TEST(Buffer, streamTwoCursors) -{ - VectorReadState state(DATA); - - Buffer buf{readFromVector, &state}; - Buffer::CursorId cur1 = buf.createCursor(); - Buffer::CursorId cur2 = buf.createCursor(); - - char c; - - std::vector res1; - while (buf.read(cur1, c)) { - res1.push_back(c); - } - - ASSERT_TRUE(buf.atEnd(cur1)); - ASSERT_FALSE(buf.atEnd(cur2)); - ASSERT_EQ(DATA_LENGTH, buf.offset(cur1)); - ASSERT_EQ(0U, buf.offset(cur2)); - - std::vector res2; - while (buf.read(cur2, c)) { - res2.push_back(c); - } - - ASSERT_TRUE(buf.atEnd(cur1)); - ASSERT_TRUE(buf.atEnd(cur2)); - ASSERT_EQ(DATA_LENGTH, buf.offset(cur1)); - ASSERT_EQ(DATA_LENGTH, buf.offset(cur2)); - - // The read data and the original data must be equal - ASSERT_EQ(DATA, res1); - ASSERT_EQ(DATA, res2); - - buf.deleteCursor(cur1); - buf.deleteCursor(cur2); -} - -TEST(Buffer, streamTwoCursorsMovingInterleaved) -{ - VectorReadState state(DATA); - - Buffer buf{readFromVector, &state}; - Buffer::CursorId cur1 = buf.createCursor(); - Buffer::CursorId cur2 = buf.createCursor(); - - char c; - - std::vector res1; - std::vector res2; - while (!buf.atEnd(cur1) || !buf.atEnd(cur2)) { - for (int i = 0; i < 100; i++) { - if (buf.read(cur1, c)) { - res1.push_back(c); - } - } - for (int i = 0; i < 120; i++) { - if (buf.read(cur2, c)) { - res2.push_back(c); - } - } - - // Move cur2 120 bytes backward and read the content again - res2.resize(res2.size() - 120); - ASSERT_EQ(-120, buf.moveCursor(cur2, -120)); - for (int i = 0; i < 120; i++) { - if (buf.read(cur2, c)) { - res2.push_back(c); - } - } - - // Move cur1 60 bytes forward and backward - buf.moveCursor(cur1, -buf.moveCursor(cur1, 60)); - - // Make sure the cursor position is correct - ASSERT_EQ(res1.size(), buf.offset(cur1)); - ASSERT_EQ(res2.size(), buf.offset(cur2)); - } - - ASSERT_EQ(DATA_LENGTH, buf.offset(cur1)); - ASSERT_EQ(DATA_LENGTH, buf.offset(cur2)); - - // The read data and the original data must be equal - ASSERT_EQ(DATA, res1); - ASSERT_EQ(DATA, res2); - - buf.deleteCursor(cur1); - buf.deleteCursor(cur2); -} - -TEST(Buffer, streamMoveForward) -{ - VectorReadState state(DATA); - - std::vector partialData; - partialData.resize(100); - std::copy(DATA.end() - partialData.size(), DATA.end(), partialData.begin()); - - Buffer buf{readFromVector, &state}; - Buffer::CursorId cursor = buf.createCursor(); - ASSERT_EQ(ssize_t(DATA_LENGTH) - 100, - buf.moveCursor(cursor, DATA_LENGTH - 100)); - - char c; - std::vector res; - while (buf.read(cursor, c)) { - res.push_back(c); - } - ASSERT_EQ(partialData, res); - - buf.deleteCursor(cursor); -} - -/* CharReader Test */ - -TEST(CharReader, simpleRead) -{ - std::string testStr{"this is a test"}; - char c; - - // Feed a test string into the reader - CharReader reader{testStr}; - - // Try to read the test string - std::string res; - while (!reader.atEnd()) { - ASSERT_TRUE(reader.read(c)); - res.append(&c, 1); - } - - // The two strings must equal - ASSERT_EQ(testStr, res); - - // We must now be at line 1, column 15 - ASSERT_EQ(1U, reader.getLine()); - ASSERT_EQ(testStr.size() + 1, reader.getColumn()); - - // If we call either read or peek, false is returned - ASSERT_FALSE(reader.read(c)); - ASSERT_FALSE(reader.peek(c)); -} - -TEST(CharReader, simplePeek) -{ - std::string testStr{"this is a test"}; - char c; - - // Feed a test string into the reader - CharReader reader{testStr}; - - // Try to read the test string - std::string res; - while (reader.peek(c)) { - res.append(&c, 1); - } - - // Peeking does not trigger the "atEnd" flag - ASSERT_FALSE(reader.atEnd()); - - // The two strings must equal - ASSERT_EQ(testStr, res); - - // We must now be at line 1, column 1 and NOT at the end of the stream - ASSERT_EQ(1U, reader.getLine()); - ASSERT_EQ(1U, reader.getColumn()); - ASSERT_FALSE(reader.atEnd()); - - // If we consume the peek, we must be at line 1, column 15 and we should be - // at the end of the stream - reader.consumePeek(); - ASSERT_EQ(1U, reader.getLine()); - ASSERT_EQ(testStr.size() + 1, reader.getColumn()); - ASSERT_TRUE(reader.atEnd()); - - // If we call either read or peek, false is returned - ASSERT_FALSE(reader.read(c)); - ASSERT_FALSE(reader.peek(c)); -} - -TEST(CharReader, rowColumnCounter) -{ - // Feed a test string into the reader - CharReader reader{"1\n\r2\n3\r\n\n4"}; - - // We should currently be in line 1, column 1 - ASSERT_EQ(1U, reader.getLine()); - ASSERT_EQ(1U, reader.getColumn()); - - // Read two characters - char c; - for (int i = 0; i < 2; i++) - reader.read(c); - ASSERT_EQ(2U, reader.getLine()); - ASSERT_EQ(1U, reader.getColumn()); - - // Read two characters - for (int i = 0; i < 2; i++) - reader.read(c); - ASSERT_EQ(3U, reader.getLine()); - ASSERT_EQ(1U, reader.getColumn()); - - // Read three characters - for (int i = 0; i < 3; i++) - reader.read(c); - ASSERT_EQ(5U, reader.getLine()); - ASSERT_EQ(1U, reader.getColumn()); -} - -TEST(CharReader, rowColumnCounterTest) -{ - // Feed a test string into the reader - CharReader reader{"1\n\r2\n3\r\n\n4", 4, 10}; - - // We should currently be in line 1, column 1 - ASSERT_EQ(4U, reader.getLine()); - ASSERT_EQ(10U, reader.getColumn()); - - // Read two characters - char c; - for (int i = 0; i < 2; i++) - reader.read(c); - ASSERT_EQ(5U, reader.getLine()); - ASSERT_EQ(1U, reader.getColumn()); - - // Read two characters - for (int i = 0; i < 2; i++) - reader.read(c); - ASSERT_EQ(6U, reader.getLine()); - ASSERT_EQ(1U, reader.getColumn()); - - // Read three characters - for (int i = 0; i < 3; i++) - reader.read(c); - ASSERT_EQ(8U, reader.getLine()); - ASSERT_EQ(1U, reader.getColumn()); -} - -TEST(CharReader, linebreakSubstitution) -{ - // Feed a test string into the reader and read all characters back - CharReader reader{"this\n\ris\n\rjust\na test\r\n\rtest\n\r"}; - std::string res; - char c; - while (reader.read(c)) { - res.append(&c, 1); - } - - // Test for equality - ASSERT_EQ("this\nis\njust\na test\n\ntest\n", res); -} - -TEST(CharReader, rowColumnCounterUTF8) -{ - // Feed a test string with some umlauts into the reader - CharReader reader{"\x61\xc3\x96\xc3\x84\xc3\x9c\xc3\x9f"}; - - // Read all bytes - char c; - while (reader.read(c)) { - // Do nothing - } - - // The sequence above equals 5 UTF-8 characters (so after reading all the - // cursor is at position 6) - ASSERT_EQ(1U, reader.getLine()); - ASSERT_EQ(6U, reader.getColumn()); -} - -TEST(CharReader, stream) -{ - // Copy the test data to a string stream - std::stringstream ss; - std::copy(DATA.begin(), DATA.end(), std::ostream_iterator(ss)); - - // Read the data back from the stream - std::vector res; - char c; - CharReader reader{ss}; - while (reader.read(c)) { - res.push_back(c); - } - ASSERT_EQ(DATA_LENGTH, res.size()); - ASSERT_EQ(DATA, res); -} - -TEST(CharReader, fork) -{ - std::string testStr{"first line\n\n\rsecond line\n\rlast line"}; - // 0123456789 0 123456789012 3456789012 - // 0 1 2 3 - - char c; - CharReader reader{testStr}; - - // Read a few characters - for (int i = 0; i < 4; i++) - reader.read(c); - - // Peek a few characters - for (int i = 4; i < 7; i++) - reader.peek(c); - - // Fork the reader - { - CharReaderFork fork = reader.fork(); - - ASSERT_EQ(1U, fork.getLine()); - ASSERT_EQ(5U, fork.getColumn()); - - fork.peek(c); - ASSERT_EQ('i', c); - - fork.read(c); - ASSERT_EQ('t', c); - - ASSERT_EQ(1U, fork.getLine()); - ASSERT_EQ(6U, fork.getColumn()); - - ASSERT_EQ(1U, reader.getLine()); - ASSERT_EQ(5U, reader.getColumn()); - - reader.read(c); - reader.read(c); - ASSERT_EQ(' ', c); - - fork.commit(); - } - ASSERT_EQ(1U, reader.getLine()); - ASSERT_EQ(6U, reader.getColumn()); -} - -TEST(CharReaderTest, context) -{ - std::string testStr{"first line\n\n\rsecond line\n\rlast line"}; - // 0123456789 0 123456789012 3456789012 - // 0 1 2 3 - - // Retrieval at beginning of stream - { - CharReader reader{testStr}; - CharReader::Context ctx = reader.getContext(80); - ASSERT_EQ("first line", ctx.line); - ASSERT_EQ(0U, ctx.relPos); - ASSERT_FALSE(ctx.truncatedStart); - ASSERT_FALSE(ctx.truncatedEnd); - } - - // Retrieval in middle of line - { - CharReader reader{testStr}; - CharReader::Context ctx = reader.getContext(80); - - char c; - for (int i = 0; i < 5; i++) - reader.read(c); - - ASSERT_EQ("first line", ctx.line); - ASSERT_EQ(0U, ctx.relPos); - ASSERT_FALSE(ctx.truncatedStart); - ASSERT_FALSE(ctx.truncatedEnd); - } - - // Retrieval in whitespace sequence - { - CharReader reader{testStr}; - - char c; - for (int i = 0; i < 11; i++) - reader.read(c); - - CharReader::Context ctx = reader.getContext(80); - ASSERT_EQ("first line", ctx.line); - ASSERT_EQ(10U, ctx.relPos); - ASSERT_FALSE(ctx.truncatedStart); - ASSERT_FALSE(ctx.truncatedEnd); - } - - // Truncation of text - { - CharReader reader{testStr}; - - char c; - for (int i = 0; i < 5; i++) - reader.read(c); - - CharReader::Context ctx = reader.getContext(3); - ASSERT_EQ("t l", ctx.line); - ASSERT_EQ(1U, ctx.relPos); - ASSERT_TRUE(ctx.truncatedStart); - ASSERT_TRUE(ctx.truncatedEnd); - } - - // Second line - { - CharReader reader{testStr}; - - char c; - for (int i = 0; i < 12; i++) - reader.read(c); - - CharReader::Context ctx = reader.getContext(80); - ASSERT_EQ("second line", ctx.line); - ASSERT_EQ(0U, ctx.relPos); - ASSERT_FALSE(ctx.truncatedStart); - ASSERT_FALSE(ctx.truncatedEnd); - } - - // End of second line - { - CharReader reader{testStr}; - - char c; - for (int i = 0; i < 23; i++) - reader.read(c); - - CharReader::Context ctx = reader.getContext(80); - ASSERT_EQ("second line", ctx.line); - ASSERT_EQ(11U, ctx.relPos); - ASSERT_FALSE(ctx.truncatedStart); - ASSERT_FALSE(ctx.truncatedEnd); - } - - // Last line - { - CharReader reader{testStr}; - - char c; - for (int i = 0; i < 24; i++) - reader.read(c); - - CharReader::Context ctx = reader.getContext(80); - ASSERT_EQ("last line", ctx.line); - ASSERT_EQ(0U, ctx.relPos); - ASSERT_FALSE(ctx.truncatedStart); - ASSERT_FALSE(ctx.truncatedEnd); - } - - // Middle of last line - { - CharReader reader{testStr}; - - char c; - for (int i = 0; i < 28; i++) - reader.read(c); - - CharReader::Context ctx = reader.getContext(80); - ASSERT_EQ("last line", ctx.line); - ASSERT_EQ(4U, ctx.relPos); - ASSERT_FALSE(ctx.truncatedStart); - ASSERT_FALSE(ctx.truncatedEnd); - } - - // Middle of last line truncated - { - CharReader reader{testStr}; - - char c; - for (int i = 0; i < 28; i++) - reader.read(c); - - CharReader::Context ctx = reader.getContext(3); - ASSERT_EQ("t l", ctx.line); - ASSERT_EQ(1U, ctx.relPos); - ASSERT_TRUE(ctx.truncatedStart); - ASSERT_TRUE(ctx.truncatedEnd); - } - - // End of stream - { - CharReader reader{testStr}; - - char c; - for (int i = 0; i < 100; i++) - reader.read(c); - - CharReader::Context ctx = reader.getContext(80); - ASSERT_EQ("last line", ctx.line); - ASSERT_EQ(9U, ctx.relPos); - ASSERT_FALSE(ctx.truncatedStart); - ASSERT_FALSE(ctx.truncatedEnd); - } - - // End of stream truncated - { - CharReader reader{testStr}; - - char c; - for (int i = 0; i < 100; i++) - reader.read(c); - - CharReader::Context ctx = reader.getContext(4); - ASSERT_EQ("line", ctx.line); - ASSERT_EQ(4U, ctx.relPos); - ASSERT_TRUE(ctx.truncatedStart); - ASSERT_FALSE(ctx.truncatedEnd); - } -} -} -} - diff --git a/test/core/variant/ReaderTest.cpp b/test/core/variant/ReaderTest.cpp deleted file mode 100644 index 43e85a5..0000000 --- a/test/core/variant/ReaderTest.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program 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. - - This program 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 this program. If not, see . -*/ - -#include -#include - -#include - -namespace ousia { -namespace variant { - -//static TerminalLogger logger{std::cerr, true}; -static Logger logger; - -TEST(Reader, readString) -{ - // Simple, double quoted string - { - CharReader reader("\"hello world\""); - auto res = Reader::parseString(reader, logger); - ASSERT_TRUE(res.first); - ASSERT_EQ("hello world", res.second); - } - - // Simple, double quoted string with whitespace - { - CharReader reader(" \"hello world\" "); - auto res = Reader::parseString(reader, logger); - ASSERT_TRUE(res.first); - ASSERT_EQ("hello world", res.second); - } - - // Simple, single quoted string - { - CharReader reader("'hello world'"); - auto res = Reader::parseString(reader, logger); - ASSERT_TRUE(res.first); - ASSERT_EQ("hello world", res.second); - } - - // Escape characters - { - CharReader reader("'\\'\\\"\\b\\f\\n\\r\\t\\v'"); - auto res = Reader::parseString(reader, logger); - ASSERT_TRUE(res.first); - ASSERT_EQ("'\"\b\f\n\r\t\v", res.second); - } -} - -TEST(Reader, parseUnescapedString) -{ - // Simple case - { - CharReader reader("hello world;"); - auto res = Reader::parseUnescapedString(reader, logger, {';'}); - ASSERT_TRUE(res.first); - ASSERT_EQ("hello world", res.second); - } - - // Simple case with whitespace - { - CharReader reader(" hello world ; "); - auto res = Reader::parseUnescapedString(reader, logger, {';'}); - ASSERT_TRUE(res.first); - ASSERT_EQ("hello world", res.second); - } - - // Linebreaks - { - CharReader reader(" hello\nworld ; "); - auto res = Reader::parseUnescapedString(reader, logger, {';'}); - ASSERT_TRUE(res.first); - ASSERT_EQ("hello\nworld", res.second); - } - - // End of stream - { - CharReader reader(" hello world "); - auto res = Reader::parseUnescapedString(reader, logger, {';'}); - ASSERT_TRUE(res.first); - ASSERT_EQ("hello world", res.second); - } -} - -static const std::unordered_set noDelim; - -TEST(Reader, parseInteger) -{ - // Valid integers - { - CharReader reader("0 "); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(0, res.second); - } - - { - CharReader reader("42 "); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(42, res.second); - } - - { - CharReader reader("-42"); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(-42, res.second); - } - - { - CharReader reader(" -0x4A2 "); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(-0x4A2, res.second); - } - - { - CharReader reader(" 0Xaffe"); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(0xAFFE, res.second); - } - - { - CharReader reader("0x7FFFFFFFFFFFFFFF"); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(0x7FFFFFFFFFFFFFFFL, res.second); - } - - { - CharReader reader("-0x7FFFFFFFFFFFFFFF"); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(-0x7FFFFFFFFFFFFFFFL, res.second); - } - - // Invalid integers - { - CharReader reader("-"); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_FALSE(res.first); - } - - { - CharReader reader("0a"); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_FALSE(res.first); - } - - { - CharReader reader("-0xag"); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_FALSE(res.first); - } - - { - CharReader reader("0x8000000000000000"); - auto res = Reader::parseInteger(reader, logger, noDelim); - ASSERT_FALSE(res.first); - } -} - -TEST(Reader, parseDouble) -{ - // Valid doubles - { - CharReader reader("1.25"); - auto res = Reader::parseDouble(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(1.25, res.second); - } - - { - CharReader reader(".25"); - auto res = Reader::parseDouble(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(.25, res.second); - } - - { - CharReader reader(".25e1"); - auto res = Reader::parseDouble(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(2.5, res.second); - } - - { - CharReader reader("-2.5e-1"); - auto res = Reader::parseDouble(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(-0.25, res.second); - } - - { - CharReader reader("-50e-2"); - auto res = Reader::parseDouble(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(-0.5, res.second); - } - - { - CharReader reader("-1."); - auto res = Reader::parseDouble(reader, logger, noDelim); - ASSERT_TRUE(res.first); - ASSERT_EQ(-1., res.second); - } - - { - CharReader reader("-50.e-2"); - auto res = Reader::parseDouble(reader, logger, {'.'}); - ASSERT_TRUE(res.first); - ASSERT_EQ(-50, res.second); - } - - // Invalid doubles - { - CharReader reader(".e1"); - auto res = Reader::parseDouble(reader, logger, noDelim); - ASSERT_FALSE(res.first); - } - - { - CharReader reader("0e100000"); - auto res = Reader::parseDouble(reader, logger, noDelim); - ASSERT_FALSE(res.first); - } -} - -TEST(Reader, parseArray) -{ - // Simple case (only primitive data types) - { - CharReader reader("[\"Hello, World\", unescaped\n string ,\n" - "1234, 0.56, true, false, null]"); - auto res = Reader::parseArray(reader, logger); - ASSERT_TRUE(res.first); - - // Make sure array has the correct size - ASSERT_EQ(7U, res.second.size()); - - // Check the types - ASSERT_TRUE(res.second[0].isString()); - ASSERT_TRUE(res.second[1].isString()); - ASSERT_TRUE(res.second[2].isInt()); - ASSERT_TRUE(res.second[3].isDouble()); - ASSERT_TRUE(res.second[4].isBool()); - ASSERT_TRUE(res.second[5].isBool()); - ASSERT_TRUE(res.second[6].isNull()); - - // Check the values - ASSERT_EQ("Hello, World", res.second[0].asString()); - ASSERT_EQ("unescaped\n string", res.second[1].asString()); - ASSERT_EQ(1234, res.second[2].asInt()); - ASSERT_EQ(0.56, res.second[3].asDouble()); - ASSERT_TRUE(res.second[4].asBool()); - ASSERT_FALSE(res.second[5].asBool()); - } - - // Ending with comma - { - CharReader reader("[ 'test' ,]"); - auto res = Reader::parseArray(reader, logger); - ASSERT_TRUE(res.first); - - // Make sure the array has the correct size - ASSERT_EQ(1U, res.second.size()); - - // Check the types - ASSERT_TRUE(res.second[0].isString()); - - // Check the values - ASSERT_EQ("test", res.second[0].asString()); - } - - // Recovery from invalid values - { - CharReader reader("[ 0invalidNumber, str, 1invalid]"); - auto res = Reader::parseArray(reader, logger); - ASSERT_TRUE(res.first); - - // Make sure the array has the correct size - ASSERT_EQ(3U, res.second.size()); - - // Check the types (all must be strings since the numbers are invalid) - ASSERT_TRUE(res.second[0].isString()); - ASSERT_TRUE(res.second[1].isString()); - ASSERT_TRUE(res.second[2].isString()); - - // Check the values - ASSERT_EQ("0invalidNumber", res.second[0].asString()); - ASSERT_EQ("str", res.second[1].asString()); - ASSERT_EQ("1invalid", res.second[2].asString()); - } -} - -TEST(Reader, parseGeneric) -{ - // Simple case, unescaped string - { - CharReader reader("hello world"); - auto res = Reader::parseGeneric(reader, logger, {';'}); - ASSERT_TRUE(res.first); - ASSERT_TRUE(res.second.isString()); - ASSERT_EQ("hello world", res.second.asString()); - } - - // Simple case, double quoted string - { - CharReader reader(" \"hello world\" "); - auto res = Reader::parseGeneric(reader, logger, {';'}); - ASSERT_TRUE(res.first); - ASSERT_TRUE(res.second.isString()); - ASSERT_EQ("hello world", res.second.asString()); - } - - // Simple case, single quoted string - { - CharReader reader(" 'hello world' "); - auto res = Reader::parseGeneric(reader, logger, {';'}); - ASSERT_TRUE(res.first); - ASSERT_TRUE(res.second.isString()); - ASSERT_EQ("hello world", res.second.asString()); - } -} - -} -} - diff --git a/test/core/variant/VariantTest.cpp b/test/core/variant/VariantTest.cpp deleted file mode 100644 index e51cf36..0000000 --- a/test/core/variant/VariantTest.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - Ousía - Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel - - This program 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. - - This program 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 this program. If not, see . -*/ - -#include - -#include - -#include - -namespace ousia { - -TEST(Variant, nullValue) -{ - Variant v; - ASSERT_TRUE(v.isNull()); - - v = 1; - ASSERT_FALSE(v.isNull()); - - v = nullptr; - ASSERT_TRUE(v.isNull()); - - Variant v2{nullptr}; - ASSERT_TRUE(v.isNull()); -} - -TEST(Variant, booleanValue) -{ - Variant v{true}; - ASSERT_TRUE(v.isBool()); - ASSERT_TRUE(v.asBool()); - - v = false; - ASSERT_TRUE(v.isBool()); - ASSERT_FALSE(v.asBool()); - - v.setBool(true); - ASSERT_TRUE(v.isBool()); - ASSERT_TRUE(v.asBool()); - - v = nullptr; - ASSERT_FALSE(v.isBool()); -} - -TEST(Variant, intValue) -{ - Variant v{42}; - ASSERT_TRUE(v.isInt()); - ASSERT_EQ(42, v.asInt()); - - v = 43; - ASSERT_TRUE(v.isInt()); - ASSERT_EQ(43, v.asInt()); - - v = false; - ASSERT_FALSE(v.isInt()); -} - -TEST(Variant, doubleValue) -{ - Variant v{42.5}; - ASSERT_TRUE(v.isDouble()); - ASSERT_EQ(42.5, v.asDouble()); - - v = 42; - ASSERT_FALSE(v.isDouble()); - - v = 43.5; - ASSERT_TRUE(v.isDouble()); - ASSERT_EQ(43.5, v.asDouble()); -} - -TEST(Variant, stringValue) -{ - Variant v{"Hello World"}; - ASSERT_TRUE(v.isString()); - ASSERT_EQ("Hello World", v.asString()); - - v = "Goodbye World"; - ASSERT_TRUE(v.isString()); - ASSERT_EQ("Goodbye World", v.asString()); - - v = 42; - ASSERT_FALSE(v.isString()); -} - -TEST(Variant, arrayValue) -{ - const Variant v{{"test1", 42}}; - ASSERT_EQ(2, v.asArray().size()); - ASSERT_EQ("test1", v.asArray()[0].asString()); - ASSERT_EQ(42, v.asArray()[1].asInt()); -} - -TEST(Variant, mapValue) -{ - const Variant v{{{"key1", "entry1"}, {"key2", "entry2"}}}; - - auto map = v.asMap(); - ASSERT_EQ(2, map.size()); - - ASSERT_EQ("entry1", map.find("key1")->second.asString()); - ASSERT_EQ("entry2", map.find("key2")->second.asString()); - - const Variant v2{{{"key1", Variant::arrayType{1, 2}}, {"key2", "entry2"}}}; - ASSERT_EQ(2, v2.asMap().find("key1")->second.asArray()[1].asInt()); -} - -TEST(Variant, relationalOperators){ - Variant a{4}; - Variant b{4}; - - ASSERT_EQ(a,b); - - b.setInt(5); - ASSERT_TRUE(a < b); - - b.setDouble(4); - ASSERT_FALSE(a == b); - - a.setDouble(4); - ASSERT_EQ(a,b); -} - -} - -- cgit v1.2.3