|
8 | 8 | // Official repository: https://github.com/cppalliance/mrdocs |
9 | 9 | // |
10 | 10 |
|
| 11 | +#include <lib/ConfigImpl.hpp> |
| 12 | +#include <lib/CorpusImpl.hpp> |
| 13 | +#include <lib/Extensions/JsBinding.hpp> |
| 14 | +#include <lib/Extensions/LuaBinding.hpp> |
11 | 15 | #include <lib/Gen/script/OutputSink.hpp> |
12 | 16 | #include <lib/Gen/script/ScriptGenerator.hpp> |
13 | 17 | #include <lib/Support/Path.hpp> |
|
19 | 23 | #include <mrdocs/Support/Path.hpp> |
20 | 24 | #include <mrdocs/Support/ThreadPool.hpp> |
21 | 25 | #include <test_suite/test_suite.hpp> |
| 26 | +#include <cstdint> |
| 27 | +#include <fstream> |
| 28 | +#include <memory> |
22 | 29 | #include <string> |
23 | 30 | #include <string_view> |
24 | 31 |
|
@@ -156,6 +163,35 @@ makeJsGenerator(std::string_view src) |
156 | 163 | return JsGenerator{fn->getFunction(), ctx}; |
157 | 164 | } |
158 | 165 |
|
| 166 | +// Write `content` verbatim to `path`. Pre-existing files are truncated. |
| 167 | +void |
| 168 | +writeFile(std::string_view path, std::string_view content) |
| 169 | +{ |
| 170 | + std::ofstream os(std::string{path}, std::ios::binary | std::ios::trunc); |
| 171 | + os.write(content.data(), |
| 172 | + static_cast<std::streamsize>(content.size())); |
| 173 | +} |
| 174 | + |
| 175 | +// A generator function that ignores its argument and returns `value`, so two |
| 176 | +// registrations can be told apart by what the resolved one returns. |
| 177 | +dom::Function |
| 178 | +makeConstGenerator(std::int64_t value) |
| 179 | +{ |
| 180 | + return dom::makeVariadicInvocable( |
| 181 | + [value](dom::Array const&) -> Expected<dom::Value, Error> |
| 182 | + { |
| 183 | + return dom::Value(value); |
| 184 | + }); |
| 185 | +} |
| 186 | + |
| 187 | +// An empty in-memory configuration. The ThreadPool is stored by reference, |
| 188 | +// so the caller must keep it alive at least as long as the config. |
| 189 | +std::shared_ptr<ConfigImpl const> |
| 190 | +makeConfig(ThreadPool& pool) |
| 191 | +{ |
| 192 | + return std::make_shared<ConfigImpl>(ConfigImpl::access_token{}, pool); |
| 193 | +} |
| 194 | + |
159 | 195 | } // (anon) |
160 | 196 |
|
161 | 197 | struct ScriptGeneratorTest |
|
342 | 378 | BOOST_TEST(!runOver(gen, outDir).has_value()); |
343 | 379 | } |
344 | 380 |
|
| 381 | + // |
| 382 | + // register_generator: corpus host and the script bindings |
| 383 | + // |
| 384 | + |
| 385 | + void |
| 386 | + testHostKeepsFirstRegistration() |
| 387 | + { |
| 388 | + ThreadPool pool; |
| 389 | + CorpusImpl corpus(makeConfig(pool)); |
| 390 | + |
| 391 | + // The first registration of an id wins; a later one is ignored. |
| 392 | + corpus.registerScriptGenerator("a", makeConstGenerator(1)); |
| 393 | + corpus.registerScriptGenerator("a", makeConstGenerator(2)); |
| 394 | + |
| 395 | + BOOST_TEST(corpus.findScriptGenerator("missing") == nullptr); |
| 396 | + dom::Function const* found = corpus.findScriptGenerator("a"); |
| 397 | + BOOST_TEST(found != nullptr); |
| 398 | + if (found) |
| 399 | + { |
| 400 | + Expected<dom::Value> got = found->try_invoke(dom::Value()); |
| 401 | + BOOST_TEST(got.has_value()); |
| 402 | + if (got) |
| 403 | + { |
| 404 | + BOOST_TEST(got->getInteger() == 1); |
| 405 | + } |
| 406 | + } |
| 407 | + } |
| 408 | + |
| 409 | + void |
| 410 | + testRegisterGeneratorLua() |
| 411 | + { |
| 412 | + ThreadPool pool; |
| 413 | + CorpusImpl corpus(makeConfig(pool)); |
| 414 | + ScopedTempDirectory td("mrdocs-reggen"); |
| 415 | + BOOST_TEST(td); |
| 416 | + // A Lua extension that registers a generator leaves it findable on |
| 417 | + // the corpus by its id, and does not warn about registering nothing. |
| 418 | + std::string const script = files::appendPath(td.path(), "gen.lua"); |
| 419 | + writeFile(script, "register_generator(\"my-gen\", function(ctx) end)\n"); |
| 420 | + BOOST_TEST(runOneLuaExtension(corpus, script).has_value()); |
| 421 | + BOOST_TEST(corpus.findScriptGenerator("my-gen") != nullptr); |
| 422 | + } |
| 423 | + |
| 424 | + void |
| 425 | + testRegisterGeneratorJs() |
| 426 | + { |
| 427 | + ThreadPool pool; |
| 428 | + CorpusImpl corpus(makeConfig(pool)); |
| 429 | + ScopedTempDirectory td("mrdocs-reggen"); |
| 430 | + BOOST_TEST(td); |
| 431 | + // The JavaScript counterpart. |
| 432 | + std::string const script = files::appendPath(td.path(), "gen.js"); |
| 433 | + writeFile(script, "register_generator(\"my-gen\", function(ctx) {});\n"); |
| 434 | + BOOST_TEST(runOneJsExtension(corpus, script).has_value()); |
| 435 | + BOOST_TEST(corpus.findScriptGenerator("my-gen") != nullptr); |
| 436 | + } |
| 437 | + |
345 | 438 | void |
346 | 439 | run() |
347 | 440 | { |
|
354 | 447 | testGeneratorIteratesCorpus(); |
355 | 448 | testWriteEscapeIsError(); |
356 | 449 | testGeneratorErrorIsReported(); |
| 450 | + testHostKeepsFirstRegistration(); |
| 451 | + testRegisterGeneratorLua(); |
| 452 | + testRegisterGeneratorJs(); |
357 | 453 | } |
358 | 454 | }; |
359 | 455 |
|
|
0 commit comments