registry.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  1. /*
  2. * Copyright © 2020 Red Hat, Inc.
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "config.h"
  6. #include "test-config.h"
  7. #include <assert.h>
  8. #if HAVE_UNISTD_H
  9. #include <unistd.h>
  10. #endif
  11. #include <stdarg.h>
  12. #include <stdio.h>
  13. #include <sys/stat.h>
  14. #include <sys/types.h>
  15. #include <libxml/parser.h>
  16. #include <libxml/tree.h>
  17. #include "xkbcommon/xkbregistry.h"
  18. #include "utils.h"
  19. #include "test.h"
  20. #define NO_VARIANT NULL
  21. enum {
  22. MODEL = 78,
  23. LAYOUT,
  24. VARIANT,
  25. OPTION,
  26. };
  27. enum popularity {
  28. POPULARITY_UNDEFINED = 0, /* Not member of enum rxkb_popularity */
  29. POPULARITY_STANDARD = RXKB_POPULARITY_STANDARD,
  30. POPULARITY_EXOTIC = RXKB_POPULARITY_EXOTIC,
  31. };
  32. struct test_model {
  33. const char *name; /* required */
  34. const char *vendor;
  35. const char *description;
  36. };
  37. struct test_layout {
  38. const char *name; /* required */
  39. const char *variant;
  40. const char *brief;
  41. const char *description;
  42. const char *iso639[3]; /* language list (iso639 three letter codes), 3 is enough for our test */
  43. const char *iso3166[3]; /* country list (iso3166 two letter codes), 3 is enough for our tests */
  44. enum popularity popularity;
  45. };
  46. struct test_option {
  47. const char *name;
  48. const char *description;
  49. };
  50. struct test_option_group {
  51. const char *name;
  52. const char *description;
  53. bool allow_multiple_selection;
  54. struct test_option options[10];
  55. };
  56. static const char *
  57. popularity_attr(enum popularity popularity)
  58. {
  59. switch (popularity) {
  60. case POPULARITY_UNDEFINED:
  61. return "";
  62. case RXKB_POPULARITY_STANDARD:
  63. return " popularity=\"standard\"";
  64. case RXKB_POPULARITY_EXOTIC:
  65. return " popularity=\"exotic\"";
  66. default:
  67. fprintf(stderr, "ERROR: unsupported popularity: %d\n", popularity);
  68. assert(false);
  69. }
  70. }
  71. static void
  72. fprint_config_item(FILE *fp,
  73. const char *name,
  74. const char *vendor,
  75. const char *brief,
  76. const char *description,
  77. const char * const iso639[3],
  78. const char * const iso3166[3],
  79. enum popularity popularity)
  80. {
  81. fprintf(fp, " <configItem%s>\n"
  82. " <name>%s</name>\n", popularity_attr(popularity), name);
  83. if (brief)
  84. fprintf(fp, " <shortDescription>%s</shortDescription>\n", brief);
  85. if (description)
  86. fprintf(fp, " <description>%s</description>\n", description);
  87. if (vendor)
  88. fprintf(fp, " <vendor>%s</vendor>\n", vendor);
  89. if (iso3166 && iso3166[0]) {
  90. fprintf(fp, " <countryList>\n");
  91. for (int i = 0; i < 3; i++) {
  92. const char *iso = iso3166[i];
  93. if (!iso)
  94. break;
  95. fprintf(fp, " <iso3166Id>%s</iso3166Id>\n", iso);
  96. }
  97. fprintf(fp, " </countryList>\n");
  98. }
  99. if (iso639 && iso639[0]) {
  100. fprintf(fp, " <languageList>\n");
  101. for (int i = 0; i < 3; i++) {
  102. const char *iso = iso639[i];
  103. if (!iso)
  104. break;
  105. fprintf(fp, " <iso639Id>%s</iso639Id>\n", iso);
  106. }
  107. fprintf(fp, " </languageList>\n");
  108. }
  109. fprintf(fp, " </configItem>\n");
  110. }
  111. /**
  112. * Create a directory populated with a rules/<ruleset>.xml that contains the
  113. * given items.
  114. *
  115. * @return the XKB base directory
  116. */
  117. static char *
  118. test_create_rules(const char *ruleset,
  119. const struct test_model *test_models,
  120. const struct test_layout *test_layouts,
  121. const struct test_option_group *test_groups)
  122. {
  123. static int iteration;
  124. char *tmpdir;
  125. char buf[PATH_MAX];
  126. int rc;
  127. FILE *fp;
  128. char *template = asprintf_safe("%s.%d.XXXXXX", ruleset, iteration++);
  129. assert(template != NULL);
  130. tmpdir = test_maketempdir(template);
  131. free(template);
  132. free(test_makedir(tmpdir, "rules"));
  133. rc = snprintf_safe(buf, sizeof(buf), "%s/rules/%s.xml", tmpdir, ruleset);
  134. assert(rc);
  135. fp = fopen(buf, "w");
  136. assert(fp);
  137. fprintf(fp,
  138. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  139. "<!DOCTYPE xkbConfigRegistry SYSTEM \"xkb.dtd\">\n"
  140. "<xkbConfigRegistry version=\"1.1\">\n");
  141. if (test_models) {
  142. fprintf(fp, "<modelList>\n");
  143. for (const struct test_model *m = test_models; m->name; m++) {
  144. fprintf(fp, "<model>\n");
  145. fprint_config_item(fp, m->name, m->vendor, NULL, m->description,
  146. NULL, NULL, POPULARITY_UNDEFINED);
  147. fprintf(fp, "</model>\n");
  148. }
  149. fprintf(fp, "</modelList>\n");
  150. }
  151. if (test_layouts) {
  152. const struct test_layout *l, *next;
  153. fprintf(fp, "<layoutList>\n");
  154. l = test_layouts;
  155. next = l + 1;
  156. assert(l->variant == NULL);
  157. while (l->name) {
  158. fprintf(fp, "<layout>\n");
  159. fprint_config_item(fp, l->name, NULL, l->brief, l->description, l->iso639, l->iso3166, l->popularity);
  160. if (next->name && streq(next->name, l->name)) {
  161. fprintf(fp, "<variantList>\n");
  162. do {
  163. fprintf(fp, "<variant>\n");
  164. fprint_config_item(fp, next->variant, NULL, next->brief, next->description, next->iso639, next->iso3166, next->popularity);
  165. fprintf(fp, "</variant>\n");
  166. l = next;
  167. next++;
  168. } while (next->name && streq(next->name, l->name));
  169. fprintf(fp, "</variantList>\n");
  170. }
  171. fprintf(fp, "</layout>\n");
  172. l++;
  173. next = l + 1;
  174. }
  175. fprintf(fp, "</layoutList>\n");
  176. }
  177. if (test_groups) {
  178. fprintf(fp, "<optionList>\n");
  179. for (const struct test_option_group *g = test_groups; g->name; g++) {
  180. fprintf(fp, "<group allowMultipleSelection=\"%s\">\n",
  181. g->allow_multiple_selection ? "true" : "false");
  182. fprint_config_item(fp, g->name, NULL, NULL, g->description,
  183. NULL, NULL, POPULARITY_UNDEFINED);
  184. for (const struct test_option *o = g->options; o->name; o++) {
  185. fprintf(fp, " <option>\n");
  186. fprint_config_item(fp, o->name, NULL, NULL, o->description,
  187. NULL, NULL, POPULARITY_UNDEFINED);
  188. fprintf(fp, "</option>\n");
  189. }
  190. fprintf(fp, "</group>\n");
  191. }
  192. fprintf(fp, "</optionList>\n");
  193. }
  194. fprintf(fp, "</xkbConfigRegistry>\n");
  195. fclose(fp);
  196. return tmpdir;
  197. }
  198. static void
  199. test_remove_rules(char *basedir, const char *ruleset)
  200. {
  201. char path[PATH_MAX];
  202. int rc;
  203. rc = snprintf_safe(path, sizeof(path), "%s/rules/%s.xml", basedir,
  204. ruleset);
  205. assert(rc);
  206. unlink(path);
  207. rc = snprintf_safe(path, sizeof(path), "%s/xkb/rules", basedir);
  208. assert(rc);
  209. rmdir(path);
  210. rmdir(basedir);
  211. free(basedir);
  212. }
  213. static struct rxkb_context *
  214. test_setup_context_for(const char *ruleset,
  215. struct test_model *system_models,
  216. struct test_model *user_models,
  217. struct test_layout *system_layouts,
  218. struct test_layout *user_layouts,
  219. struct test_option_group *system_groups,
  220. struct test_option_group *user_groups)
  221. {
  222. char *sysdir = NULL, *userdir = NULL;
  223. struct rxkb_context *ctx;
  224. sysdir = test_create_rules(ruleset, system_models, system_layouts,
  225. system_groups);
  226. if (user_models || user_layouts || user_groups)
  227. userdir = test_create_rules(ruleset, user_models, user_layouts,
  228. user_groups);
  229. ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
  230. assert(ctx);
  231. if (userdir)
  232. assert(rxkb_context_include_path_append(ctx, userdir));
  233. assert(rxkb_context_include_path_append(ctx, sysdir));
  234. assert(rxkb_context_parse(ctx, ruleset));
  235. test_remove_rules(sysdir, ruleset);
  236. if (userdir)
  237. test_remove_rules(userdir, ruleset);
  238. return ctx;
  239. }
  240. static struct rxkb_context *
  241. test_setup_context(struct test_model *system_models,
  242. struct test_model *user_models,
  243. struct test_layout *system_layouts,
  244. struct test_layout *user_layouts,
  245. struct test_option_group *system_groups,
  246. struct test_option_group *user_groups)
  247. {
  248. const char *ruleset = "xkbtests";
  249. return test_setup_context_for(ruleset, system_models,
  250. user_models, system_layouts,
  251. user_layouts, system_groups,
  252. user_groups);
  253. }
  254. static struct rxkb_model *
  255. fetch_model(struct rxkb_context *ctx, const char *model)
  256. {
  257. struct rxkb_model *m = rxkb_model_first(ctx);
  258. while (m) {
  259. if (streq(rxkb_model_get_name(m), model))
  260. return rxkb_model_ref(m);
  261. m = rxkb_model_next(m);
  262. }
  263. return NULL;
  264. }
  265. static bool
  266. find_model(struct rxkb_context *ctx, const char *model)
  267. {
  268. struct rxkb_model *m = fetch_model(ctx, model);
  269. rxkb_model_unref(m);
  270. return m != NULL;
  271. }
  272. static bool
  273. find_models(struct rxkb_context *ctx, ...)
  274. {
  275. va_list args;
  276. const char *name;
  277. int idx = 0;
  278. bool rc = false;
  279. va_start(args, ctx);
  280. name = va_arg(args, const char *);
  281. while(name) {
  282. ++idx;
  283. assert(idx < 20); /* safety guard */
  284. if (!find_model(ctx, name))
  285. goto out;
  286. name = va_arg(args, const char *);
  287. };
  288. rc = true;
  289. out:
  290. va_end(args);
  291. return rc;
  292. }
  293. static struct rxkb_layout *
  294. fetch_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
  295. {
  296. struct rxkb_layout *l = rxkb_layout_first(ctx);
  297. while (l) {
  298. const char *v = rxkb_layout_get_variant(l);
  299. if (streq(rxkb_layout_get_name(l), layout) &&
  300. ((v == NULL && variant == NULL) ||
  301. (v != NULL && variant != NULL && streq(v, variant))))
  302. return rxkb_layout_ref(l);
  303. l = rxkb_layout_next(l);
  304. }
  305. return NULL;
  306. }
  307. static bool
  308. find_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
  309. {
  310. struct rxkb_layout *l = fetch_layout(ctx, layout, variant);
  311. rxkb_layout_unref(l);
  312. return l != NULL;
  313. }
  314. static bool
  315. find_layouts(struct rxkb_context *ctx, ...)
  316. {
  317. va_list args;
  318. const char *name, *variant;
  319. int idx = 0;
  320. bool rc = false;
  321. va_start(args, ctx);
  322. name = va_arg(args, const char *);
  323. variant = va_arg(args, const char *);
  324. while(name) {
  325. ++idx;
  326. assert(idx < 20); /* safety guard */
  327. if (!find_layout(ctx, name, variant))
  328. goto out;
  329. name = va_arg(args, const char *);
  330. if (name)
  331. variant = va_arg(args, const char *);
  332. };
  333. rc = true;
  334. out:
  335. va_end(args);
  336. return rc;
  337. }
  338. static bool
  339. check_layouts_order(struct rxkb_context *ctx, ...)
  340. {
  341. va_list args;
  342. const char *layout, *variant;
  343. int idx = 0;
  344. bool rc = false;
  345. struct rxkb_layout *l = rxkb_layout_first(ctx);
  346. va_start(args, ctx);
  347. layout = va_arg(args, const char *);
  348. variant = va_arg(args, const char *);
  349. while(layout) {
  350. ++idx;
  351. assert(idx < 20); /* safety guard */
  352. if (!l) {
  353. fprintf(stderr, "ERROR: expected layout #%d \"%s(%s)\", got none\n",
  354. idx, layout, variant ? variant : "-");
  355. goto out;
  356. }
  357. const char *v = rxkb_layout_get_variant(l);
  358. if (!streq(rxkb_layout_get_name(l), layout) || !streq_null(v, variant)) {
  359. fprintf(stderr, "ERROR: expected layout #%d \"%s(%s)\", got \"%s(%s)\"\n",
  360. idx, layout, variant ? variant : "-",
  361. rxkb_layout_get_name(l), v ? v : "-");
  362. goto out;
  363. }
  364. layout = va_arg(args, const char *);
  365. if (layout)
  366. variant = va_arg(args, const char *);
  367. l = rxkb_layout_next(l);
  368. };
  369. if (l) {
  370. const char *v = rxkb_layout_get_variant(l);
  371. fprintf(stderr, "ERROR: unexpected layout at #%d: \"%s(%s)\"\n",
  372. idx + 1, rxkb_layout_get_name(l), v ? v : "-");
  373. }
  374. rc = l == NULL;
  375. out:
  376. va_end(args);
  377. return rc;
  378. }
  379. static struct rxkb_option_group *
  380. fetch_option_group(struct rxkb_context *ctx, const char *grp)
  381. {
  382. struct rxkb_option_group *g = rxkb_option_group_first(ctx);
  383. while (g) {
  384. if (streq(grp, rxkb_option_group_get_name(g)))
  385. return rxkb_option_group_ref(g);
  386. g = rxkb_option_group_next(g);
  387. }
  388. return NULL;
  389. }
  390. static struct rxkb_option *
  391. fetch_option(struct rxkb_context *ctx, const char *grp, const char *opt)
  392. {
  393. struct rxkb_option_group *g = rxkb_option_group_first(ctx);
  394. while (g) {
  395. if (streq(grp, rxkb_option_group_get_name(g))) {
  396. struct rxkb_option *o = rxkb_option_first(g);
  397. while (o) {
  398. if (streq(opt, rxkb_option_get_name(o)))
  399. return rxkb_option_ref(o);
  400. o = rxkb_option_next(o);
  401. }
  402. }
  403. g = rxkb_option_group_next(g);
  404. }
  405. return NULL;
  406. }
  407. static bool
  408. find_option(struct rxkb_context *ctx, const char *grp, const char *opt)
  409. {
  410. struct rxkb_option *o = fetch_option(ctx, grp, opt);
  411. rxkb_option_unref(o);
  412. return o != NULL;
  413. }
  414. static bool
  415. find_options(struct rxkb_context *ctx, ...)
  416. {
  417. va_list args;
  418. const char *grp, *opt;
  419. int idx = 0;
  420. bool rc = false;
  421. va_start(args, ctx);
  422. grp = va_arg(args, const char *);
  423. opt = va_arg(args, const char *);
  424. while(grp) {
  425. ++idx;
  426. assert(idx < 20); /* safety guard */
  427. if (!find_option(ctx, grp, opt))
  428. goto out;
  429. grp = va_arg(args, const char *);
  430. if (grp)
  431. opt = va_arg(args, const char *);
  432. };
  433. rc = true;
  434. out:
  435. va_end(args);
  436. return rc;
  437. }
  438. static bool
  439. cmp_models(struct test_model *tm, struct rxkb_model *m)
  440. {
  441. if (!tm || !m)
  442. return false;
  443. if (!streq(tm->name, rxkb_model_get_name(m)))
  444. return false;
  445. if (!streq_null(tm->vendor, rxkb_model_get_vendor(m)))
  446. return false;
  447. if (!streq_null(tm->description, rxkb_model_get_description(m)))
  448. return false;
  449. return true;
  450. }
  451. static bool
  452. cmp_layouts(struct test_layout *tl, struct rxkb_layout *l)
  453. {
  454. struct rxkb_iso3166_code *iso3166 = NULL;
  455. struct rxkb_iso639_code *iso639 = NULL;
  456. if (!tl || !l)
  457. return false;
  458. if (!streq(tl->name, rxkb_layout_get_name(l)))
  459. return false;
  460. if (!streq_null(tl->variant, rxkb_layout_get_variant(l)))
  461. return false;
  462. if (!streq_null(tl->brief, rxkb_layout_get_brief(l)))
  463. return false;
  464. if (!streq_null(tl->description, rxkb_layout_get_description(l)))
  465. return false;
  466. iso3166 = rxkb_layout_get_iso3166_first(l);
  467. for (size_t i = 0; i < ARRAY_SIZE(tl->iso3166); i++) {
  468. const char *iso = tl->iso3166[i];
  469. if (iso == NULL && iso3166 == NULL)
  470. break;
  471. if (!streq_null(iso, rxkb_iso3166_code_get_code(iso3166)))
  472. return false;
  473. iso3166 = rxkb_iso3166_code_next(iso3166);
  474. }
  475. if (iso3166 != NULL)
  476. return false;
  477. iso639 = rxkb_layout_get_iso639_first(l);
  478. for (size_t i = 0; i < ARRAY_SIZE(tl->iso639); i++) {
  479. const char *iso = tl->iso639[i];
  480. if (iso == NULL && iso639 == NULL)
  481. break;
  482. if (!streq_null(iso, rxkb_iso639_code_get_code(iso639)))
  483. return false;
  484. iso639 = rxkb_iso639_code_next(iso639);
  485. }
  486. if (iso639 != NULL)
  487. return false;
  488. return true;
  489. }
  490. static bool
  491. cmp_options(struct test_option *to, struct rxkb_option *o)
  492. {
  493. if (!to || !o)
  494. return false;
  495. if (!streq(to->name, rxkb_option_get_name(o)))
  496. return false;
  497. if (!streq_null(to->description, rxkb_option_get_description(o)))
  498. return false;
  499. return true;
  500. }
  501. enum cmp_type {
  502. CMP_EXACT,
  503. CMP_MATCHING_ONLY,
  504. };
  505. static bool
  506. cmp_option_groups(struct test_option_group *tg, struct rxkb_option_group *g,
  507. enum cmp_type cmp)
  508. {
  509. struct rxkb_option *o;
  510. struct test_option *to;
  511. if (!tg || !g)
  512. return false;
  513. if (!streq(tg->name, rxkb_option_group_get_name(g)))
  514. return false;
  515. if (!streq_null(tg->description, rxkb_option_group_get_description(g)))
  516. return false;
  517. if (tg->allow_multiple_selection != rxkb_option_group_allows_multiple(g))
  518. return false;
  519. to = tg->options;
  520. o = rxkb_option_first(g);
  521. while (o && to->name) {
  522. if (!cmp_options(to, o))
  523. return false;
  524. to++;
  525. o = rxkb_option_next(o);
  526. }
  527. if (cmp == CMP_EXACT && (o || to->name))
  528. return false;
  529. return true;
  530. }
  531. static void
  532. test_load_basic(void)
  533. {
  534. struct test_model system_models[] = {
  535. {"m1"},
  536. {"m2"},
  537. {NULL},
  538. };
  539. struct test_layout system_layouts[] = {
  540. {"l1"},
  541. {"l1", "v1"},
  542. {NULL},
  543. };
  544. struct test_option_group system_groups[] = {
  545. {"grp1", NULL, true,
  546. { {"grp1:1"}, {"grp1:2"} } },
  547. {"grp2", NULL, false,
  548. { {"grp2:1"}, {"grp2:2"} } },
  549. { NULL },
  550. };
  551. struct rxkb_context *ctx;
  552. ctx = test_setup_context(system_models, NULL,
  553. system_layouts, NULL,
  554. system_groups, NULL);
  555. assert(find_models(ctx, "m1", "m2", NULL));
  556. assert(find_layouts(ctx, "l1", NO_VARIANT,
  557. "l1", "v1", NULL));
  558. assert(find_options(ctx, "grp1", "grp1:1",
  559. "grp1", "grp1:2",
  560. "grp2", "grp2:1",
  561. "grp2", "grp2:2", NULL));
  562. rxkb_context_unref(ctx);
  563. }
  564. static void
  565. test_load_full(void)
  566. {
  567. struct test_model system_models[] = {
  568. {"m1", "vendor1", "desc1"},
  569. {"m2", "vendor2", "desc2"},
  570. {NULL},
  571. };
  572. struct test_layout system_layouts[] = {
  573. {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
  574. {"l1", "v1", "vbrief1", "vdesc1"},
  575. {"l1", "v2", NULL, "vdesc2"},
  576. {NULL},
  577. };
  578. struct test_option_group system_groups[] = {
  579. {"grp1", "gdesc1", true,
  580. { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
  581. {"grp2", "gdesc2", false,
  582. { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
  583. { NULL },
  584. };
  585. struct rxkb_context *ctx;
  586. struct rxkb_model *m;
  587. struct rxkb_layout *l;
  588. struct rxkb_option_group *g;
  589. ctx = test_setup_context(system_models, NULL,
  590. system_layouts, NULL,
  591. system_groups, NULL);
  592. m = fetch_model(ctx, "m1");
  593. assert(cmp_models(&system_models[0], m));
  594. rxkb_model_unref(m);
  595. m = fetch_model(ctx, "m2");
  596. assert(cmp_models(&system_models[1], m));
  597. rxkb_model_unref(m);
  598. l = fetch_layout(ctx, "l1", NO_VARIANT);
  599. assert(cmp_layouts(&system_layouts[0], l));
  600. rxkb_layout_unref(l);
  601. l = fetch_layout(ctx, "l1", "v1");
  602. assert(cmp_layouts(&system_layouts[1], l));
  603. rxkb_layout_unref(l);
  604. l = fetch_layout(ctx, "l1", "v2");
  605. struct test_layout expected = {"l1", "v2", "lbrief1", "vdesc2"};
  606. assert(cmp_layouts(&expected, l));
  607. rxkb_layout_unref(l);
  608. g = fetch_option_group(ctx, "grp1");
  609. assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
  610. rxkb_option_group_unref(g);
  611. g = fetch_option_group(ctx, "grp2");
  612. assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
  613. rxkb_option_group_unref(g);
  614. rxkb_context_unref(ctx);
  615. }
  616. static void
  617. test_load_languages(void)
  618. {
  619. struct test_model system_models[] = {
  620. {"m1", "vendor1", "desc1"},
  621. {NULL},
  622. };
  623. struct test_layout system_layouts[] = {
  624. {"l1", NO_VARIANT, "lbrief1", "ldesc1",
  625. .iso639 = { "abc", "def" },
  626. .iso3166 = { "uv", "wx" }},
  627. {"l1", "v1", "vbrief1", "vdesc1",
  628. .iso639 = {"efg"},
  629. .iso3166 = {"yz"}},
  630. {"l2", NO_VARIANT, "lbrief1", "ldesc1",
  631. .iso639 = { "hij", "klm" },
  632. .iso3166 = { "op", "qr" }},
  633. {"l2", "v2", "lbrief1", "ldesc1",
  634. .iso639 = { NULL }, /* inherit from parent */
  635. .iso3166 = { NULL }}, /* inherit from parent */
  636. {NULL},
  637. };
  638. struct test_option_group system_groups[] = {
  639. {"grp1", "gdesc1", true,
  640. { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
  641. { NULL },
  642. };
  643. struct rxkb_context *ctx;
  644. struct rxkb_layout *l;
  645. struct rxkb_iso3166_code *iso3166;
  646. struct rxkb_iso639_code *iso639;
  647. ctx = test_setup_context(system_models, NULL,
  648. system_layouts, NULL,
  649. system_groups, NULL);
  650. l = fetch_layout(ctx, "l1", NO_VARIANT);
  651. assert(cmp_layouts(&system_layouts[0], l));
  652. rxkb_layout_unref(l);
  653. l = fetch_layout(ctx, "l1", "v1");
  654. assert(cmp_layouts(&system_layouts[1], l));
  655. rxkb_layout_unref(l);
  656. l = fetch_layout(ctx, "l2", "v2");
  657. iso3166 = rxkb_layout_get_iso3166_first(l);
  658. assert(streq(rxkb_iso3166_code_get_code(iso3166), "op"));
  659. iso3166 = rxkb_iso3166_code_next(iso3166);
  660. assert(streq(rxkb_iso3166_code_get_code(iso3166), "qr"));
  661. iso639 = rxkb_layout_get_iso639_first(l);
  662. assert(streq(rxkb_iso639_code_get_code(iso639), "hij"));
  663. iso639 = rxkb_iso639_code_next(iso639);
  664. assert(streq(rxkb_iso639_code_get_code(iso639), "klm"));
  665. rxkb_layout_unref(l);
  666. rxkb_context_unref(ctx);
  667. }
  668. static void
  669. test_load_invalid_languages(void)
  670. {
  671. struct test_model system_models[] = {
  672. {"m1", "vendor1", "desc1"},
  673. {NULL},
  674. };
  675. struct test_layout system_layouts[] = {
  676. {"l1", NO_VARIANT, "lbrief1", "ldesc1",
  677. .iso639 = { "ab", "def" },
  678. .iso3166 = { "uvw", "xz" }},
  679. {NULL},
  680. };
  681. struct test_option_group system_groups[] = {
  682. {"grp1", "gdesc1", true,
  683. { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
  684. { NULL },
  685. };
  686. struct rxkb_context *ctx;
  687. struct rxkb_layout *l;
  688. struct rxkb_iso3166_code *iso3166;
  689. struct rxkb_iso639_code *iso639;
  690. ctx = test_setup_context(system_models, NULL,
  691. system_layouts, NULL,
  692. system_groups, NULL);
  693. l = fetch_layout(ctx, "l1", NO_VARIANT);
  694. /* uvw is invalid, we expect 2 letters, verify it was ignored */
  695. iso3166 = rxkb_layout_get_iso3166_first(l);
  696. assert(streq(rxkb_iso3166_code_get_code(iso3166), "xz"));
  697. assert(rxkb_iso3166_code_next(iso3166) == NULL);
  698. /* ab is invalid, we expect 3 letters, verify it was ignored */
  699. iso639 = rxkb_layout_get_iso639_first(l);
  700. assert(streq(rxkb_iso639_code_get_code(iso639), "def"));
  701. assert(rxkb_iso639_code_next(iso639) == NULL);
  702. rxkb_layout_unref(l);
  703. rxkb_context_unref(ctx);
  704. }
  705. static void
  706. test_popularity(void)
  707. {
  708. static_assert(POPULARITY_UNDEFINED != POPULARITY_STANDARD, "");
  709. static_assert(POPULARITY_UNDEFINED != POPULARITY_EXOTIC, "");
  710. struct test_layout system_layouts[] = {
  711. {.name = "l1", .variant = NO_VARIANT }, /* Default popularity */
  712. {.name = "l1", .variant = "v1" }, /* Default popularity */
  713. {.name = "l2", .popularity = POPULARITY_STANDARD },
  714. {.name = "l3", .popularity = POPULARITY_EXOTIC },
  715. {NULL},
  716. };
  717. struct rxkb_context *ctx;
  718. struct rxkb_layout *l;
  719. struct ruleset_conf {
  720. const char *ruleset;
  721. enum rxkb_context_flags flags;
  722. enum rxkb_popularity popularity; /* Default popularity */
  723. };
  724. struct ruleset_conf rulesets[] = {
  725. { /* Rules with “standard” popularity */
  726. .ruleset="xkbtests",
  727. .flags=RXKB_CONTEXT_NO_DEFAULT_INCLUDES,
  728. .popularity=RXKB_POPULARITY_STANDARD
  729. },
  730. { /* Rules with “exotic” popularity (hack, see below) */
  731. .ruleset="xkbtests.extras",
  732. .flags=RXKB_CONTEXT_NO_DEFAULT_INCLUDES |
  733. RXKB_CONTEXT_LOAD_EXOTIC_RULES,
  734. .popularity=RXKB_POPULARITY_EXOTIC
  735. }
  736. };
  737. for (size_t k = 0; k < ARRAY_SIZE(rulesets); k++) {
  738. struct ruleset_conf *conf = &rulesets[k];
  739. char *dir = NULL;
  740. dir = test_create_rules(conf->ruleset, NULL, system_layouts, NULL);
  741. ctx = rxkb_context_new(conf->flags);
  742. assert(ctx);
  743. assert(rxkb_context_include_path_append(ctx, dir));
  744. /* Hack: ruleset "xkbtests.extras" above generates xkbtests.extras.xml,
  745. * loading "xkbtests" means the extras file counts as exotic */
  746. assert(rxkb_context_parse(ctx, "xkbtests"));
  747. /* Test implicit popularity */
  748. l = fetch_layout(ctx, "l1", NO_VARIANT);
  749. assert(rxkb_layout_get_popularity(l) == conf->popularity);
  750. rxkb_layout_unref(l);
  751. l = fetch_layout(ctx, "l1", "v1");
  752. assert(rxkb_layout_get_popularity(l) == conf->popularity);
  753. rxkb_layout_unref(l);
  754. /* Test explicit popularity */
  755. l = fetch_layout(ctx, "l2", NO_VARIANT);
  756. assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_STANDARD);
  757. rxkb_layout_unref(l);
  758. l = fetch_layout(ctx, "l3", NO_VARIANT);
  759. assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_EXOTIC);
  760. rxkb_layout_unref(l);
  761. test_remove_rules(dir, conf->ruleset);
  762. rxkb_context_unref(ctx);
  763. }
  764. }
  765. static void
  766. test_load_merge(void)
  767. {
  768. struct test_model system_models[] = {
  769. {"m1", "vendor1", "desc1"},
  770. {"m2", "vendor2", "desc2"},
  771. {NULL},
  772. };
  773. struct test_model user_models[] = {
  774. {"m3", "vendor3", "desc3"},
  775. {"m4", "vendor4", "desc4"},
  776. {NULL},
  777. };
  778. struct test_layout system_layouts[] = {
  779. {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
  780. {"l1", "v1", "vbrief1", "vdesc1"},
  781. {NULL},
  782. };
  783. struct test_layout user_layouts[] = {
  784. {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
  785. {"l2", "v2", "vbrief2", "vdesc2"},
  786. // Duplicate with system
  787. {"l1", NO_VARIANT, "lbrief1bis", "ldesc1bis"},
  788. {"l1", "v1", "vbrief1bis", "vdesc1bis"},
  789. {NULL},
  790. };
  791. struct test_option_group system_groups[] = {
  792. {"grp1", NULL, true,
  793. { {"grp1:1"}, {"grp1:2"} } },
  794. {"grp2", NULL, false,
  795. { {"grp2:1"}, {"grp2:2"} } },
  796. { NULL },
  797. };
  798. struct test_option_group user_groups[] = {
  799. {"grp3", NULL, true,
  800. { {"grp3:1"}, {"grp3:2"} } },
  801. {"grp4", NULL, false,
  802. { {"grp4:1"}, {"grp4:2"} } },
  803. { NULL },
  804. };
  805. struct rxkb_context *ctx;
  806. struct rxkb_model *m;
  807. struct rxkb_layout *l;
  808. struct rxkb_option_group *g;
  809. ctx = test_setup_context(system_models, user_models,
  810. system_layouts, user_layouts,
  811. system_groups, user_groups);
  812. assert(find_models(ctx, "m1", "m2", "m3", "m4", NULL));
  813. assert(find_layouts(ctx, "l1", NO_VARIANT,
  814. "l1", "v1",
  815. "l2", NO_VARIANT,
  816. "l2", "v2", NULL));
  817. m = fetch_model(ctx, "m1");
  818. assert(cmp_models(&system_models[0], m));
  819. rxkb_model_unref(m);
  820. m = fetch_model(ctx, "m2");
  821. assert(cmp_models(&system_models[1], m));
  822. rxkb_model_unref(m);
  823. m = fetch_model(ctx, "m3");
  824. assert(cmp_models(&user_models[0], m));
  825. rxkb_model_unref(m);
  826. m = fetch_model(ctx, "m4");
  827. assert(cmp_models(&user_models[1], m));
  828. rxkb_model_unref(m);
  829. l = fetch_layout(ctx, "l1", NO_VARIANT);
  830. assert(cmp_layouts(&system_layouts[0], l));
  831. rxkb_layout_unref(l);
  832. l = fetch_layout(ctx, "l1", "v1");
  833. assert(cmp_layouts(&system_layouts[1], l));
  834. rxkb_layout_unref(l);
  835. l = fetch_layout(ctx, "l2", NO_VARIANT);
  836. assert(cmp_layouts(&user_layouts[0], l));
  837. rxkb_layout_unref(l);
  838. l = fetch_layout(ctx, "l2", "v2");
  839. assert(cmp_layouts(&user_layouts[1], l));
  840. rxkb_layout_unref(l);
  841. /* Check that the duplicate layouts are skipped */
  842. assert(check_layouts_order(ctx,
  843. "l1", NO_VARIANT, "l1", "v1",
  844. "l2", NO_VARIANT, "l2", "v2",
  845. NULL));
  846. g = fetch_option_group(ctx, "grp1");
  847. assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
  848. rxkb_option_group_unref(g);
  849. g = fetch_option_group(ctx, "grp2");
  850. assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
  851. rxkb_option_group_unref(g);
  852. g = fetch_option_group(ctx, "grp3");
  853. assert(cmp_option_groups(&user_groups[0], g, CMP_EXACT));
  854. rxkb_option_group_unref(g);
  855. g = fetch_option_group(ctx, "grp4");
  856. assert(cmp_option_groups(&user_groups[1], g, CMP_EXACT));
  857. rxkb_option_group_unref(g);
  858. rxkb_context_unref(ctx);
  859. }
  860. static void
  861. test_load_merge_no_overwrite(void)
  862. {
  863. struct test_model system_models[] = {
  864. {"m1", "vendor1", "desc1"},
  865. {"m2", "vendor2", "desc2"},
  866. {NULL},
  867. };
  868. struct test_model user_models[] = {
  869. {"m1", "vendor3", "desc3"}, /* must not overwrite */
  870. {"m4", "vendor4", "desc4"},
  871. {NULL},
  872. };
  873. struct test_layout system_layouts[] = {
  874. {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
  875. {"l1", "v1", "vbrief1", "vdesc1"},
  876. {NULL},
  877. };
  878. struct test_layout user_layouts[] = {
  879. {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
  880. {"l2", "v2", "vbrief2", "vdesc2"},
  881. {"l1", NO_VARIANT, "lbrief3", "ldesc3"}, /* must not overwrite */
  882. {"l1", "v2", "vbrief3", "vdesc3"}, /* must not overwrite */
  883. {NULL},
  884. };
  885. struct test_option_group system_groups[] = {
  886. {"grp1", "gdesc1", true,
  887. { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
  888. {"grp2", "gdesc2", false,
  889. { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
  890. { NULL },
  891. };
  892. struct test_option_group user_groups[] = {
  893. {"grp1", "XXXXX", false, /* must not overwrite */
  894. { {"grp1:1", "YYYYYYY"}, /* must not overwrite */
  895. {"grp1:3", "ZZZZZZ"} } }, /* append */
  896. {"grp4", "gdesc4", false,
  897. { {"grp4:1", "odesc41"}, {"grp4:2", "odesc42"} } },
  898. { NULL },
  899. };
  900. struct rxkb_context *ctx;
  901. struct rxkb_model *m;
  902. struct rxkb_layout *l;
  903. struct rxkb_option_group *g;
  904. ctx = test_setup_context(system_models, user_models,
  905. system_layouts, user_layouts,
  906. system_groups, user_groups);
  907. m = fetch_model(ctx, "m1");
  908. assert(cmp_models(&system_models[0], m));
  909. rxkb_model_unref(m);
  910. l = fetch_layout(ctx, "l1", NO_VARIANT);
  911. assert(cmp_layouts(&system_layouts[0], l));
  912. rxkb_layout_unref(l);
  913. l = fetch_layout(ctx, "l1", "v1");
  914. assert(cmp_layouts(&system_layouts[1], l));
  915. rxkb_layout_unref(l);
  916. assert(find_option(ctx, "grp1", "grp1:3"));
  917. g = fetch_option_group(ctx, "grp1");
  918. assert(cmp_option_groups(&system_groups[0], g, CMP_MATCHING_ONLY));
  919. rxkb_option_group_unref(g);
  920. rxkb_context_unref(ctx);
  921. }
  922. static void
  923. test_no_include_paths(void)
  924. {
  925. struct rxkb_context *ctx;
  926. ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
  927. assert(ctx);
  928. assert(!rxkb_context_parse_default_ruleset(ctx));
  929. rxkb_context_unref(ctx);
  930. }
  931. static void
  932. test_invalid_include(void)
  933. {
  934. struct rxkb_context *ctx;
  935. ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
  936. assert(ctx);
  937. assert(!rxkb_context_include_path_append(ctx, "/foo/bar/baz/bat"));
  938. assert(!rxkb_context_parse_default_ruleset(ctx));
  939. rxkb_context_unref(ctx);
  940. }
  941. /* Check that libxml2 error handler is reset after parsing */
  942. static void
  943. test_xml_error_handler(void)
  944. {
  945. struct test_model system_models[] = { {NULL} };
  946. struct test_layout system_layouts[] = { {NULL} };
  947. struct test_option_group system_groups[] = { { NULL } };
  948. struct rxkb_context *ctx;
  949. ctx = test_setup_context(system_models, NULL,
  950. system_layouts, NULL,
  951. system_groups, NULL);
  952. assert(ctx);
  953. rxkb_context_unref(ctx);
  954. const char invalid_xml[] = "<test";
  955. /* This should trigger a segfault if error handler is not reset, because
  956. * else it would use our error handler with an invalid (freed) context. */
  957. xmlDocPtr doc = xmlParseMemory (invalid_xml, strlen(invalid_xml));
  958. assert(!doc);
  959. xmlFreeDoc (doc);
  960. xmlCleanupParser();
  961. }
  962. int
  963. main(void)
  964. {
  965. test_init();
  966. /* Reject unsupported flags */
  967. assert(!rxkb_context_new(-1));
  968. assert(!rxkb_context_new(0xffff));
  969. test_xml_error_handler();
  970. test_no_include_paths();
  971. test_invalid_include();
  972. test_load_basic();
  973. test_load_full();
  974. test_load_merge();
  975. test_load_merge_no_overwrite();
  976. test_load_languages();
  977. test_load_invalid_languages();
  978. test_popularity();
  979. return 0;
  980. }