"""The title screen. Most of what matters here is when it does *not* appear. Everything in this program can be piped into something else, and a banner in the middle of a table somebody is parsing is corruption rather than decoration. """ from rich.console import Console from bandsaunter import splash def _plain(console: Console, drawing) -> str: with console.capture() as caught: drawing() return caught.get() # --------------------------------------------------------------------------- # The drawing # --------------------------------------------------------------------------- def test_both_names_come_out_as_four_rows_of_braille(): for word in (splash.SCANNER_NAME, splash.BROWSER_NAME): rows = splash.banner(word) assert len(rows) == splash.ROWS assert all(all(0x2800 <= ord(c) <= 0x28FF for c in row) for row in rows), "something in there is not braille" assert any(c != "\u2800" for row in rows for c in row) def test_the_names_are_written_the_way_they_are_spelled(): """Not shouted. Braille gives four times the vertical detail a block does, which is what makes room for two heights of letter at once.""" assert splash.SCANNER_NAME == "BandSaunter" assert splash.BROWSER_NAME == "SaunterBrowse" assert set("BS") <= set(splash.CAPS) assert "a" in splash.LOWER and "d" in splash.ASCENDERS def test_a_capital_stands_taller_than_the_letter_beside_it(): """The point of the whole exercise: if both were drawn the same height the name would read as BANDSAUNTER, which is not its name.""" def top_of(word): drawn = splash.pixels(word) return next(i for i, row in enumerate(drawn) if row.strip()) assert top_of("B") < top_of("a"), "the capital does not rise" assert top_of("d") == top_of("B"), "the ascender does not reach" assert top_of("t") < top_of("a"), "the t does not rise at all" assert top_of("o") == top_of("a"), "two x-height letters disagree" def test_every_letter_sits_on_the_same_baseline(): def bottom_of(word): drawn = splash.pixels(word) return max(i for i, row in enumerate(drawn) if row.strip()) heights = {ch: bottom_of(ch) for ch in "BSandutersow"} assert len(set(heights.values())) == 1, heights def test_both_names_fit_an_eighty_column_terminal(): """Which is the narrowest terminal anybody still has, and the width a banner has to be cut to rather than wrapped at.""" for word in (splash.SCANNER_NAME, splash.BROWSER_NAME): assert max(len(r) for r in splash.banner(word)) <= 78, word def test_every_letter_the_two_names_need_has_a_glyph(): """A missing one would silently shorten the name rather than fail.""" for word in (splash.SCANNER_NAME, splash.BROWSER_NAME): for letter in word: assert letter in splash.FONT, letter def test_a_stroke_is_two_dots_thick_in_both_directions(): """One dot thick reads as a dotted line rather than a line, which is the one thing that makes drawing in braille difficult. Doubling the small font is what avoids it.""" doubled = splash._double(("#.",)) assert doubled == ["##..", "##.."] def test_a_letter_with_no_glyph_is_left_out_rather_than_drawn_as_a_hole(): assert splash.banner("Band!!") == splash.banner("Band") assert splash.banner("!!!") == [] assert splash.banner("") == [] # And an uppercase name is no longer the same as a lowercase one, which # is the whole change: the font has two heights now. assert splash.banner("Band") != splash.banner("BAND") def test_the_gradient_is_cold_the_whole_way_along(): """Digital blues, cyans and white, and nothing warm anywhere. Checked as an invariant rather than by naming the five stops: blue is never the weakest channel and red is never the strongest, which is what "blue through cyan to white" *means* and what any warm colour slipping back in would break. This is deliberately not the waterfall ramp the rest of the program draws in -- that one has to run to red because it stands in for a spectrum. """ for step in range(0, 101): style = splash._colour(step / 100) red, green, blue = (int(v) for v in style[4:-1].split(",")) assert blue >= green >= red, (step, style) assert blue >= 120, f"too dark to read at {step}: {style}" def test_the_gradient_ends_pale_and_starts_deep(): """A gradient that did not travel would be a colour.""" start = [int(v) for v in splash._colour(0.0)[4:-1].split(",")] end = [int(v) for v in splash._colour(1.0)[4:-1].split(",")] assert sum(end) > sum(start) * 2, "it barely brightens" assert start[2] >= 120 and end[1] >= 200 def test_the_gradient_runs_from_one_end_of_the_ramp_to_the_other(): first, last = splash._colour(0.0), splash._colour(1.0) assert first != last assert first.startswith("rgb(") and last.startswith("rgb(") # And it is clamped rather than extrapolated off either end. assert splash._colour(-5.0) == first assert splash._colour(5.0) == last def test_the_lines_under_it_are_drawn_in_the_order_they_were_given(): console = Console(width=100, force_terminal=False) shown = _plain(console, lambda: splash.show( console, splash.SCANNER_NAME, ("Conceived by: The Dust Council", "100% AI Coded by Claude Code."), force=True)) assert "Conceived by: The Dust Council" in shown assert "100% AI Coded by Claude Code." in shown assert shown.index("Conceived") < shown.index("100%") # --------------------------------------------------------------------------- # When it stays out of the way # --------------------------------------------------------------------------- def test_nothing_is_drawn_when_the_output_is_not_a_terminal(): """The one that matters. A table piped into something else must come out as a table.""" console = Console(width=100, force_terminal=False) shown = _plain(console, lambda: splash.show(console, splash.SCANNER_NAME, ("a", "b"))) assert shown == "" assert splash.wanted(console) is False def test_no_splash_turns_it_off_on_a_terminal_too(): console = Console(width=100, force_terminal=True) assert splash.wanted(console) is True assert splash.wanted(console, no_splash=True) is False shown = _plain(console, lambda: splash.show(console, splash.SCANNER_NAME, ("a",), no_splash=True)) assert shown == "" def test_the_environment_can_turn_it_off_for_good(monkeypatch): """For anybody who would rather it did not, without having to remember a flag on every invocation.""" console = Console(width=100, force_terminal=True) monkeypatch.setenv("BANDSAUNTER_NO_SPLASH", "1") assert splash.wanted(console) is False shown = _plain(console, lambda: splash.show(console, splash.SCANNER_NAME, ("a",))) assert shown == "" def test_it_says_whether_it_drew_anything(): console = Console(width=100, force_terminal=False) assert splash.show(console, splash.SCANNER_NAME, ("a",)) is False assert splash.show(console, splash.SCANNER_NAME, ("a",), force=True) is True assert splash.show(console, "!!!", ("a",), force=True) is False def test_nothing_sleeps_when_nothing_is_drawn(monkeypatch): """The pause is for a program about to take the screen over. Paying it with no banner drawn would be a tenth of a second added to every piped command for nothing.""" import time slept = [] monkeypatch.setattr(time, "sleep", lambda s: slept.append(s)) console = Console(width=100, force_terminal=False) splash.show(console, splash.BROWSER_NAME, ("a",), pause=5.0) assert slept == [] splash.show(console, splash.BROWSER_NAME, ("a",), pause=0.25, force=True) assert slept == [0.25] # --------------------------------------------------------------------------- # Where it is wired in # --------------------------------------------------------------------------- def test_both_programs_can_be_told_not_to_draw_one(): from bandsaunter.browse import build_parser as browse_parser from bandsaunter.cli import build_parser as cli_parser assert cli_parser().parse_args(["--no-splash", "devices"]).no_splash assert not cli_parser().parse_args(["devices"]).no_splash assert browse_parser().parse_args(["--no-splash"]).no_splash assert not browse_parser().parse_args([]).no_splash def test_the_scanner_draws_its_own_name(monkeypatch): drawn = [] monkeypatch.setattr(splash, "show", lambda console, word, lines=(), **kw: drawn.append((word, tuple(lines))) or False) from bandsaunter import cli cli.main(["devices"]) assert drawn and drawn[0][0] == splash.SCANNER_NAME lines = drawn[0][1] assert lines[0] == "Conceived by: The Dust Council" assert lines[1] == "100% AI Coded by Claude Code." def test_the_browser_draws_its_own_name_and_waits_a_moment(monkeypatch, tmp_path): drawn = [] def fake(console, word, lines=(), **kw): drawn.append((word, tuple(lines), kw.get("pause", 0.0))) return False monkeypatch.setattr(splash, "show", fake) from bandsaunter import browse # Straight back out again: the banner is drawn before anything else. browse.main([str(tmp_path), "--callsigns"]) assert drawn and drawn[0][0] == splash.BROWSER_NAME assert drawn[0][1][0] == "Conceived by: The Dust Council" assert drawn[0][1][1] == "100% AI Coded by Claude Code." # The browser runs on the alternate screen, so the banner needs a beat. assert drawn[0][2] > 0