#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 "../io/line.hpp" #include "../tests/fdhack.hpp" #include "../poison.hpp" namespace tmwa { namespace ast { namespace npc { #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(npcast, eof) { QuietFd q; LString inputs[] = { ""_s, "\n"_s, "\n\n"_s, }; for (auto input : inputs) { io::LineCharReader lr(io::from_string, ""_s, input); auto res = parse_top(lr); EXPECT_TRUE(res.is_none()); } } TEST(npcast, comment) { QuietFd q; LString inputs[] = { //23456789 "// hello"_s, "// hello\n "_s, "// hello\nabc"_s, }; for (auto input : inputs) { io::LineCharReader lr(io::from_string, ""_s, input); auto res = TRY_UNWRAP(parse_top(lr), FAIL()); 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 = top.get_if(); EXPECT_TRUE(p); if (p) { EXPECT_EQ(p->comment, "// hello"_s); } } } TEST(npcast, warp) { QuietFd q; LString inputs[] = { // 1 2 3 4 //234567890123456789012345678901234567890123456789 "map.gat,1,2|warp|3,4,other.gat,7,8"_s, "map.gat,1,2|warp|3,4,other.gat,7,8\n"_s, "map.gat,1,2|warp|3,4,other.gat,7,8{"_s, // no optional fields in warp }; for (auto input : inputs) { io::LineCharReader lr(io::from_string, ""_s, input); auto res = TRY_UNWRAP(parse_top(lr), FAIL()); EXPECT_TRUE(res.get_success().is_some()); auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); EXPECT_SPAN(top.span, 1,1, 1,34); auto p = top.get_if(); 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->xs.span, 1,18, 1,18); EXPECT_EQ(p->xs.data, 5); EXPECT_SPAN(p->ys.span, 1,20, 1,20); EXPECT_EQ(p->ys.data, 6); EXPECT_SPAN(p->to_m.span, 1,22, 1,30); EXPECT_EQ(p->to_m.data, stringish("other"_s)); EXPECT_SPAN(p->to_x.span, 1,32, 1,32); EXPECT_EQ(p->to_x.data, 7); EXPECT_SPAN(p->to_y.span, 1,34, 1,34); EXPECT_EQ(p->to_y.data, 8); } } } TEST(npcast, 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(io::from_string, ""_s, input); auto res = TRY_UNWRAP(parse_top(lr), FAIL()); EXPECT_TRUE(res.get_success().is_some()); auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL()); EXPECT_SPAN(top.span, 1,1, 1,55); auto p = top.get_if(); 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,55); 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_EQ(p->items.data[0].data.value_multiply, false); 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_EQ(p->items.data[1].data.value_multiply, false); 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,55); 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_EQ(p->items.data[2].data.value_multiply, true); EXPECT_SPAN(p->items.data[2].data.value.span, 1,55, 1,55); EXPECT_EQ(p->items.data[2].data.value.data, 8); } } } TEST(npcast, 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(io::from_string, ""_s, input); auto res = TRY_UNWRAP(parse_top(lr), FAIL()); 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 = top.get_if(); 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(npcast, 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, "nap.gat|mapflag|flagname|aa,b,c"_s, "nap.gat|mapflag|flagname|aa,b,c\n"_s, "nap.gat|mapflag|flagname|aa,b,c{"_s, }; for (auto input : inputs) { bool first = input.startswith('m'); bool second = input.startswith('M'); bool third = input.startswith('n'); EXPECT_EQ(first + second + third, 1); io::LineCharReader lr(io::from_string, ""_s, input); auto res = TRY_UNWRAP(parse_top(lr), FAIL()); 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?24:31); auto p = top.get_if(); EXPECT_TRUE(p); if (p) { EXPECT_SPAN(p->m.span, 1,1, 1,7); if (first) { EXPECT_EQ(p->m.data, stringish("map"_s)); } if (second) { EXPECT_EQ(p->m.data, stringish("Map"_s)); } if (third) { EXPECT_EQ(p->m.data, stringish("nap"_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 (first) { EXPECT_SPAN(p->vec_extra.span, 1,25, 1,25); EXPECT_EQ(p->vec_extra.data.size(), 0); } if (second) { EXPECT_SPAN(p->vec_extra.span, 1,26, 1,31); EXPECT_EQ(p->vec_extra.data.size(), 1); EXPECT_SPAN(p->vec_extra.data[0].span, 1,26, 1,31); EXPECT_EQ(p->vec_extra.data[0].data, "optval"_s); } if (third) { EXPECT_SPAN(p->vec_extra.span, 1,26, 1,31); EXPECT_EQ(p->vec_extra.data.size(), 3); EXPECT_SPAN(p->vec_extra.data[0].span, 1,26, 1,27); EXPECT_EQ(p->vec_extra.data[0].data, "aa"_s); EXPECT_SPAN(p->vec_extra.data[1].span, 1,29, 1,29); EXPECT_EQ(p->vec_extra.data[1].data, "b"_s); EXPECT_SPAN(p->vec_extra.data[2].span, 1,31, 1,31); EXPECT_EQ(p->vec_extra.data[2].data, "c"_s); } } } } TEST(npcast, 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(io::from_string, ""_s, input); auto res = TRY_UNWRAP(parse_top(lr), FAIL()); 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 script = top.get_if