#!/usr/bin/env python3 # Generates the expected values for taicpp/tests/mplex_core_test.cpp by running the real # ../tai.py classes (MplexCommandParser, MplexArgs, MplexStyle, MplexOptions, MplexConfig, # MplexFormat, MplexLayout). Run from taicpp/: python3 -I tests/mplex_core_gen.py # Writes build/core_cases.txt and build/core_cfg/*.toml (nothing else). import importlib.util import os import random import sys import time HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.dirname(HERE) os.environ["TZ"] = "UTC" time.tzset() os.environ["VISUAL"] = "nvi" os.environ.pop("EDITOR", None) spec = importlib.util.spec_from_file_location("tai_mod", os.path.join(ROOT, "..", "tai.py")) tai = importlib.util.module_from_spec(spec) sys.modules["tai_mod"] = tai spec.loader.exec_module(tai) OUT = os.path.join(ROOT, "build") CFG = os.path.join(OUT, "core_cfg") os.makedirs(CFG, exist_ok=True) rng = random.Random(20260701) lines = [] def esc(text): out = [] for ch in text: o = ord(ch) if ch == "\\": out.append("\\\\") elif ch == "\n": out.append("\\n") elif ch == "\t": out.append("\\t") elif ch == "\r": out.append("\\r") elif o < 0x20 and ch not in "": out.append("\\x%02x" % o) else: out.append(ch) return "".join(out) def emit(*fields): lines.append("\t".join(esc(str(f)) for f in fields)) def enc_list(items): return str(len(items)) + "".join("\x1f" + str(x) for x in items) def outcome(func): try: return "OK:" + func() except tai.MplexError as exc: return "ERR:" + str(exc) except Exception as exc: # noqa: BLE001 return "EXC:" + type(exc).__name__ # --------------------------------------------------------------------------- parser P = tai.MplexCommandParser parser_inputs = [ "", " ", "a", "a b", " a b ", "a;b", "a ; b", "a;;b;", ";", "a\nb", "a\\\nb", "a\\b", "a\\", "\\", "'a b'", "'a;b' c", "'unterminated", '"unterminated', '"a b"', '"a\\nb"', '"a\\tb\\e\\r\\q"', '"a\\"b"', '"a\\', '"\\', "a'b c'd", 'a"b c"d', "{ a b }", "{a;b}", "{ send-keys -X cancel }", "x {a b}", "{a {b} c}", "{a '}' c}", '{a "}" c}', "{a \\} c}", "{", "{a", "{{a}", "a { b }", "a{b}", "{a}b", "{a} {b}", "{}", "{ }", "command-prompt -I \"#S\" { rename-session \"%%\" }", "confirm-before -p \"kill-window #W? (y/n)\" kill-window", "set -g status-left '[#S] ' ; set -g mouse on", "a\tb", "a\rb", "café über", "'café'", '"\\é"', "\\é", "a;b\\;c", "a \\; b", "x '' y", 'x "" y', "''", '""', "a''b", "a\"\"b", "send-keys -l 'hello world' ; send-keys Enter", "if-shell 'true' { display ok } { display no }", "{\n one\n two\n}", "{ a } ; { b }", "bind-key -T prefix '|' split-window -h", "\\\\", "\\'", "'\\'", "\"\\\\\"", "{'}", "{\"}", "{a\\", "{'a\\'}", "\u0001\u0002", "a\x00b", "; ;", ] alphabet = ["a", "b", " ", " ", "'", '"', "\\", "{", "}", ";", "\n", "$", "é", "#", "n", "t", "-", "x y", "{a}", "'q'", '"w"'] for _ in range(1500): parser_inputs.append("".join(rng.choice(alphabet) for _ in range(rng.randint(1, 14)))) for text in parser_inputs: def run(text=text): return "\x1e".join("\x1f".join(words) if words else "" for words in P.parse(text)) + ("|%d" % len(P.parse(text))) emit("P", text, outcome(run)) words_for_quote = ["", "a", "a b", "a-b_c.d/e:f=g%h@i+j,k", "a\"b", "a\\b", "a$b", "café", "éè", "-", "--x", "x y z", "'", "☃", "1", "a;b", "{}", "\n", "\t"] for word in words_for_quote: emit("Q", word, P.quote(word)) # --------------------------------------------------------------------------- args A = tai.MplexArgs def flags_dump(args): parts = [] for key in sorted(args.flags): vals = ["T" if v is True else "S" + v for v in args.flags[key]] parts.append(key + ":" + enc_list(vals)) gets = [] ints = [] for key in sorted(args.flags): gets.append(key + "=" + repr(args.get(key))) gets.append(key + "*" + repr(args.all(key))) try: ints.append(key + "=" + repr(args.int(key, 7))) except tai.MplexError as exc: ints.append(key + "=ERR " + str(exc)) return "\x1e".join([enc_list(parts), enc_list(args.positional), enc_list(gets), enc_list(ints)]) arg_values = ["", "x", "12", "-3", " 4 ", "1_0", "abc", "-", "--", "-x", "café", "0", "+5", "٣", "9" * 30] for name, (aliases, spec_text, low, high) in tai.MPLEX_COMMAND_SPECS.items(): letters = [c for c in spec_text if c != ":"] takes = {spec_text[i - 1] for i in range(1, len(spec_text)) if spec_text[i] == ":"} cases = [] cases.append([]) cases.append(["pos"]) cases.append(["pos1", "pos2"]) cases.append(["a", "b", "c", "d"]) cases.append(["--"]) cases.append(["--", "-x"]) cases.append(["-"]) cases.append(["-q"]) cases.append(["-é"]) cases.append(["-" + (letters[0] if letters else "z")]) for _ in range(40): words = [] for _i in range(rng.randint(0, 6)): roll = rng.random() if roll < 0.45 and letters: combo = "".join(rng.choice(letters) for _k in range(rng.randint(1, 3))) words.append("-" + combo) elif roll < 0.6 and takes: letter = rng.choice(sorted(takes)) if rng.random() < 0.5: words.append("-" + letter + rng.choice(arg_values)) else: words.append("-" + letter) words.append(rng.choice(arg_values)) elif roll < 0.7: words.append("--") else: words.append(rng.choice(arg_values)) cases.append(words) for words in cases: def run(name=name, spec_text=spec_text, words=words, low=low, high=high): return flags_dump(A.parse(name, spec_text, words, low, high)) emit("A", name, spec_text, low, -1 if high is None else high, enc_list(words), outcome(run)) # --------------------------------------------------------------------------- style S = tai.MplexStyle colors = ["", "default", "terminal", "red", "RED", " Blue ", "brightred", "brightwhite", "bright", "brightx", "colour0", "colour255", "colour256", "color5", "colour", "colour 5", "colour005", "colour²", "colour٣", "#ff0000", "#FFAA00", "#12345", "#1234567", "#gg0000", "# 1 2 3", "#+f+f+f", "#-f0000", "# f 0 0 ", "#0x0000", "#_f0000", "#f_0000", "#ff_ff_", "colour" + "9" * 30, "nonsense", "é", "BrightBlack"] for text in colors: for bg in (False, True): emit("C", text, int(bg), outcome(lambda text=text, bg=bg: repr(S.color(text, bg)))) frags = [None, "31", "42", "93", "104", "38;5;12", "48;5;200", "38;2;1;2;3", "48;2;255;0;128"] texts = ["", "default", "bg=green,fg=black", "bold", "noattr,bg=yellow,fg=black", "fg=red bg=blue", "fg=default", "nobold", "none", "bold,dim,italics,underscore,blink,reverse,hidden,strikethrough", "bright", "nodim,noitalics", "align=left,list=on", "range=left", "fill=red", "push-default", "pop-default", "norange", "nolist", "fg=bogus", "bg=#zzzzzz", "xyz", "no", "nobogus", " , ,", "FG=RED,BG=Blue,BOLD", "fg=colour196,bg=colour21,underline", "fg=#112233,bg=#445566", "default,bold", "bold,default", "fg=red,default,bg=blue", "noreverse", "reverse,noreverse", "underline,nounderline", "dim,nobold", "é"] for text in texts: for base in [(None, None, 0), ("31", "42", 5), ("38;5;12", None, 255)]: for reset in (None, (None, "104", 2)): def run(text=text, base=base, reset=reset): r = S.parse(text, base, reset) return "|".join(["-" if x is None else str(x) for x in r]) emit("S", text, enc_list(["-" if x is None else x for x in base[:2]]) + "\x1e" + str(base[2]), "-" if reset is None else enc_list([reset[0] or "-", reset[1] or "-"]) + "\x1e" + str(reset[2]), outcome(run)) for fg in frags: for bg in frags: for attr in (0, 1, 2, 4, 8, 16, 32, 64, 128, 255, 37): emit("G", fg or "-", bg or "-", attr, S.sgr(fg, bg, attr)) # --------------------------------------------------------------------------- options O = tai.MplexOptions def show_val(v): if v is True: return "bool:True" if v is False: return "bool:False" if isinstance(v, int): return "int:%d" % v return "str:" + v samples = { "str": ["", "x", "hello world", "#{x}", "café"], "bool": ["on", "off", "yes", "no", "true", "false", "1", "0", " ON ", "TRUE", "maybe", "", "2", "é"], "key": ["C-a", "None", "none", "", " none ", "M-x", "F5", "BTab", "C-M-a", "S-Up", "^a", "Space", "bogus", "C-", "C-é", "Escape", "PageUp", " "], "colour": ["red", "default", "colour5", "#ffffff", "bogus", "", "brightblue", "colour999"], "style": ["bg=red", "default", "bold,fg=blue", "bogus", "", "#{?x,a,b}", "fg=zz", "noattr,bg=yellow,fg=black"], "int": ["0", "1", "5", "-1", "10001", "x", "", " 7 ", "1_0", "999999999999", "3.5", "+4", "٤"], "choice": ["vi", "emacs", "top", "bottom", "left", "centre", "center", "right", "12", "24", "single", "double", "heavy", "simple", "on", "external", "off", "VI", "", "x"], } names = [d for d in O.DEFS] for name in names: kind = O.DEFS[name][0] base_kind = kind.split(":")[0] pool = list(samples[base_kind]) if base_kind == "int": _t, lo, hi = kind.split(":") pool += [lo, hi, str(int(lo) - 1), str(int(hi) + 1)] for raw in pool: emit("O", name, raw, outcome(lambda name=name, raw=raw: show_val(O.check(name, raw)))) for raw in ["x"]: emit("O", "no-such-option", raw, outcome(lambda: show_val(O.check("no-such-option", "x")))) emit("O", "mouse", raw, outcome(lambda: show_val(O.check("mouse", "on")))) emit("D", enc_list([n for n in O.DEFS]), enc_list([show_val(O.DEFS[n][1]) + "~" + O.DEFS[n][0] for n in O.DEFS]), O.WORD_SEPARATORS) def dump_levels(levels): return "\x1e".join(enc_list(["%s=%s" % (k, show_val(v)) for k, v in lvl.values.items()]) for lvl in levels) for scenario in range(120): g = O() s = O(g) w = O(s) levels = [g, s, w] emit("OBEGIN", scenario) pool_names = rng.sample(names, 8) + ["status", "prefix", "mode-keys", "status-left", "history-limit", "bogus-name"] for _ in range(40): lvl = rng.randrange(3) obj = levels[lvl] name = rng.choice(pool_names) op = rng.choice(["set", "set", "set", "append", "get", "get", "toggle", "unset", "is_set", "show"]) if name in O.DEFS: kind = O.DEFS[name][0].split(":")[0] raw = rng.choice(samples[kind]) else: raw = "x" if op in ("set", "append"): res = outcome(lambda: show_val(obj.set(name, raw, append=(op == "append")))) elif op == "get": res = outcome(lambda: show_val(obj.get(name))) elif op == "toggle": res = outcome(lambda: (obj.toggle(name), "")[1]) elif op == "unset": res = outcome(lambda: (obj.unset(name), "")[1]) elif op == "is_set": res = "OK:" + str(obj.is_set(name)) else: res = outcome(lambda: O.show(obj.get(name))) emit("OOP", lvl, op, name, raw, res, dump_levels(levels)) # --------------------------------------------------------------------------- config cfg_samples = [ "", "[options]\nprefix = \"C-a\"\nprefix2 = \"F12\"\nbase-index = 1\nmode-keys = \"vi\"\nstatus-position = \"top\"\n", "[options]\nstatus = false\nrenumber-windows = true\nhistory-limit = 10000\nstatus-left = \"[#S] \"\nstatus-style = \"bg=blue,fg=white\"\n", "[options]\nbogus = 1\nstatus = 3.5\nprefix = [1]\nhistory-limit = 999999\nbase-index = \"x\"\nmode-keys = \"nano\"\nmouse = true\n", "[options]\nstatus-left = 5\nprefix = 5\n", "[options]\nwhen = 1979-05-27T07:32:00Z\nbase-index = 2024-01-01\n", "[unknown]\nx = 1\n[options]\nstatus = true\n[other]\ny = 2\n", "options = 3\nkeys = 4\n", "options = \"a\"\n[keys]\nx = 1\n", "[keys]\nclear = [\"prefix\"]\n[keys.prefix]\n\"|\" = \"split-window -h\"\n\"-\" = \"split-window -v\"\n\"C-b\" = false\nr = { command = \"source-file ~/.tai/mplex.toml\", note = \"reload\", repeat = false }\nUp = { command = \"select-pane -U\", repeat = true }\n", "[keys.prefix]\n\"C-b\" = \"\"\nz = \"\"\nx = false\nbogus-key = \"split-window\"\n\"C-\" = \"split-window\"\nq = \"nosuchcommand\"\nw = \"split-window ; nosuchcommand\"\ne = \"\"\n", "[keys.root]\n\"M-Left\" = \"previous-window\"\n\"M-Right\" = \"next-window\"\n[keys.copy-mode-vi]\ny = \"send-keys -X copy-selection-and-cancel\"\n[keys.mytable]\nA = \"display-message hi\"\n", "[keys]\nclear = \"prefix\"\n[keys.prefix]\na = 1\nb = true\nc = [\"x\"]\nd = { command = 5 }\ne = { cmd = \"split-window\" }\nf = { command = \"split-window\", repeat = 1, note = 5 }\ng = { command = \" \" }\nh = { repeat = true }\n", "[keys]\nclear = [\"prefix\", \"root\", 5, \"newtable\"]\n[keys.prefix]\nc = \"new-window\"\n", "[keys]\nprefix = \"split-window\"\n[keys.root]\n", "[keys]\nclear = [\"copy-mode\"]\n", "[keys.prefix]\nSpace = { command = \"next-layout\", repeat = true, note = \"café\" }\n\"M-x\" = \"unsupported-cmd\"\nd = \"detach\"\nk = \"kill-ser\"\nl = \"display-menu\"\n", "[keys.prefix]\nk = \"kill\"\n", "[keys.prefix]\na = \"split-window { oops\"\nb = 'display-message \"unterminated'\n", "this is not toml\n", "[options\nstatus = true\n", "[options]\nstatus = true\nstatus = false\n", "[options]\nstatus = \"unterminated\n", "[options]\nprefix = \"C-é\"\n", "x = 1\ny = 2\n[options]\nstatus = true\n", ] # many error producers to hit the ERRORS_MAX cap (50) many = "[options]\n" + "".join("opt%d = 1\n" % i for i in range(70)) cfg_samples.append(many) many_keys = "[keys.prefix]\n" + "".join("bogus%d = \"nosuchcommand\"\n" % i for i in range(60)) cfg_samples.append(many_keys) # mutate good samples by deleting or inserting characters (random but deterministic) good = cfg_samples[1] + cfg_samples[9] + cfg_samples[11] for _ in range(60): pieces = list(good) for _k in range(rng.randint(1, 4)): pos = rng.randrange(len(pieces)) if rng.random() < 0.5: del pieces[pos] else: pieces.insert(pos, rng.choice(list("\"'=[]{}.,\n# -aZ0"))) cfg_samples.append("".join(pieces)) def cfg_dump(config): opts = enc_list(["%s=%s" % (k, show_val(v)) for k, v in config.options.values.items()]) tabs = [] for tname, table in config.tables.items(): rows = ["%s\x1d%s\x1d%s\x1d%s" % (k, b.command, int(bool(b.repeat)), b.note) for k, b in table.items()] tabs.append(tname + "\x1c" + enc_list(rows)) return "\x1e".join([opts, enc_list(tabs), enc_list(config.errors), str(config.skipped), config.path]) def write_cfg(index, data, mode="w"): path = os.path.join("build", "core_cfg", "%03d.toml" % index) with open(os.path.join(ROOT, path), "wb") as handle: handle.write(data if isinstance(data, bytes) else data.encode("utf-8")) return path os.chdir(ROOT) index = 0 for text in cfg_samples: path = write_cfg(index, text) index += 1 emit("K", path, cfg_dump(tai.MplexConfig.load(path))) for data in (b"\xff\xfe[options]\n", b"[options]\nstatus = \"\xe2\x82\"\n", b"abc\xc3", b"\xc3\x28", b"\xe0\x80\x80", b"\xf5\x80\x80\x80", b"[options]\n\x80\n", b"\xed\xa0\x80"): path = write_cfg(index, data) index += 1 emit("K", path, cfg_dump(tai.MplexConfig.load(path))) big = write_cfg(index, b"#" * (tai.MPLEX_CONFIG_MAX + 1)) index += 1 emit("K", big, cfg_dump(tai.MplexConfig.load(big))) exact = write_cfg(index, b"#" * tai.MPLEX_CONFIG_MAX) index += 1 emit("K", exact, cfg_dump(tai.MplexConfig.load(exact))) emit("K", "build/core_cfg/missing.toml", cfg_dump(tai.MplexConfig.load("build/core_cfg/missing.toml"))) os.makedirs("build/core_cfg/adir.toml", exist_ok=True) emit("K", "build/core_cfg/adir.toml", cfg_dump(tai.MplexConfig.load("build/core_cfg/adir.toml"))) emit("K", "build/core_cfg/missing/deeper.toml", cfg_dump(tai.MplexConfig.load("build/core_cfg/missing/deeper.toml"))) emit("K", "", cfg_dump(tai.MplexConfig.load(""))) emit("DEFAULTPATH", tai.MplexConfig.default_path("/home/x/.tai"), tai.MplexConfig.default_path("rel")) fresh = tai.MplexConfig() emit("K", "-", cfg_dump(fresh)) emit("LOOKUP", "".join("%s/%s=%s;" % (t, k, fresh.lookup(t, k) is not None) for t, k in (("prefix", "C-b"), ("prefix", "nope"), ("root", "C-b"), ("nonexistent", "x"), ("copy-mode", "Escape")))) for t, k in (("prefix", "C-b"), ("prefix", "nope"), ("nonexistent", "x")): emit("UNBIND", t, k, int(fresh.unbind(t, k)), cfg_dump(fresh)[:0]) # --------------------------------------------------------------------------- format F = tai.MplexFormat VARS = { "session_name": "work", "session_id": "$3", "window_index": "2", "window_name": "vim edit", "window_flags": "*Z", "pane_index": "1", "pane_title": "host:/home/user/project", "pane_id": "%7", "host": "box.example.com", "host_short": "box", "empty": "", "zero": "0", "one": "1", "num_a": "7", "num_b": "2", "num_c": "-3.5", "big": "1e30", "tiny": "0.1", "path": "/usr/local/bin/tool.sh", "path2": "/", "uni": "café über ☃ ok", "word": "hello", "sentence": "the quick brown fox", "stamp": "86400", "negstamp": "-1.5", "badstamp": "abc", "hugestamp": "1e30", "fl": "2.5", "sp": "a b", "special": "a b;c'd\"e$f#g{h}i[j](k)*l?mo&p|q~r\\s", "pane_in_mode": "0", "synchronize-panes": "0", "inf": "inf", "nan": "nan", "ws": " 12 ", "a_b": "x", "cmd": "ls -l", } for k, v in VARS.items(): emit("V", k, v) def resolver(name): if name.startswith("("): return "SH<" + name[1:] + ">" return VARS.get(name) fmt_cases = [ "", "plain", "#S #I #W #F #P #T #H #h #D", "##", "#", "a#", "##{x}", "#{session_name}", "#{missing}", "[#{session_name}] ", "#{", "#{unterminated", "#(", "#(echo hi)", "#(echo #(inner) x)", "#(a", "#[fg=red]x#[default]", "#[bold", "#[a]#[b]", "x#,y", "x#}y", "#{?empty,yes,no}", "#{?one,yes,no}", "#{?zero,yes,no}", "#{?missing,yes,no}", "#{?one,yes}", "#{?one}", "#{?empty,a,zero,b,one,c,d}", "#{?empty,a,zero,b,empty,c,d}", "#{?empty,a,zero,b}", "#{?#{one},A,B}", "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}", "#{?window_flags,#{window_flags}, }", "#{?one,a#,b,c}", "#{?one,#{?zero,x,y},z}", "#{==:#{num_a},7}", "#{==:a,a}", "#{==:a,b}", "#{!=:a,b}", "#{!=:a,a}", "#{<:1,2}", "#{<:10,9}", "#{<:b,a}", "#{>:b,a}", "#{<=:a,a}", "#{>=:2,10}", "#{>=:abc,abd}", "#{&&:1,1}", "#{&&:1,0}", "#{||:0,0}", "#{||:0,1}", "#{&&:,1}", "#{||:#{one},#{zero}}", "#{e|+:7,2}", "#{e|-:7,2}", "#{e|*:7,2}", "#{e|/:7,2}", "#{e|%:7,2}", "#{e|+:#{num_a},#{num_b}}", "#{e|/:1,0}", "#{e|%:1,0}", "#{e|*:1e30,1e30}", "#{e|+:0.1,0.2}", "#{e|/:1,3}", "#{e|*:big,1}", "#{e|+:x,1}", "#{e|==:1,1}", "#{e|!=:1,1}", "#{e|<:1,2}", "#{e|>:1,2}", "#{e|<=:1,1}", "#{e|>=:1,2}", "#{e|?:1,2}", "#{e|%:-7,3}", "#{e|%:7,-3}", "#{e|%:-7.5,2}", "#{e|+:inf,1}", "#{e|*:inf,0}", "#{e|-:inf,inf}", "#{e|+:nan,1}", "#{e|*:1e300,1e300}", "#{e|*:-1,0}", "#{e|+:123456789012345678,1}", "#{e|*:99999999999,99999999999}", "#{e|*:1e18,10}", "#{e|*:1e19,10}", "#{e|+:2e308,0}", "#{m:*vim*,#{window_name}}", "#{m:vim,#{window_name}}", "#{m:V*,#{window_name}}", "#{m/i:V*,#{window_name}}", "#{m/r:^vim,#{window_name}}", "#{m/r:[,#{window_name}}", "#{m/ri:VIM,#{window_name}}", "#{m/r:v.m,x}", "#{m:??m*,#{window_name}}", "#{m:[a-z]*,#{word}}", "#{m:[!a-z]*,#{word}}", "#{m:,}", "#{=3:window_name}", "#{=-3:window_name}", "#{=0:window_name}", "#{=100:window_name}", "#{=-100:window_name}", "#{=/3/...:window_name}", "#{=/-3/...:window_name}", "#{=3:uni}", "#{=-4:uni}", "#{=21:pane_title}", "#{=x:window_name}", "#{=:window_name}", "#{=/:window_name}", "#{=/5:window_name}", "#{=/5/a/b:sentence}", "#{=+3:sentence}", "#{= 3:sentence}", "#{E:status}", "#{E:#{word}}", "#{T:#{word}}", "#{q:special}", "#{q:sp}", "#{n:word}", "#{n:uni}", "#{n:empty}", "#{b:path}", "#{d:path}", "#{b:path2}", "#{d:path2}", "#{d:word}", "#{!:one}", "#{!:zero}", "#{!:empty}", "#{!:missing}", "#{!!:one}", "#{!!:word}", "#{!!:zero}", "#{l:literal text}", "#{l:#{word}}", "#{l:a:b}", "#{s/l/L/:word}", "#{s/l+/_/:sentence}", "#{s/(o)/[\\1]/:sentence}", "#{s/[/x/:word}", "#{s/a/b:word}", "#{s//-/:word}", "#{s/ /_/:sentence}", "#{s/e/E/g:sentence}", "#{s/(?Pe)/<\\g>/:word}", "#{s/a/\\q/:word}", "#{s/^/>/:word}", "#{s/$/ 1: idx = rng.randrange(len(leaves)) victim = leaves[idx] root = L.remove(root, victim) panes = [p for p in panes if p is not victim.pane] + [] emit("LOP", "remove", str(idx), tree_dump(root)) L.assign(root, 0, 0, W, H) sync(root) emit("LOP", "assign", "%d %d" % (W, H), tree_dump(root)) elif roll < 0.6: idx = rng.randrange(len(leaves)) kind = rng.choice(["lr", "tb"]) delta = rng.randint(-12, 12) prefer = rng.random() < 0.5 res = outcome(lambda: (L.resize_by(leaves[idx], kind, delta, prefer), tree_dump(root))[1]) emit("LOP", "resize_by", "%d %s %d %d" % (idx, kind, delta, int(prefer)), res) L.assign(root, 0, 0, W, H) sync(root) emit("LOP", "assign", "%d %d" % (W, H), tree_dump(root)) elif roll < 0.72: idx = rng.randrange(len(leaves)) kind = rng.choice(["lr", "tb"]) size = rng.randint(1, 60) res = outcome(lambda: (L.resize_to(leaves[idx], kind, size), tree_dump(root))[1]) emit("LOP", "resize_to", "%d %s %d" % (idx, kind, size), res) L.assign(root, 0, 0, W, H) sync(root) emit("LOP", "assign", "%d %d" % (W, H), tree_dump(root)) elif roll < 0.82: W = rng.choice([1, 2, 3, 5, 8, 20, 40, 80, 120, 200, 233]) H = rng.choice([1, 2, 3, 4, 6, 12, 24, 40, 50, 77]) L.assign(root, 0, 0, W, H) sync(root) emit("LOP", "assign", "%d %d" % (W, H), tree_dump(root)) elif roll < 0.92: for p in panes: p.last_active = float(rng.randint(0, 5)) emit("LOP", "active", enc_list([int(p.last_active) for p in panes]), "") for p in panes: for d in "LRUD": got = L.neighbour([leaf.pane for leaf in root.leaves()], p, d, W, H) emit("LOP", "neighbour", "%d %s %d %d" % (p.id, d, W, H), "-" if got is None else got.pane.id if hasattr(got, "pane") else got.id) else: name = rng.choice(names_l) count = rng.randint(1, 9) mw = rng.choice([1, 5, 20, 80, 200]) mh = rng.choice([1, 5, 12, 24, 100]) pset = [Pane(counter + i) for i in range(count)] counter += count panes = pset root = L.preset(name, pset, W, H, mw, mh) emit("LOP", "preset", "%s %d %d %d %d %d" % (name, count, W, H, mw, mh), tree_dump(root)) L.assign(root, 0, 0, W, H) sync(root) emit("LOP", "assign", "%d %d" % (W, H), tree_dump(root)) for name in names_l: for count in range(1, 10): for (W, H) in [(1, 1), (2, 2), (3, 7), (10, 5), (20, 10), (80, 24), (80, 23), (81, 25), (120, 40), (200, 50), (233, 61)]: for (mw, mh) in [(80, 24), (1, 1), (200, 100)]: pset = [Pane(i) for i in range(count)] root = L.preset(name, pset, W, H, mw, mh) pre = tree_dump(root) L.assign(root, 0, 0, W, H) sync(root) emit("LPRESET", "%s %d %d %d %d %d" % (name, count, W, H, mw, mh), pre, tree_dump(root)) for total in range(-2, 40): for count in range(1, 9): for first in (True, False): emit("LSHARES", total, count, int(first), enc_list(L.shares(total, count, first))) open(os.path.join(OUT, "core_cases.txt"), "w", encoding="utf-8", newline="\n").write("\n".join(lines) + "\n") print("cases:", len(lines))