Skip to content

Commit

Permalink
test: Add more tests on validation
Browse files Browse the repository at this point in the history
Part of #25

All cases are covered (at least all I can think of)
  • Loading branch information
Gashmob committed Sep 22, 2024
1 parent 7c3b22a commit 8313b68
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 52 deletions.
7 changes: 5 additions & 2 deletions src/grammar/Position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ auto Position::getStartPosition() const -> std::pair<unsigned int, unsigned int>
auto Position::getEndPosition() const -> std::pair<unsigned int, unsigned int> { return _end_position; }

auto Position::getContent() const -> std::vector<std::string> {
if (_filename.empty()) {
throw std::logic_error("Filename is empty");
if (_filename.empty() || _filename == "<unknown>") {
return {};
}

std::ifstream file(_filename);
Expand Down Expand Up @@ -85,6 +85,9 @@ auto Position::dump(const std::string &color) const -> std::string {
const auto end_line = _end_position.first;
const auto end_column = _end_position.second;
const auto content = getContent();
if (content.empty()) {
return "";
}

if (content.size() == 1) { // Single line
const auto nth = " " + std::to_string(start_line) + " ";
Expand Down
5 changes: 1 addition & 4 deletions src/validation/ValidationVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,6 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable)
return;
}
variable_type = _environment->getType(variable->getTypeName());
if (variable_type == nullptr) {
return;
}
}

if (variable->getValue() != nullptr) {
Expand All @@ -144,7 +141,7 @@ auto ValidationVisitor::visitVariableDeclaration(VariableDeclaration *variable)
_context->unstack();
const auto value_type = variable->getValue()->getType();
if (value_type == nullptr) {
throw std::logic_error("Variable value has no type");
return;
}
if (variable_type != nullptr && variable_type->getName() != value_type->getName()) {
displayError("Cannot assign value of type " + value_type->toDisplay() + " to a variable of type " +
Expand Down
2 changes: 0 additions & 2 deletions tests/unit/Fixtures/validation/assignation.fil

This file was deleted.

1 change: 0 additions & 1 deletion tests/unit/Fixtures/validation/binary.fil

This file was deleted.

2 changes: 0 additions & 2 deletions tests/unit/Fixtures/validation/boolean.fil

This file was deleted.

2 changes: 0 additions & 2 deletions tests/unit/Fixtures/validation/character.fil

This file was deleted.

2 changes: 0 additions & 2 deletions tests/unit/Fixtures/validation/float.fil

This file was deleted.

2 changes: 0 additions & 2 deletions tests/unit/Fixtures/validation/identifier.fil

This file was deleted.

2 changes: 0 additions & 2 deletions tests/unit/Fixtures/validation/integer.fil

This file was deleted.

2 changes: 0 additions & 2 deletions tests/unit/Fixtures/validation/invalid_program.fil

This file was deleted.

2 changes: 0 additions & 2 deletions tests/unit/Fixtures/validation/string.fil

This file was deleted.

2 changes: 0 additions & 2 deletions tests/unit/Fixtures/validation/valid_program.fil

This file was deleted.

1 change: 0 additions & 1 deletion tests/unit/Fixtures/validation/variable.fil

This file was deleted.

4 changes: 2 additions & 2 deletions tests/unit/grammar/PositionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ TEST(Position, defaultConstructor) {
ASSERT_THAT(position.getStartPosition(), Pair(0, 0));
ASSERT_THAT(position.getEndPosition(), Pair(0, 0));
ASSERT_STREQ("", position.getFilename().c_str());
ASSERT_THROW(position.getContent(), std::logic_error);
ASSERT_THROW(position.dump(""), std::logic_error);
ASSERT_THAT(position.getContent(), IsEmpty());
ASSERT_STREQ("", position.dump("").c_str());
}

TEST(Position, tokenConstructor) {
Expand Down
175 changes: 151 additions & 24 deletions tests/unit/validation/ValidationVisitorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "test_tools.h"
#include <filc/grammar/Parser.h>
#include <filc/grammar/expression/Expression.h>
#include <filc/grammar/program/Program.h>
#include <filc/validation/ValidationVisitor.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
Expand All @@ -37,97 +37,224 @@ using namespace ::testing;

#define VALIDATION_FIXTURES FIXTURES_PATH "/validation"

TEST(ValidationVisitor, visitProgram_valid) {
TEST(ValidationVisitor, program_valid) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/valid_program.fil");
const auto program = parseString("0");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
}

TEST(ValidationVisitor, visitProgram_invalid) {
TEST(ValidationVisitor, program_invalid) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/invalid_program.fil");
const auto program = parseString("'a'");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("Expected type int aka i32 but got char* aka u8*"));
HasSubstr("Expected type int aka i32 but got char"));
ASSERT_TRUE(visitor.hasError());
}

TEST(ValidationVisitor, visitBooleanLiteral) {
TEST(ValidationVisitor, boolean) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/boolean.fil");
const auto program = parseString("true\n0");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("Boolean value not used"));
ASSERT_FALSE(visitor.hasError());
}

TEST(ValidationVisitor, visitIntegerLiteral) {
TEST(ValidationVisitor, integer) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/integer.fil");
const auto program = parseString("2\n0");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("Integer value not used"));
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

TEST(ValidationVisitor, visitFloatLiteral) {
TEST(ValidationVisitor, float) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/float.fil");
const auto program = parseString("3.14\n0");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("Float value not used"));
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("f64", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

TEST(ValidationVisitor, visitCharacterLiteral) {
TEST(ValidationVisitor, character) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/character.fil");
const auto program = parseString("'a'\n0");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("Character value not used"));
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("char", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

TEST(ValidationVisitor, visitStringLiteral) {
TEST(ValidationVisitor, string) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/string.fil");
const auto program = parseString("\"hello\"\n0");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("String value not used"));
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("char*", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

TEST(ValidationVisitor, visitVariableDeclaration) {
TEST(ValidationVisitor, variable_alreadyDefined) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/variable.fil");
const auto program = parseString("val my_constant = 2\nval my_constant = 3");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("my_constant is already defined"));
ASSERT_TRUE(visitor.hasError());
ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str());
ASSERT_EQ(nullptr, program->getExpressions()[1]->getType());
}

TEST(ValidationVisitor, variable_constantWithoutValue) {
VISITOR;
const auto program = parseString("val my_constant");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("When declaring a constant, you must provide it a value"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[0]->getType());
}

TEST(ValidationVisitor, variable_unknowType) {
VISITOR;
const auto program = parseString("var my_var: foo");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("Unknown type: foo"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[0]->getType());
}

TEST(ValidationVisitor, variable_differentValueType) {
VISITOR;
const auto program = parseString("var my_var: i32 = 'a'");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("Cannot assign value of type char aka u8 to a variable of type i32"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[0]->getType());
}

TEST(ValidationVisitor, variable_assignAliasType) {
VISITOR;
const auto program = parseString("var my_var: u8 = 'a'\n0");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("u8", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

TEST(ValidationVisitor, variable_withoutTypeAndValue) {
VISITOR;
const auto program = parseString("var my_var");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("When declaring a variable, you must provide at least a type or a value"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[0]->getType());
}

TEST(ValidationVisitor, variable_valid) {
VISITOR;
const auto program = parseString("val foo: i32 = 45");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("i32", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

TEST(ValidationVisitor, visitIdentifier) {
TEST(ValidationVisitor, identifier_nonExisting) {
VISITOR;
const auto program = parseString("bar");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("Unknown name, don't know what it refers to: bar"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[0]->getType());
}

TEST(ValidationVisitor, identifier_valid) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/identifier.fil");
const auto program = parseString("val foo = 1\nfoo");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("int", program->getExpressions()[1]->getType()->getDisplayName().c_str());
}

TEST(ValidationVisitor, visitBinaryCalcul) {
TEST(ValidationVisitor, calcul_invalidLeft) {
VISITOR;
const auto program = parseString("(val foo) + 2");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("When declaring a constant, you must provide it a value"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[0]->getType());
}

TEST(ValidationVisitor, calcul_invalidRight) {
VISITOR;
const auto program = parseString("2 + (val foo)");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("When declaring a constant, you must provide it a value"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[0]->getType());
}

TEST(ValidationVisitor, calcul_invalid) {
VISITOR;
const auto program = parseString("'a' && 3");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("You cannot use operator && with char aka u8 and int aka i32"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[0]->getType());
}

TEST(ValidationVisitor, calcul_valid) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/binary.fil");
const auto program = parseString("2 + 2");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
ASSERT_STREQ("int", program->getExpressions()[0]->getType()->getDisplayName().c_str());
}

TEST(ValidationVisitor, visitAssignation) {
TEST(ValidationVisitor, assignation_nonExisting) {
VISITOR;
const auto program = parseString("foo = 3");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("Unknown name, don't know what it refers to: foo"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[0]->getType());
}

TEST(ValidationVisitor, assignation_constant) {
VISITOR;
const auto program = parseString("val foo = 3\nfoo = 4");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), HasSubstr("Cannot modify a constant"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[1]->getType());
}

TEST(ValidationVisitor, assignation_differentType) {
VISITOR;
const auto program = parseString("var foo = 3\nfoo = false");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}),
HasSubstr("Cannot assign value of type bool to a variable of type int aka i32"));
ASSERT_TRUE(visitor.hasError());
ASSERT_EQ(nullptr, program->getExpressions()[1]->getType());
}

TEST(ValidationVisitor, assignation_valid) {
VISITOR;
const auto program = filc::ParserProxy::parse(VALIDATION_FIXTURES "/assignation.fil");
const auto program = parseString("var foo = 3\nfoo = 2");
program->accept(&visitor);
ASSERT_THAT(std::string(std::istreambuf_iterator<char>(ss), {}), IsEmpty());
ASSERT_FALSE(visitor.hasError());
Expand Down

0 comments on commit 8313b68

Please sign in to comment.