diff options
Diffstat (limited to 'src/ast/npc_test.cpp')
-rw-r--r-- | src/ast/npc_test.cpp | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/src/ast/npc_test.cpp b/src/ast/npc_test.cpp new file mode 100644 index 0000000..2697351 --- /dev/null +++ b/src/ast/npc_test.cpp @@ -0,0 +1,569 @@ +#include "npc.hpp" +// ast/npc_test.cpp - Testsuite for npc parser. +// +// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// 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 <http://www.gnu.org/licenses/>. + +#include <gtest/gtest.h> + +#include "../tests/fdhack.hpp" + +//#include "../poison.hpp" + + +namespace tmwa +{ +namespace npc +{ +namespace parse +{ + static + io::FD string_pipe(ZString sz) + { + io::FD rfd, wfd; + if (-1 == io::FD::pipe(rfd, wfd)) + return io::FD(); + if (sz.size() != wfd.write(sz.c_str(), sz.size())) + { + rfd.close(); + wfd.close(); + return io::FD(); + } + wfd.close(); + return rfd; + } + +#define EXPECT_SPAN(span, bl,bc, el,ec) \ + ({ \ + EXPECT_EQ((span).begin.line, bl); \ + EXPECT_EQ((span).begin.column, bc); \ + EXPECT_EQ((span).end.line, el); \ + EXPECT_EQ((span).end.column, ec); \ + }) + + TEST(ast, eof) + { + QuietFd q; + LString inputs[] = + { + ""_s, + "\n"_s, + "\n\n"_s, + }; + for (auto input : inputs) + { + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_EQ(res.get_success(), Some(std::unique_ptr<TopLevel>(nullptr))); + } + } + TEST(ast, comment) + { + QuietFd q; + LString inputs[] = + { + //23456789 + "// hello"_s, + "// hello\n "_s, + "// hello\nabc"_s, + }; + for (auto input : inputs) + { + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_TRUE(res.get_success().is_some()); + auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); + EXPECT_SPAN(top->span, 1,1, 1,8); + auto p = dynamic_cast<Comment *>(top.get()); + EXPECT_TRUE(p); + if (p) + { + EXPECT_EQ(p->comment, "// hello"_s); + } + } + } + TEST(ast, warp) + { + QuietFd q; + LString inputs[] = + { + // 1 2 3 4 + //234567890123456789012345678901234567890123456789 + "map.gat,1,2|warp|To Other Map|3,4,other.gat,5,6"_s, + "map.gat,1,2|warp|To Other Map|3,4,other.gat,5,6\n"_s, + "map.gat,1,2|warp|To Other Map|3,4,other.gat,5,6{"_s, + // no optional fields in warp + }; + for (auto input : inputs) + { + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_TRUE(res.get_success().is_some()); + auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); + EXPECT_SPAN(top->span, 1,1, 1,47); + auto p = dynamic_cast<Warp *>(top.get()); + EXPECT_TRUE(p); + if (p) + { + EXPECT_SPAN(p->m.span, 1,1, 1,7); + EXPECT_EQ(p->m.data, stringish<MapName>("map"_s)); + EXPECT_SPAN(p->x.span, 1,9, 1,9); + EXPECT_EQ(p->x.data, 1); + EXPECT_SPAN(p->y.span, 1,11, 1,11); + EXPECT_EQ(p->y.data, 2); + EXPECT_SPAN(p->key_span, 1,13, 1,16); + EXPECT_SPAN(p->name.span, 1,18, 1,29); + EXPECT_EQ(p->name.data, stringish<NpcName>("To Other Map"_s)); + EXPECT_SPAN(p->xs.span, 1,31, 1,31); + EXPECT_EQ(p->xs.data, 3); + EXPECT_SPAN(p->ys.span, 1,33, 1,33); + EXPECT_EQ(p->ys.data, 4); + EXPECT_SPAN(p->to_m.span, 1,35, 1,43); + EXPECT_EQ(p->to_m.data, stringish<MapName>("other"_s)); + EXPECT_SPAN(p->to_x.span, 1,45, 1,45); + EXPECT_EQ(p->to_x.data, 5); + EXPECT_SPAN(p->to_y.span, 1,47, 1,47); + EXPECT_EQ(p->to_y.data, 6); + } + } + } + TEST(ast, shop) + { + QuietFd q; + LString inputs[] = + { + // 1 2 3 4 5 + //2345678901234567890123456789012345678901234567890123456789 + "map.gat,1,2,3|shop|Flower Shop|4,5:6,Named:7,Spaced :8"_s, + "map.gat,1,2,3|shop|Flower Shop|4,5:6,Named:7,Spaced :8\n"_s, + "map.gat,1,2,3|shop|Flower Shop|4,5:6,Named:7,Spaced :8{"_s, + // no optional fields in shop + }; + for (auto input : inputs) + { + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_TRUE(res.get_success().is_some()); + auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); + EXPECT_SPAN(top->span, 1,1, 1,54); + auto p = dynamic_cast<Shop *>(top.get()); + EXPECT_TRUE(p); + if (p) + { + EXPECT_SPAN(p->m.span, 1,1, 1,7); + EXPECT_EQ(p->m.data, stringish<MapName>("map"_s)); + EXPECT_SPAN(p->x.span, 1,9, 1,9); + EXPECT_EQ(p->x.data, 1); + EXPECT_SPAN(p->y.span, 1,11, 1,11); + EXPECT_EQ(p->y.data, 2); + EXPECT_SPAN(p->d.span, 1,13, 1,13); + EXPECT_EQ(p->d.data, DIR::NW); + EXPECT_SPAN(p->key_span, 1,15, 1,18); + EXPECT_SPAN(p->name.span, 1,20, 1,30); + EXPECT_EQ(p->name.data, stringish<NpcName>("Flower Shop"_s)); + EXPECT_SPAN(p->npc_class.span, 1,32, 1,32); + EXPECT_EQ(p->npc_class.data, wrap<Species>(4)); + EXPECT_SPAN(p->items.span, 1,34, 1,54); + EXPECT_EQ(p->items.data.size(), 3); + EXPECT_SPAN(p->items.data[0].span, 1,34, 1,36); + EXPECT_SPAN(p->items.data[0].data.name.span, 1,34, 1,34); + EXPECT_EQ(p->items.data[0].data.name.data, stringish<ItemName>("5"_s)); + EXPECT_SPAN(p->items.data[0].data.value.span, 1,36, 1,36); + EXPECT_EQ(p->items.data[0].data.value.data, 6); + EXPECT_SPAN(p->items.data[1].span, 1,38, 1,44); + EXPECT_SPAN(p->items.data[1].data.name.span, 1,38, 1,42); + EXPECT_EQ(p->items.data[1].data.name.data, stringish<ItemName>("Named"_s)); + EXPECT_SPAN(p->items.data[1].data.value.span, 1,44, 1,44); + EXPECT_EQ(p->items.data[1].data.value.data, 7); + EXPECT_SPAN(p->items.data[2].span, 1,46, 1,54); + EXPECT_SPAN(p->items.data[2].data.name.span, 1,46, 1,52); + EXPECT_EQ(p->items.data[2].data.name.data, stringish<ItemName>("Spaced"_s)); + EXPECT_SPAN(p->items.data[2].data.value.span, 1,54, 1,54); + EXPECT_EQ(p->items.data[2].data.value.data, 8); + } + } + } + TEST(ast, monster) + { + QuietFd q; + LString inputs[] = + { + // 1 2 3 4 5 6 + //23456789012345678901234567890123456789012345678901234567890123456789 + "map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000,Npc::Event"_s, + "map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000,Npc::Event\n"_s, + "map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000,Npc::Event{"_s, + "Map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000"_s, + "Map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000\n"_s, + "Map.gat,1,2,3,4|monster|Feeping Creature|5,6,7000,8000{"_s, + "nap.gat,1,20304|monster|Feeping Creature|506,700008000"_s, + "nap.gat,1,20304|monster|Feeping Creature|506,700008000\n"_s, + "nap.gat,1,20304|monster|Feeping Creature|506,700008000{"_s, + }; + for (auto input : inputs) + { + bool first = input.startswith('m'); + bool second = input.startswith('M'); + bool third = input.startswith('n'); + assert(first + second + third == 1); + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_TRUE(res.get_success().is_some()); + auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); + EXPECT_SPAN(top->span, 1,1, 1,first?65:54); + auto p = dynamic_cast<Monster *>(top.get()); + EXPECT_TRUE(p); + if (p) + { + EXPECT_SPAN(p->m.span, 1,1, 1,7); + if (first) + { + EXPECT_EQ(p->m.data, stringish<MapName>("map"_s)); + } + else if (second) + { + EXPECT_EQ(p->m.data, stringish<MapName>("Map"_s)); + } + else + { + EXPECT_EQ(p->m.data, stringish<MapName>("nap"_s)); + } + EXPECT_SPAN(p->x.span, 1,9, 1,9); + EXPECT_EQ(p->x.data, 1); + if (!third) + { + EXPECT_SPAN(p->y.span, 1,11, 1,11); + EXPECT_EQ(p->y.data, 2); + EXPECT_SPAN(p->xs.span, 1,13, 1,13); + EXPECT_EQ(p->xs.data, 3); + EXPECT_SPAN(p->ys.span, 1,15, 1,15); + EXPECT_EQ(p->ys.data, 4); + } + else + { + EXPECT_SPAN(p->y.span, 1,11, 1,15); + EXPECT_EQ(p->y.data, 20304); + EXPECT_SPAN(p->xs.span, 1,16, 1,16); + EXPECT_EQ(p->xs.data, 0); + EXPECT_SPAN(p->ys.span, 1,16, 1,16); + EXPECT_EQ(p->ys.data, 0); + } + EXPECT_SPAN(p->key_span, 1,17, 1,23); + EXPECT_SPAN(p->name.span, 1,25, 1,40); + EXPECT_EQ(p->name.data, stringish<MobName>("Feeping Creature"_s)); + if (!third) + { + EXPECT_SPAN(p->mob_class.span, 1,42, 1,42); + EXPECT_EQ(p->mob_class.data, wrap<Species>(5)); + EXPECT_SPAN(p->num.span, 1,44, 1,44); + EXPECT_EQ(p->num.data, 6); + EXPECT_SPAN(p->delay1.span, 1,46, 1,49); + EXPECT_EQ(p->delay1.data, 7_s); + EXPECT_SPAN(p->delay2.span, 1,51, 1,54); + EXPECT_EQ(p->delay2.data, 8_s); + } + else + { + EXPECT_SPAN(p->mob_class.span, 1,42, 1,44); + EXPECT_EQ(p->mob_class.data, wrap<Species>(506)); + EXPECT_SPAN(p->num.span, 1,46, 1,54); + EXPECT_EQ(p->num.data, 700008000); + EXPECT_SPAN(p->delay1.span, 1,55, 1,55); + EXPECT_EQ(p->delay1.data, 0_s); + EXPECT_SPAN(p->delay2.span, 1,55, 1,55); + EXPECT_EQ(p->delay2.data, 0_s); + } + if (first) + { + EXPECT_SPAN(p->event.span, 1,56, 1,65); + EXPECT_EQ(p->event.data.npc, stringish<NpcName>("Npc"_s)); + EXPECT_EQ(p->event.data.label, stringish<ScriptLabel>("Event"_s)); + } + else + { + EXPECT_SPAN(p->event.span, 1,55, 1,55); + EXPECT_EQ(p->event.data.npc, NpcName()); + EXPECT_EQ(p->event.data.label, ScriptLabel()); + } + } + } + } + TEST(ast, mapflag) + { + QuietFd q; + LString inputs[] = + { + // 1 2 3 + //23456789012345678901234567890123456789 + "map.gat|mapflag|flagname"_s, + "map.gat|mapflag|flagname\n"_s, + "map.gat|mapflag|flagname{"_s, + "Map.gat|mapflag|flagname|optval"_s, + "Map.gat|mapflag|flagname|optval\n"_s, + "Map.gat|mapflag|flagname|optval{"_s, + }; + for (auto input : inputs) + { + bool second = input.startswith('M'); + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_TRUE(res.get_success().is_some()); + auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); + EXPECT_SPAN(top->span, 1,1, 1,!second?24:31); + auto p = dynamic_cast<MapFlag *>(top.get()); + EXPECT_TRUE(p); + if (p) + { + EXPECT_SPAN(p->m.span, 1,1, 1,7); + if (!second) + { + EXPECT_EQ(p->m.data, stringish<MapName>("map"_s)); + } + else + { + EXPECT_EQ(p->m.data, stringish<MapName>("Map"_s)); + } + EXPECT_SPAN(p->key_span, 1,9, 1,15); + EXPECT_SPAN(p->name.span, 1,17, 1,24); + EXPECT_EQ(p->name.data, "flagname"_s); + if (!second) + { + EXPECT_SPAN(p->opt_extra.span, 1,25, 1,25); + EXPECT_EQ(p->opt_extra.data, ""_s); + } + else + { + EXPECT_SPAN(p->opt_extra.span, 1,26, 1,31); + EXPECT_EQ(p->opt_extra.data, "optval"_s); + } + } + } + } + + TEST(ast, scriptfun) + { + QuietFd q; + LString inputs[] = + { + // 1 2 3 + //23456789012345678901234567890123456789 + "function|script|Fun Name{end;}"_s, + // 123456 + "function|script|Fun Name\n{end;}\n"_s, + // 1234567 + "function|script|Fun Name\n \n {end;} "_s, + }; + for (auto input : inputs) + { + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_TRUE(res.get_success().is_some()); + auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); + EXPECT_SPAN(top->span, 1,1, 1,24); + auto p = dynamic_cast<ScriptFunction *>(top.get()); + EXPECT_TRUE(p); + if (p) + { + EXPECT_SPAN(p->key1_span, 1,1, 1,8); + EXPECT_SPAN(p->key_span, 1,10, 1,15); + EXPECT_SPAN(p->name.span, 1,17, 1,24); + EXPECT_EQ(p->name.data, "Fun Name"_s); + if (input.endswith('}')) + { + EXPECT_SPAN(p->body.span, 1,25, 1,30); + } + else if (input.endswith('\n')) + { + EXPECT_SPAN(p->body.span, 2,1, 2,6); + } + else if (input.endswith(' ')) + { + EXPECT_SPAN(p->body.span, 3,2, 3,7); + } + else + { + FAIL(); + } + EXPECT_EQ(p->body.braced_body, "{end;}"_s); + } + } + } + TEST(ast, scriptnone) + { + QuietFd q; + LString inputs[] = + { + // 1 2 3 + //23456789012345678901234567890123456789 + "-|script|#config|-1{end;}"_s, + // 123456 + "-|script|#config|-1\n{end;}\n"_s, + // 1234567 + "-|script|#config|-1\n \n {end;} "_s, + }; + for (auto input : inputs) + { + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_TRUE(res.get_success().is_some()); + auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); + EXPECT_SPAN(top->span, 1,1, 1,19); + auto p = dynamic_cast<ScriptNone *>(top.get()); + EXPECT_TRUE(p); + if (p) + { + EXPECT_SPAN(p->key1_span, 1,1, 1,1); + EXPECT_SPAN(p->key_span, 1,3, 1,8); + EXPECT_SPAN(p->name.span, 1,10, 1,16); + EXPECT_EQ(p->name.data, stringish<NpcName>("#config"_s)); + EXPECT_SPAN(p->key4_span, 1,18, 1,19); + if (input.endswith('}')) + { + EXPECT_SPAN(p->body.span, 1,20, 1,25); + } + else if (input.endswith('\n')) + { + EXPECT_SPAN(p->body.span, 2,1, 2,6); + } + else if (input.endswith(' ')) + { + EXPECT_SPAN(p->body.span, 3,2, 3,7); + } + else + { + FAIL(); + } + EXPECT_EQ(p->body.braced_body, "{end;}"_s); + } + } + } + TEST(ast, scriptmapnone) + { + QuietFd q; + LString inputs[] = + { + // 1 2 3 + //23456789012345678901234567890123456789 + "map.gat,1,2,3|script|Init|-1{end;}"_s, + "map.gat,1,2,3|script|Init|-1\n{end;}\n"_s, + "map.gat,1,2,3|script|Init|-1\n \n {end;} "_s, + }; + for (auto input : inputs) + { + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_TRUE(res.get_success().is_some()); + auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); + EXPECT_SPAN(top->span, 1,1, 1,28); + auto p = dynamic_cast<ScriptMapNone *>(top.get()); + EXPECT_TRUE(p); + if (p) + { + EXPECT_SPAN(p->m.span, 1,1, 1,7); + EXPECT_EQ(p->m.data, stringish<MapName>("map"_s)); + EXPECT_SPAN(p->x.span, 1,9, 1,9); + EXPECT_EQ(p->x.data, 1); + EXPECT_SPAN(p->y.span, 1,11, 1,11); + EXPECT_EQ(p->y.data, 2); + EXPECT_SPAN(p->d.span, 1,13, 1,13); + EXPECT_EQ(p->d.data, DIR::NW); + EXPECT_SPAN(p->key_span, 1,15, 1,20); + EXPECT_SPAN(p->name.span, 1,22, 1,25); + EXPECT_EQ(p->name.data, stringish<NpcName>("Init"_s)); + EXPECT_SPAN(p->key4_span, 1,27, 1,28); + if (input.endswith('}')) + { + EXPECT_SPAN(p->body.span, 1,29, 1,34); + } + else if (input.endswith('\n')) + { + EXPECT_SPAN(p->body.span, 2,1, 2,6); + } + else if (input.endswith(' ')) + { + EXPECT_SPAN(p->body.span, 3,2, 3,7); + } + else + { + FAIL(); + } + EXPECT_EQ(p->body.braced_body, "{end;}"_s); + } + } + } + TEST(ast, scriptmap) + { + QuietFd q; + LString inputs[] = + { + // 1 2 3 + //23456789012345678901234567890123456789 + "map.gat,1,2,3|script|Asdf|4,5,6{end;}"_s, + "map.gat,1,2,3|script|Asdf|4,5,6\n{end;}\n"_s, + "map.gat,1,2,3|script|Asdf|4,5,6\n \n {end;} "_s, + }; + for (auto input : inputs) + { + io::LineCharReader lr("<string>"_s, string_pipe(input)); + auto res = parse_top(lr); + EXPECT_TRUE(res.get_success().is_some()); + auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); + EXPECT_SPAN(top->span, 1,1, 1,31); + auto p = dynamic_cast<ScriptMap *>(top.get()); + EXPECT_TRUE(p); + if (p) + { + EXPECT_SPAN(p->m.span, 1,1, 1,7); + EXPECT_EQ(p->m.data, stringish<MapName>("map"_s)); + EXPECT_SPAN(p->x.span, 1,9, 1,9); + EXPECT_EQ(p->x.data, 1); + EXPECT_SPAN(p->y.span, 1,11, 1,11); + EXPECT_EQ(p->y.data, 2); + EXPECT_SPAN(p->d.span, 1,13, 1,13); + EXPECT_EQ(p->d.data, DIR::NW); + EXPECT_SPAN(p->key_span, 1,15, 1,20); + EXPECT_SPAN(p->name.span, 1,22, 1,25); + EXPECT_EQ(p->name.data, stringish<NpcName>("Asdf"_s)); + EXPECT_SPAN(p->npc_class.span, 1,27, 1,27); + EXPECT_EQ(p->npc_class.data, wrap<Species>(4)); + EXPECT_SPAN(p->xs.span, 1,29, 1,29); + EXPECT_EQ(p->xs.data, 5); + EXPECT_SPAN(p->ys.span, 1,31, 1,31); + EXPECT_EQ(p->ys.data, 6); + if (input.endswith('}')) + { + EXPECT_SPAN(p->body.span, 1,32, 1,37); + } + else if (input.endswith('\n')) + { + EXPECT_SPAN(p->body.span, 2,1, 2,6); + } + else if (input.endswith(' ')) + { + EXPECT_SPAN(p->body.span, 3,2, 3,7); + } + else + { + FAIL(); + } + EXPECT_EQ(p->body.braced_body, "{end;}"_s); + } + } + } +} // namespace parse +} // namespace npc +} // namespace tmwa |