#include "npc.hpp" // ast/npc_test.cpp - Testsuite for npc parser. // // Copyright © 2014 Ben Longbons // // 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 . #include #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(""_s, string_pipe(input)); auto res = parse_top(lr); EXPECT_EQ(res.get_success(), Some(std::unique_ptr(nullptr))); } } TEST(ast, comment) { QuietFd q; LString inputs[] = { //23456789 "// hello"_s, "// hello\n "_s, "// hello\nabc"_s, }; for (auto input : inputs) { io::LineCharReader lr(""_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(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(""_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(top.get()); EXPECT_TRUE(p); if (p) { EXPECT_SPAN(p->m.span, 1,1, 1,7); EXPECT_EQ(p->m.data, stringish("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("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("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(""_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(top.get()); EXPECT_TRUE(p); if (p) { EXPECT_SPAN(p->m.span, 1,1, 1,7); EXPECT_EQ(p->m.data, stringish("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("Flower Shop"_s)); EXPECT_SPAN(p->npc_class.span, 1,32, 1,32); EXPECT_EQ(p->npc_class.data, wrap(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("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("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("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(""_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(top.get()); EXPECT_TRUE(p); if (p) { EXPECT_SPAN(p->m.span, 1,1, 1,7); if (first) { EXPECT_EQ(p->m.data, stringish("map"_s)); } else if (second) { EXPECT_EQ(p->m.data, stringish("Map"_s)); } else { EXPECT_EQ(p->m.data, stringish("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("Feeping Creature"_s)); if (!third) { EXPECT_SPAN(p->mob_class.span, 1,42, 1,42); EXPECT_EQ(p->mob_class.data, wrap(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(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("Npc"_s)); EXPECT_EQ(p->event.data.label, stringish("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(""_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(top.get()); EXPECT_TRUE(p); if (p) { EXPECT_SPAN(p->m.span, 1,1, 1,7); if (!second) { EXPECT_EQ(p->m.data, stringish("map"_s)); } else { EXPECT_EQ(p->m.data, stringish("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(""_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(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(""_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(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("#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(""_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(top.get()); EXPECT_TRUE(p); if (p) { EXPECT_SPAN(p->m.span, 1,1, 1,7); EXPECT_EQ(p->m.data, stringish("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("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(""_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(top.get()); EXPECT_TRUE(p); if (p) { EXPECT_SPAN(p->m.span, 1,1, 1,7); EXPECT_EQ(p->m.data, stringish("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("Asdf"_s)); EXPECT_SPAN(p->npc_class.span, 1,27, 1,27); EXPECT_EQ(p->npc_class.data, wrap(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