diff --git a/.travis.yml b/.travis.yml index de62c2200..dd3f469ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ addons: - libncurses5-dev - libsqlite3-dev - libxml2-dev + - valgrind os: - linux - osx diff --git a/scripts/tests/study.lua b/scripts/tests/study.lua index e5ab84018..9185535d7 100644 --- a/scripts/tests/study.lua +++ b/scripts/tests/study.lua @@ -6,13 +6,19 @@ function setup() conf = [[{ "races" : { "human" : {} }, "terrains" : { "plain" : { "flags" : [ "land" ] } }, - "keywords" : { "de" : { "study": "LERNEN" } }, - "skills" : { "de": { "alchemy" : "Alchemie", "crossbow" : "Armbrust" } }, + "keywords" : { "de" : { "study": "LERNEN", "teach": "LEHREN" } }, + "skills" : { "de": { + "tactics" : "Taktik", + "alchemy" : "Alchemie", + "crossbow" : "Armbrust" + } }, "spells" : { "fireball" : { "syntax" : "u+" } } }]] eressea.game.reset() eressea.config.reset(); eressea.settings.set('rules.magic.playerschools', '') + eressea.settings.set("rules.economy.food", "4") + eressea.settings.set('study.random_progress', '0') eressea.config.parse(conf) end @@ -51,3 +57,62 @@ function test_unit_spells() assert_equal("fireball", sp.name) assert_equal(2, sp.level) end + +local function make_teacher(student, f, skill) + f = f or student.faction + local u = unit.create(f, student.region, 1) + u:clear_orders() + u:add_order("LEHRE " .. itoa36(student.id)) + u:set_skill(skill or "crossbow", 10) + return u +end + +local function make_student(f, r, num, skill) + local u = unit.create(f, r, num or 1) + u:clear_orders() + u:add_order("LERNE " .. (skill or "Armbrust")) + return u +end + +function test_study_no_teacher() + local r = region.create(0, 0, "plain") + local f = faction.create("test@example.com", "human", "de") + local u1 = make_student(f, r, 1) + u1:set_skill("crossbow", 1) + process_orders() + assert_equal(1, u1:get_skill("crossbow")) +end + +function test_study_with_teacher() + local r = region.create(0, 0, "plain") + local f = faction.create("test@example.com", "human", "de") + local u1 = make_student(f, r, 1) + + make_teacher(u1) + u1:set_skill("crossbow", 1) + process_orders() + assert_equal(2, u1:get_skill("crossbow")) +end + +function test_study_too_many_students() + local r = region.create(0, 0, "plain") + local f = faction.create("test@example.com", "human", "de") + local u1 = make_student(f, r, 20, "Taktik") + u1.name = "Student" + u1:add_item("money", 201*u1.number) + make_teacher(u1, f, "tactics") + process_orders() + assert_equal(u1.number, u1:get_item("money")) +end + +function test_study_multiple_teachers() + local r = region.create(0, 0, "plain") + local f = faction.create("test@example.com", "human", "de") + local u1 = make_student(f, r, 20, "Taktik") + u1.name = "Student" + u1:add_item("money", 201*u1.number) + make_teacher(u1, f, "tactics") + make_teacher(u1, f, "tactics") + process_orders() + assert_equal(u1.number, u1:get_item("money")) +end diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 27c85c8de..c2cd21396 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -193,6 +193,7 @@ set(TESTS_SRC skill.test.c spells.test.c spy.test.c + study.test.c upkeep.test.c spells/magicresistance.test.c ${ATTRIBUTES_TESTS} diff --git a/src/bind_process.c b/src/bind_process.c index c411e257a..2e883d38a 100755 --- a/src/bind_process.c +++ b/src/bind_process.c @@ -159,7 +159,7 @@ void process_quit(void) { void process_study(void) { process_cmd(K_TEACH, teach_cmd, PROC_LONG_ORDER); - process_cmd(K_STUDY, learn_cmd, PROC_LONG_ORDER); + process_cmd(K_STUDY, study_cmd, PROC_LONG_ORDER); } void process_movement(void) { diff --git a/src/jsreport.c b/src/jsreport.c index 6d9f710c2..44f015053 100644 --- a/src/jsreport.c +++ b/src/jsreport.c @@ -59,9 +59,11 @@ static int report_json(const char *filename, report_context * ctx, const char *c if (sr) { terrain_t ter = oldterrain(r->terrain); if (ter == NOTERRAIN) { - log_warning("report_json: %s has no terrain id\n", r->terrain->_name); + data = 1 + r->terrain->_name[0]; + } + else { + data = 1 + (int)ter; } - data = 1 + (int)ter; } } fprintf(F, "%d", data); diff --git a/src/kernel/order.c b/src/kernel/order.c index 008bfe0fe..90bea11d3 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -115,7 +115,9 @@ char* get_command(const order *ord, char *sbuffer, size_t size) { assert(str); if (text) --size; bytes = (int)strlcpy(bufp, str, size); - if (wrptr(&bufp, &size, bytes) != 0) WARN_STATIC_BUFFER(); + if (wrptr(&bufp, &size, bytes) != 0) { + WARN_STATIC_BUFFER(); + } if (text) *bufp++ = ' '; } else { diff --git a/src/kernel/terrain.c b/src/kernel/terrain.c index 36efb3591..f27fe1bd0 100644 --- a/src/kernel/terrain.c +++ b/src/kernel/terrain.c @@ -127,8 +127,7 @@ const struct terrain_type *newterrain(terrain_t t) terrain_t oldterrain(const struct terrain_type * terrain) { terrain_t t; - if (terrain == NULL) - return NOTERRAIN; + assert(terrain); for (t = 0; t != MAXTERRAINS; ++t) { if (newterrains[t] == terrain) return t; diff --git a/src/laws.c b/src/laws.c index 849a2597b..af1347bd1 100755 --- a/src/laws.c +++ b/src/laws.c @@ -4461,7 +4461,7 @@ void init_processor(void) add_proc_order(p, K_TEACH, teach_cmd, PROC_THISORDER | PROC_LONGORDER, "Lehren"); p += 10; - add_proc_order(p, K_STUDY, learn_cmd, PROC_THISORDER | PROC_LONGORDER, + add_proc_order(p, K_STUDY, study_cmd, PROC_THISORDER | PROC_LONGORDER, "Lernen"); p += 10; diff --git a/src/reports.c b/src/reports.c index c98ac1c19..639b859bf 100644 --- a/src/reports.c +++ b/src/reports.c @@ -2295,7 +2295,7 @@ static void eval_race(struct opstack **stack, const void *userdata) static void eval_order(struct opstack **stack, const void *userdata) { /* order -> string */ const struct order *ord = (const struct order *)opop(stack).v; - char buf[512]; + char buf[4096]; size_t len; variant var; diff --git a/src/study.c b/src/study.c index ec377f0f7..08bb749b2 100644 --- a/src/study.c +++ b/src/study.c @@ -222,7 +222,7 @@ bool report, int *academy) teach->teachers[index] = NULL; } else { - log_warning("MAXTEACHERS is too low at %d", MAXTEACHERS); + log_error("MAXTEACHERS=%d is too low for student %s, teacher %s", MAXTEACHERS, unitname(student), unitname(teacher)); } teach->value += n; @@ -527,7 +527,7 @@ static double study_speedup(unit * u, skill_t s, study_rule_t rule) return 1.0; } -int learn_cmd(unit * u, order * ord) +int study_cmd(unit * u, order * ord) { region *r = u->region; int p; diff --git a/src/study.h b/src/study.h index d5d664fd2..1feb55921 100644 --- a/src/study.h +++ b/src/study.h @@ -20,19 +20,20 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define H_KRNL_STUDY #include "skill.h" +#include #ifdef __cplusplus extern "C" { #endif extern int teach_cmd(struct unit *u, struct order *ord); - extern int learn_cmd(struct unit *u, struct order *ord); + extern int study_cmd(struct unit *u, struct order *ord); extern magic_t getmagicskill(const struct locale *lang); extern bool is_migrant(struct unit *u); extern int study_cost(struct unit *u, skill_t talent); -#define MAXTEACHERS 16 +#define MAXTEACHERS 20 typedef struct teaching_info { struct unit *teachers[MAXTEACHERS]; int value; diff --git a/src/study.test.c b/src/study.test.c new file mode 100644 index 000000000..58614d37c --- /dev/null +++ b/src/study.test.c @@ -0,0 +1,99 @@ +#include + +#include "study.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +typedef struct { + unit *u; + unit *teachers[2]; +} study_fixture; + +static void setup_study(study_fixture *fix, skill_t sk) { + struct region * r; + struct faction *f; + struct locale *lang; + + assert(fix); + test_cleanup(); + test_create_world(); + r = test_create_region(0, 0, 0); + f = test_create_faction(0); + lang = get_or_create_locale(locale_name(f->locale)); + locale_setstring(lang, mkname("skill", skillnames[sk]), skillnames[sk]); + init_skills(lang); + fix->u = test_create_unit(f, r); + assert(fix->u); + fix->u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[sk]); + + fix->teachers[0] = test_create_unit(f, r); + assert(fix->teachers[0]); + fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + + fix->teachers[1] = test_create_unit(f, r); + assert(fix->teachers[1]); + fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); +} + +static void test_study_no_teacher(CuTest *tc) { + study_fixture fix; + skill *sv; + + setup_study(&fix, SK_CROSSBOW); + study_cmd(fix.u, fix.u->thisorder); + CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 2, sv->weeks); + CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + test_cleanup(); +} + +static void test_study_with_teacher(CuTest *tc) { + study_fixture fix; + skill *sv; + + setup_study(&fix, SK_CROSSBOW); + set_level(fix.teachers[0], SK_CROSSBOW, TEACHDIFFERENCE); + teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); + CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + study_cmd(fix.u, fix.u->thisorder); + CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 1, sv->weeks); + test_cleanup(); +} + +static void test_study_with_bad_teacher(CuTest *tc) { + study_fixture fix; + skill *sv; + message *msg; + + setup_study(&fix, SK_CROSSBOW); + teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); + CuAssertPtrNotNull(tc, msg = test_get_last_message(fix.u->faction->msgs)); + CuAssertStrEquals(tc, "teach_asgood", test_get_messagetype(msg)); + study_cmd(fix.u, fix.u->thisorder); + CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); + CuAssertIntEquals(tc, 1, sv->level); + CuAssertIntEquals(tc, 2, sv->weeks); + test_cleanup(); +} + +CuSuite *get_study_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_study_no_teacher); + SUITE_ADD_TEST(suite, test_study_with_teacher); + SUITE_ADD_TEST(suite, test_study_with_bad_teacher); + return suite; +} diff --git a/src/test_eressea.c b/src/test_eressea.c index b5a2e2a42..e434f6c7f 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -89,6 +89,7 @@ int RunAllTests(void) RUN_TESTS(suite, vortex); RUN_TESTS(suite, wormhole); RUN_TESTS(suite, spy); + RUN_TESTS(suite, study); printf("\ntest summary: %d tests, %d failed\n", suite->count, suite->failCount); log_flags = flags; diff --git a/src/tests.c b/src/tests.c index 9c03e1e18..5a19ce9c0 100644 --- a/src/tests.c +++ b/src/tests.c @@ -209,15 +209,20 @@ void test_create_world(void) } message * test_get_last_message(message_list *msgs) { - struct mlist *iter = msgs->begin; - while (iter->next) { - iter = iter->next; + if (msgs) { + struct mlist *iter = msgs->begin; + while (iter->next) { + iter = iter->next; + } + return iter->msg; } - return iter->msg; + return 0; } const char * test_get_messagetype(const message *msg) { - const char * name = msg->type->name; + const char * name; + assert(msg); + name = msg->type->name; if (strcmp(name, "missing_message") == 0) { name = (const char *)msg->parameters[0].v; } diff --git a/tests/data/184.dat b/tests/data/184.dat index 74d72258a..ffe368510 100644 Binary files a/tests/data/184.dat and b/tests/data/184.dat differ diff --git a/tests/write-reports.sh b/tests/write-reports.sh index ec120bca8..775fdb622 100755 --- a/tests/write-reports.sh +++ b/tests/write-reports.sh @@ -16,14 +16,25 @@ while [ ! -d $ROOT/.git ]; do ROOT=`dirname $ROOT` done +set -e cd $ROOT/tests setup cleanup -valgrind ../Debug/eressea/eressea -t 184 ../scripts/reports.lua +VALGRIND=`which valgrind` +SERVER=../Debug/eressea/eressea +if [ -n "$VALGRIND" ]; then +SUPP=../share/ubuntu-12_04.supp +SERVER="$VALGRIND --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" +fi +echo "running $SERVER" +$SERVER -t 184 ../scripts/reports.lua [ -d reports ] || quit 4 "no reports directory created" CRFILE=184-zvto.cr -grep -q FACTION reports/$CRFILE || quit 1 "CR did not contain any factions" +grep -q PARTEI reports/$CRFILE || quit 1 "CR did not contain any factions" grep -q REGION reports/$CRFILE || quit 2 "CR did not contain any regions" -grep -q EINHEIT reports/$CRFILE || quit 3 "CR did not contain any units" +grep -q SCHIFF reports/$CRFILE || quit 3 "CR did not contain any ships" +grep -q BURG reports/$CRFILE || quit 4 "CR did not contain any buildings" +grep -q EINHEIT reports/$CRFILE || quit 5 "CR did not contain any units" +grep -q GEGENSTAENDE reports/$CRFILE || quit 6 "CR did not contain any items" echo "integration tests: PASS" cleanup