#include #include "study.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { unit *u; unit *teachers[2]; } study_fixture; static void setup_locale(struct locale *lang) { int i; for (i = 0; i < MAXSKILLS; ++i) { if (!locale_getstring(lang, mkname("skill", skillnames[i]))) locale_setstring(lang, mkname("skill", skillnames[i]), skillnames[i]); } locale_setstring(lang, parameters[P_ANY], "ALLE"); init_parameters(lang); init_skills(lang); } static void setup_study(study_fixture *fix, skill_t sk) { struct region * r; struct faction *f; struct locale *lang; assert(fix); test_cleanup(); config_set("study.random_progress", "0"); test_create_world(); r = findregion(0, 0); f = test_create_faction(0); lang = get_or_create_locale(locale_name(f->locale)); setup_locale(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)); test_clear_messages(f); } 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; setup_study(&fix, SK_CROSSBOW); teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); CuAssertPtrNotNull(tc, test_find_messagetype(fix.u->faction->msgs, "teach_asgood")); 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(); } static void test_study_bug_2194(CuTest *tc) { unit *u, *u1, *u2; struct locale * loc; building * b; test_cleanup(); random_source_inject_constant(0.0); init_resources(); loc = get_or_create_locale("de"); setup_locale(loc); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); scale_number(u, 2); set_level(u, SK_CROSSBOW, TEACHDIFFERENCE); u->faction->locale = loc; u1 = test_create_unit(u->faction, u->region); scale_number(u1, 17); u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); u2 = test_create_unit(u->faction, u->region); scale_number(u2, 3); u2->thisorder = create_order(K_STUDY, loc, skillnames[SK_MAGIC]); u->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u1->no), itoa36(u2->no)); b = test_create_building(u->region, test_create_buildingtype("academy")); b->size = 22; u_set_building(u, b); u_set_building(u1, b); u_set_building(u2, b); i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); b->flags = BLD_WORKING; teach_cmd(u, u->thisorder); CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "teach_asgood")); test_cleanup(); } static CuTest *g_tc; static bool cb_learn_one(unit *u, skill_t sk, double chance) { CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); CuAssertDblEquals(g_tc, 0.5 / u->number, chance, 0.01); return false; } static bool cb_learn_two(unit *u, skill_t sk, double chance) { CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); CuAssertDblEquals(g_tc, 2 * 0.5 / u->number, chance, 0.01); return false; } static void test_produceexp(CuTest *tc) { unit *u; g_tc = tc; test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); config_set("study.from_use", "0.5"); produceexp_ex(u, SK_ALCHEMY, 1, cb_learn_one); produceexp_ex(u, SK_ALCHEMY, 2, cb_learn_two); test_cleanup(); } #define MAXLOG 4 typedef struct log_entry { unit *u; skill_t sk; double ch; } log_entry; static log_entry log_learners[MAXLOG]; static int log_size; static bool log_learn(unit *u, skill_t sk, double ch) { if (log_size < MAXLOG) { log_entry * entry = &log_learners[log_size++]; entry->u = u; entry->sk = sk; entry->ch = ch; } return true; } static void test_academy_building(CuTest *tc) { unit *u, *u1, *u2; struct locale * loc; building * b; message * msg; test_cleanup(); mt_register(mt_new_va("teach_asgood", "unit:unit", "region:region", "command:order", "student:unit", 0)); random_source_inject_constant(0.0); init_resources(); loc = get_or_create_locale("de"); setup_locale(loc); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); scale_number(u, 2); set_level(u, SK_CROSSBOW, TEACHDIFFERENCE); u->faction->locale = loc; u1 = test_create_unit(u->faction, u->region); scale_number(u1, 15); u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); u2 = test_create_unit(u->faction, u->region); scale_number(u2, 5); u2->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); set_level(u2, SK_CROSSBOW, 1); u->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u1->no), itoa36(u2->no)); b = test_create_building(u->region, test_create_buildingtype("academy")); b->size = 22; u_set_building(u, b); u_set_building(u1, b); u_set_building(u2, b); i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); b->flags = BLD_WORKING; inject_learn(log_learn); teach_cmd(u, u->thisorder); inject_learn(0); CuAssertPtrNotNull(tc, msg = test_find_messagetype(u->faction->msgs, "teach_asgood")); CuAssertPtrEquals(tc, u, (unit *)(msg)->parameters[0].v); CuAssertPtrEquals(tc, u2, (unit *)(msg)->parameters[3].v); CuAssertPtrEquals(tc, u, log_learners[0].u); CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); CuAssertDblEquals(tc, 0.05, log_learners[0].ch, 0.001); test_cleanup(); } void test_learn_skill_single(CuTest *tc) { unit *u; skill *sv; test_cleanup(); config_set("study.random_progress", "0"); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); learn_skill(u, SK_ALCHEMY, STUDYDAYS); CuAssertPtrNotNull(tc, sv = u->skills); CuAssertIntEquals(tc, SK_ALCHEMY, sv->id); CuAssertIntEquals(tc, 1, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); learn_skill(u, SK_ALCHEMY, STUDYDAYS); CuAssertIntEquals(tc, 1, sv->weeks); learn_skill(u, SK_ALCHEMY, STUDYDAYS * 2); CuAssertIntEquals(tc, 2, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); test_cleanup(); } void test_learn_skill_multi(CuTest *tc) { unit *u; skill *sv; test_cleanup(); config_set("study.random_progress", "0"); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); scale_number(u, 10); learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number); CuAssertPtrNotNull(tc, sv = u->skills); CuAssertIntEquals(tc, SK_ALCHEMY, sv->id); CuAssertIntEquals(tc, 1, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number); CuAssertIntEquals(tc, 1, sv->weeks); learn_skill(u, SK_ALCHEMY, STUDYDAYS * u->number * 2); CuAssertIntEquals(tc, 2, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); test_cleanup(); } void test_demon_skillchanges(CuTest *tc) { unit * u; race * rc; test_cleanup(); rc = test_create_race("demon"); CuAssertPtrEquals(tc, rc, get_race(RC_DAEMON)); u = test_create_unit(test_create_faction(rc), 0); CuAssertPtrNotNull(tc, u); set_level(u, SK_CROSSBOW, 1); demon_skillchange(u); // TODO: sensing here test_cleanup(); } CuSuite *get_study_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_learn_skill_single); SUITE_ADD_TEST(suite, test_learn_skill_multi); 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); SUITE_ADD_TEST(suite, test_produceexp); SUITE_ADD_TEST(suite, test_academy_building); SUITE_ADD_TEST(suite, test_demon_skillchanges); DISABLE_TEST(suite, test_study_bug_2194); return suite; }