Files
Marcos Paulo 5e8020aa38
ci / ci (macos-latest) (push) Has been cancelled
ci / ci (ubuntu-latest) (push) Has been cancelled
ci / ci (windows-latest) (push) Has been cancelled
feat: Obsidian basics, editor previews, graph rework, query blocks, stacked tabs, pop-out windows
Editor previews (colour swatches, arrows, embeds), the 18 obsidian-basics tasks (find and replace
with regex, back/forward, auto-pair, footnotes, folding, search operators, split view, PDF/audio/
video embeds, spelling, drag and drop, pinned tabs, settings) and round 2: ```query blocks, stacked
tabs, pop-out windows, tasks ticked inside embeds, text around an embed, embeds that follow edits,
block menu shortcut. New crates: notescraft-regex (linear-time engine) and notescraft-syntax.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 11:21:19 -03:00

147 lines
6.3 KiB
Rust

//! Table-driven tests of [`notescraft_blocks::detect_language`]: every rule positive, near-miss
//! negatives, CRLF input, empty and hostile bodies, and the 4 KiB / 40 line bounds.
use notescraft_blocks::detect_language;
/// (body, expected label) rows: a rule positive or a near miss that must stay `None`.
#[test]
fn the_table() {
let rows: Vec<(&str, Option<&str>)> = vec![
// shebang
("#!/usr/bin/env python\nprint('hi')\n", Some("python")),
("#!/usr/bin/python\nimport os\n", Some("python")),
("#!/usr/bin/env bash\necho hi\n", Some("bash")),
("#!/bin/bash\necho hi\n", Some("bash")),
("#!/bin/sh\necho hi\n", Some("bash")),
("#!/usr/bin/env zsh\necho hi\n", Some("bash")),
("#!/usr/bin/env node\nconsole.log(1)\n", Some("javascript")),
("#!/usr/bin/env ruby\nputs 'no'\n", None),
("# not a shebang\n", None),
// php, html, xml by opener
("<?php\necho 1;\n", Some("php")),
("<?PHP\necho 1;\n", None),
("<!DOCTYPE html>\n<html><body></body></html>\n", Some("html")),
("<!doctype html>\n<p>hi</p>\n", Some("html")),
("<HTML>\n</HTML>\n", Some("html")),
("<div>no label for fragments</div>\n", None),
("<?xml version=\"1.0\"?>\n<a/>\n", Some("xml")),
// json
("{\"a\": 1}\n", Some("json")),
("[1, 2, 3]\n", Some("json")),
("`{\"a\":1}`\n", Some("json")),
("{\"a\": 1}\r\n", Some("json")),
("{a: 1}\nb: 2\n", None),
("42\n", None),
// yaml
("name: NotesCraft\nversion: 1.0\nitems:\n - one\n - two\n", Some("yaml")),
("title: a\n# a comment\n- one\nkey: value\n", Some("yaml")),
("a: {x}\nb: 1\nc: 2\n", None),
("a: b\nc: d\n", None),
("x: 1; y\nz: 2\nw: 3\n", None),
// diff
("diff --git a/x b/x\nindex 1..2 100644\n", Some("diff")),
("--- a/x\n+++ b/x\n@@ -1 +1 @@\n-old\n+new\n", Some("diff")),
("+++ b/x\n--- a/x\n", None),
("- removed\n+ added\n", None),
// shell transcripts
("$ cargo build\n$ cargo test\n", Some("bash")),
("$ ls\nfile1 file2\n$ pwd\n/home\n", None),
("$ make\n", None),
// rust
("fn main() {\n let x = 1;\n}\n", Some("rust")),
("impl Foo {\n fn bar(&self) -> u32 {\n 0\n }\n}\n", Some("rust")),
("use std::collections::HashMap;\n", Some("rust")),
("pub fn id(x: u32) -> u32 {\n x\n}\n", Some("rust")),
("let x = 1;\n", None),
("fn main() {}\n", None),
("use foo::bar;\n", None),
// python
("def add(a, b):\n return a + b\n", Some("python")),
("import os\nprint(os.getcwd())\n", Some("python")),
("from os import path\nprint(path)\n", Some("python")),
("class A:\n def x(self):\n pass\n", Some("python")),
("def f(): pass\n", None),
("import os\n", None),
("from x y\n", None),
// sql
("SELECT * FROM users;\n", Some("sql")),
("select name\nfrom t\nwhere id = 1\n", Some("sql")),
("INSERT INTO t VALUES (1);\n", Some("sql")),
("CREATE TABLE t (id INT);\n", Some("sql")),
("DELETE FROM t WHERE id = 1;\n", Some("sql")),
("UPDATE t SET a = 1 WHERE b = 2;\n", Some("sql")),
("UPDATE t SET a = 1\n", None),
("SELECT 1\n", None),
("insert into\n", None),
// toml
("[package]\nname = \"x\"\n", Some("toml")),
("a = 1\nb = 2\n", Some("toml")),
("a = 1\n", None),
("a = 1\nb = 2;\n", None),
("[\"a\",]\n", None),
("[section]\n", Some("toml")),
// prose, lists, one-liners, hostile
("The plan is simple: we ship it.\nAnother line here: no code.\nMostly prose without any shape at all.\n", None),
("Note: this\nWarning: that\n", None),
("- one\n- two\n- three\n", None),
("x = 1", None),
("a: b", None),
("just a sentence\n", None),
("1 + 1\n2 + 2\n", None),
("hello: world\nfoo: bar\n", None),
("\u{0}\u{0}\u{fffd}\n", None),
("caf\u{e9}: no\n", None),
// crlf versions of the keyword rules
("fn main() {\r\n let x = 1;\r\n}\r\n", Some("rust")),
("def add(a, b):\r\n return a + b\r\n", Some("python")),
("$ cargo build\r\n$ cargo test\r\n", Some("bash")),
("[package]\r\nname = \"x\"\r\n", Some("toml")),
];
for (body, expected) in rows {
assert_eq!(detect_language(body), expected, "body {body:?}");
}
}
#[test]
fn empty_and_blank_bodies_are_none() {
assert_eq!(detect_language(""), None);
assert_eq!(detect_language(" \n\n \n"), None);
assert_eq!(detect_language("\r\n\r\n"), None);
}
#[test]
fn only_the_first_4_kib_and_40_non_blank_lines_are_examined() {
// 1500 `$ x` lines (6000 bytes) push the bad line past the 4 KiB window.
let beyond_bytes = format!("{}not a prompt\n", "$ x\n".repeat(1500));
assert_eq!(detect_language(&beyond_bytes), Some("bash"));
// 40 prompt lines: the 41st is never examined.
let beyond_lines = format!("{}nope\n", "$ x\n".repeat(40));
assert_eq!(detect_language(&beyond_lines), Some("bash"));
// 39 prompt lines plus the bad one: the rule fails.
let within = format!("{}nope\n", "$ x\n".repeat(39));
assert_eq!(detect_language(&within), None);
}
#[test]
fn a_mib_body_stays_bounded_in_time() {
let began = std::time::Instant::now();
let big = "x".repeat(1024 * 1024);
assert_eq!(detect_language(&big), None);
let mut pythonish = String::from("def add(a, b):\n return a + b\n");
pythonish.push_str(&"x".repeat(1024 * 1024));
assert_eq!(detect_language(&pythonish), Some("python"));
let mut multibyte = String::from("fn main() {\n let x = \"\u{e9}\";\n}\n");
multibyte.push_str(&"\u{e9}".repeat(700 * 1024));
assert_eq!(detect_language(&multibyte), Some("rust"));
assert!(began.elapsed().as_secs_f64() < 1.0, "took {}s", began.elapsed().as_secs_f64());
}
#[test]
fn hostile_bytes_never_panic_and_stay_none() {
for hostile in ["\u{0}", "\u{0}\n\u{0}", "\u{7f}\u{8}", "\u{fffd}".repeat(5000).as_str(), "#!/usr/bin/env\u{0}python\n", "<<??php", "[]", "{}", "[\u{0}]"] {
let _ = detect_language(hostile);
}
assert_eq!(detect_language("[\u{0}]"), None);
assert_eq!(detect_language("#!/usr/bin/env python\u{0}"), None);
}