Skip to content

Commit

Permalink
Support LINES() without size, as the last segment (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
fushar committed Dec 3, 2016
1 parent 09a17bd commit 03ac5e1
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 23 deletions.
33 changes: 29 additions & 4 deletions include/tcframe/io_manipulator/LinesIOSegmentManipulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ class LinesIOSegmentManipulator {

string lastVariableName;

for (int j = 0; j < *segment->size(); j++) {
for (int j = 0; j != *segment->size(); j++) {
if (*segment->size() == -1 && WhitespaceManipulator::isEof(in)) {
break;
}

bool isFirstColumn = true;
for (Variable* variable : segment->variables()) {
if (variable->type() == VariableType::VECTOR) {
Expand Down Expand Up @@ -58,7 +62,8 @@ class LinesIOSegmentManipulator {
static void print(LinesIOSegment* segment, ostream* out) {
checkVectorSizes(segment);

for (int j = 0; j < *segment->size(); j++) {
int size = getSize(segment);
for (int j = 0; j < size; j++) {
for (int i = 0; i < segment->variables().size(); i++) {
Variable *variable = segment->variables()[i];
if (variable->type() == VariableType::VECTOR) {
Expand All @@ -78,7 +83,22 @@ class LinesIOSegmentManipulator {
}

private:
static int getSize(LinesIOSegment* segment) {
if (*segment->size() != -1) {
return *segment->size();
}

Variable* firstVariable = segment->variables()[0];
if (firstVariable->type() == VariableType::VECTOR) {
return ((Vector*) firstVariable)->size();
} else {
return ((Matrix*) firstVariable)->rows();
}
}

static void checkVectorSizes(LinesIOSegment* segment) {
int expectedSize = getSize(segment);

for (Variable* variable : segment->variables()) {
int size;
string type;
Expand All @@ -89,10 +109,15 @@ class LinesIOSegmentManipulator {
size = ((Matrix*) variable)->rows();
type = "jagged vector";
}
if (size != *segment->size()) {
if (size != expectedSize) {
string withoutSizeMessage;
if (*segment->size() == -1) {
string firstVariableName = TokenFormatter::formatVariable(segment->variables()[0]->name());
withoutSizeMessage = " (number of elements of " + firstVariableName + ")";
}
throw runtime_error(
"Number of elements of " + type + " " + TokenFormatter::formatVariable(variable->name())
+ " unsatisfied. Expected: " + StringUtils::toString(*segment->size())
+ " unsatisfied. Expected: " + StringUtils::toString(expectedSize) + withoutSizeMessage
+ ", actual: " + StringUtils::toString(size));
}
}
Expand Down
18 changes: 17 additions & 1 deletion include/tcframe/spec/io/IOFormat.hpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#pragma once

#include <vector>
#include <stdexcept>
#include <utility>
#include <vector>

#include "GridIOSegment.hpp"
#include "IOSegment.hpp"
#include "LineIOSegment.hpp"
#include "LinesIOSegment.hpp"

using std::move;
using std::runtime_error;
using std::vector;

namespace tcframe {
Expand Down Expand Up @@ -99,11 +101,25 @@ class IOFormatBuilder {
void addLastSegment() {
if (lastBuilder_ != nullptr) {
if (currentFormat_ != nullptr) {
if (hasLinesSegmentWithoutSizeAsLastSegment()) {
throw runtime_error("Lines segment without size can only be the last segment");
}
currentFormat_->push_back(lastBuilder_->build());
}
lastBuilder_ = nullptr;
}
}

bool hasLinesSegmentWithoutSizeAsLastSegment() {
if (currentFormat_->empty()) {
return false;
}
IOSegment* lastSegment = currentFormat_->back();
if (lastSegment->type() != IOSegmentType::LINES) {
return false;
}
return *((LinesIOSegment*) lastSegment)->size() == -1;
}
};

}
4 changes: 0 additions & 4 deletions include/tcframe/spec/io/LinesIOSegment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,6 @@ class LinesIOSegmentBuilder : public IOSegmentBuilder {
if (subject_->variables_.empty()) {
throw runtime_error("Lines segment must have at least one variable");
}

if (*subject_->size_ == -1) {
throw runtime_error("Lines segment must define vector sizes");
}
}

void checkJaggedVector() {
Expand Down
4 changes: 4 additions & 0 deletions include/tcframe/spec/variable/WhitespaceManipulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class WhitespaceManipulator {
}
}

static bool isEof(istream* in) {
return in->peek() == char_traits<char>::eof();
}

static void ensureEof(istream* in) {
if (in->peek() != char_traits<char>::eof()) {
throw runtime_error("Expected: <EOF>");
Expand Down
138 changes: 138 additions & 0 deletions test/tcframe/io_manipulator/LinesIOSegmentManipulatorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ class LinesIOSegmentManipulatorTests : public Test {
.addJaggedVectorVariable(Matrix::create(Z, "Z"))
.setSize(size)
.build();
LinesIOSegment* segmentWithoutSize = LinesIOSegmentBuilder()
.addVectorVariable(Vector::create(X, "X"))
.addVectorVariable(Vector::create(Y, "Y"))
.build();
LinesIOSegment* segmentWithJaggedVectorWithoutSize = LinesIOSegmentBuilder()
.addVectorVariable(Vector::create(X, "X"))
.addVectorVariable(Vector::create(Y, "Y"))
.addJaggedVectorVariable(Matrix::create(Z, "Z"))
.build();
};

TEST_F(LinesIOSegmentManipulatorTests, Parsing_Successful) {
Expand Down Expand Up @@ -105,6 +114,79 @@ TEST_F(LinesIOSegmentManipulatorTests, Parsing_WithJaggedVector_Failed_MissingSp
}
}

TEST_F(LinesIOSegmentManipulatorTests, Parsing_WithoutSize_Successful) {
istringstream in("1 2\n3 4\n5 6\n");

LinesIOSegmentManipulator::parse(segmentWithoutSize, &in);
EXPECT_THAT(X, Eq(vector<int>{1, 3, 5}));
EXPECT_THAT(Y, Eq(vector<int>{2, 4, 6}));
}

TEST_F(LinesIOSegmentManipulatorTests, Parsing_WithoutSize_Successful_CheckLastVariable) {
istringstream in("1 2\n3 4\n5 6\n");

EXPECT_THAT(LinesIOSegmentManipulator::parse(segmentWithoutSize, &in), Eq("'Y[2]'"));
}

TEST_F(LinesIOSegmentManipulatorTests, Parsing_WithoutSize_Failed_MissingVariable) {
istringstream in("1 2\n3 ");

try {
LinesIOSegmentManipulator::parse(segmentWithoutSize, &in);
FAIL();
} catch (runtime_error& e) {
EXPECT_THAT(e.what(), StrEq("Cannot parse for 'Y[1]'. Found: <whitespace>"));
}
}

TEST_F(LinesIOSegmentManipulatorTests, Parsing_WithoutSize__Failed_MissingWhitespace) {
istringstream in("1 2\n3");

try {
LinesIOSegmentManipulator::parse(segmentWithoutSize, &in);
FAIL();
} catch (runtime_error& e) {
EXPECT_THAT(e.what(), StrEq("Expected: <space> after 'X[1]'"));
}
}

TEST_F(LinesIOSegmentManipulatorTests, Parsing_WithoutSize_Failed_MissingNewline) {
istringstream in("1 2\n3 4 ");

try {
LinesIOSegmentManipulator::parse(segmentWithoutSize, &in);
FAIL();
} catch (runtime_error& e) {
EXPECT_THAT(e.what(), StrEq("Expected: <newline> after 'Y[1]'"));
}
}

TEST_F(LinesIOSegmentManipulatorTests, Parsing_WithJaggedVector_WithoutSize_Successful) {
istringstream in("1 2 10\n3 4\n5 6 20 30\n");

LinesIOSegmentManipulator::parse(segmentWithJaggedVectorWithoutSize, &in);
EXPECT_THAT(X, Eq(vector<int>{1, 3, 5}));
EXPECT_THAT(Y, Eq(vector<int>{2, 4, 6}));
EXPECT_THAT(Z, Eq(vector<vector<int>>{{10}, {}, {20, 30}}));
}

TEST_F(LinesIOSegmentManipulatorTests, Parsing_WithJaggedVector_WithoutSize_Successful_CheckLastVariable) {
istringstream in("1 2 10\n3 4\n5 6 20 30\n");

EXPECT_THAT(LinesIOSegmentManipulator::parse(segmentWithJaggedVectorWithoutSize, &in), Eq("'Z[2][1]'"));
}

TEST_F(LinesIOSegmentManipulatorTests, Parsing_WithJaggedVector_WithoutSize_Failed_MissingSpaceOrNewline) {
istringstream in("1 2 10\n3 4\n5 6 20 30");

try {
LinesIOSegmentManipulator::parse(segmentWithJaggedVectorWithoutSize, &in);
FAIL();
} catch (runtime_error& e) {
EXPECT_THAT(e.what(), StrEq("Expected: <space> or <newline> after 'Z[2][1]'"));
}
}

TEST_F(LinesIOSegmentManipulatorTests, Printing_Successful) {
ostringstream out;

Expand Down Expand Up @@ -155,4 +237,60 @@ TEST_F(LinesIOSegmentManipulatorTests, Printing_WithJaggedVector_Failed_SizeMism
}
}

TEST_F(LinesIOSegmentManipulatorTests, Printing_WithoutSize_Successful) {
ostringstream out;

X = {1, 3, 5};
Y = {2, 4, 6};

LinesIOSegmentManipulator::print(segmentWithoutSize, &out);
EXPECT_THAT(out.str(), Eq("1 2\n3 4\n5 6\n"));
}

TEST_F(LinesIOSegmentManipulatorTests, Printing_WithoutSize_Failed_DifferentSizes) {
ostringstream out;

X = {1, 3, 5};
Y = {2, 4};

try {
LinesIOSegmentManipulator::print(segmentWithoutSize, &out);
FAIL();
} catch (runtime_error& e) {
EXPECT_THAT(e.what(), StrEq(
"Number of elements of vector 'Y' unsatisfied. "
"Expected: 3 (number of elements of 'X'), "
"actual: 2"));
}
}

TEST_F(LinesIOSegmentManipulatorTests, Printing_WithJaggedVector_WithoutSize_Successful) {
ostringstream out;

X = {1, 3, 5};
Y = {2, 4, 6};
Z = {{10}, {}, {20, 30}};

LinesIOSegmentManipulator::print(segmentWithJaggedVectorWithoutSize, &out);
EXPECT_THAT(out.str(), Eq("1 2 10\n3 4\n5 6 20 30\n"));
}

TEST_F(LinesIOSegmentManipulatorTests, Printing_WithJaggedVector_WithoutSize_Failed_DifferentSizes) {
ostringstream out;

X = {1, 3, 5};
Y = {2, 4, 6};
Z = {{10}, {}};

try {
LinesIOSegmentManipulator::print(segmentWithJaggedVectorWithoutSize, &out);
FAIL();
} catch (runtime_error& e) {
EXPECT_THAT(e.what(),StrEq(
"Number of elements of jagged vector 'Z' unsatisfied. "
"Expected: 3 (number of elements of 'X'), "
"actual: 2"));
}
}

}
6 changes: 6 additions & 0 deletions test/tcframe/spec/core/MagicTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class MagicTests : public Test {
int N = 4;
vector<int> X, Y;
vector<vector<int>> Z;
vector<int> A, B;

int bogus;

Expand All @@ -68,6 +69,7 @@ class MagicTests : public Test {
LINES(X) % SIZE(2);
LINES(X, Y) % SIZE(3);
LINES(X, Y, Z) % SIZE(N);
LINES(A, B);
}

void testInvalid() {
Expand Down Expand Up @@ -189,6 +191,10 @@ TEST_F(MagicTests, LINES_Valid) {
.addVectorVariable(Vector::create(dummy, "Y"))
.addJaggedVectorVariable(Matrix::create(dummy2, "Z"))
.setSize(new int(4));
builder.newLinesIOSegment()
.addVectorVariable(Vector::create(dummy, "A"))
.addVectorVariable(Vector::create(dummy, "B"))
.build();

EXPECT_THAT(ioFormat, Eq(builder.build()));
}
Expand Down
41 changes: 40 additions & 1 deletion test/tcframe/spec/io/IOFormatBuilderTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using ::testing::A;
using ::testing::ElementsAre;
using ::testing::StrEq;
using ::testing::Test;

namespace tcframe {
Expand All @@ -17,7 +18,7 @@ class IOFormatBuilderTests : public Test {
IOFormatBuilder builder;
};

TEST_F(IOFormatBuilderTests, Building) {
TEST_F(IOFormatBuilderTests, Building_Successful) {
builder.prepareForInputFormat();
builder
.newLineIOSegment()
Expand Down Expand Up @@ -58,4 +59,42 @@ TEST_F(IOFormatBuilderTests, Building) {
A<LineIOSegment*>()));
}

TEST_F(IOFormatBuilderTests, Building_Failed_LinesSegmentWithoutSizeNotLast) {
try {
builder.prepareForInputFormat();
builder
.newLineIOSegment()
.addScalarVariable(Scalar::create(X, "X"));
builder
.newLinesIOSegment()
.addVectorVariable(Vector::create(Y, "Y"));
builder
.newGridIOSegment()
.addMatrixVariable(Matrix::create(Z, "Z"))
.setSize(new int(2), new int(3));
builder.build();
FAIL();
} catch (runtime_error& e) {
EXPECT_THAT(e.what(), StrEq("Lines segment without size can only be the last segment"));
}

try {
builder.prepareForOutputFormat();
builder
.newLineIOSegment()
.addScalarVariable(Scalar::create(X, "X"));
builder
.newLinesIOSegment()
.addVectorVariable(Vector::create(Y, "Y"));
builder
.newGridIOSegment()
.addMatrixVariable(Matrix::create(Z, "Z"))
.setSize(new int(2), new int(3));
builder.build();
FAIL();
} catch (runtime_error& e) {
EXPECT_THAT(e.what(), StrEq("Lines segment without size can only be the last segment"));
}
}

}

0 comments on commit 03ac5e1

Please sign in to comment.