package com.example.aiapp import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue /** * The line arithmetic behind the file viewer. * * Worth a test rather than an eye: a line number that is one out is invisible in a short file and * obvious in a long one, and a colour that stops at a line break is invisible until the file has a * block comment in it. */ class FileLinesTest { @Test fun `a file that ends with a newline has the number of lines its author would count`() { assertEquals(listOf("one", "two"), FileLines.of("one\ntwo\n", null).lines) assertEquals(listOf("one", "two"), FileLines.of("one\ntwo", null).lines) // Only one is dropped: a blank line at the end of a file is a line somebody typed. assertEquals(listOf("one", "two", ""), FileLines.of("one\ntwo\n\n", null).lines) } @Test fun `an empty file is one empty line`() { val lines = FileLines.of("", null) assertEquals(1, lines.size) assertEquals("", lines.line(0).text) } @Test fun `a comment that spans lines is coloured on every line it covers`() { val text = "fn a() {}\n/* still\n a comment */\nfn b() {}\n" val lines = FileLines.of(text, Language.RUST) assertEquals(4, lines.size) val comment = catppuccinSyntax().of(Kind.COMMENT) // The whole of the middle line, and the part of the third up to the closer. assertTrue(lines.line(1).spanStyles.any { it.item.color == comment && it.start == 0 }) val third = lines.line(2) assertTrue(third.spanStyles.any { it.item.color == comment && it.end == third.length }) // And the code around it is not commented. assertTrue(lines.line(0).spanStyles.none { it.item.color == comment }) assertTrue(lines.line(3).spanStyles.none { it.item.color == comment }) } @Test fun `a span never runs past the line it was cut into`() { val lines = FileLines.of("val x = \"a\nb\"\nval y = 1\n", Language.KOTLIN) for (index in 0 until lines.size) { val line = lines.line(index) assertTrue( line.spanStyles.all { it.start >= 0 && it.end <= line.length }, "line $index", ) } } /** * The number every row in the viewer is sized to. It has to be the widest line, because rows of * their natural widths scroll sideways by different amounts -- see [FileViewer]. */ @Test fun `the column count is the widest line, counting a tab as eight`() { assertEquals(5, FileLines.of("one\nthree\nx\n", null).columns) // A tab counts up to eight, and upwards on purpose: over-estimating leaves empty space // past the longest line, under-estimating puts its end out of reach. assertEquals(9, FileLines.of("\tx\nshort\n", null).columns) // An empty file is one empty line, which is no columns at all rather than an error. assertEquals(0, FileLines.of("", null).columns) } @Test fun `a file with no language is plain`() { val lines = FileLines.of("fn main() {}\n", null) assertTrue(lines.line(0).spanStyles.isEmpty()) } @Test fun `a language comes from the extension, and only from a real one`() { assertEquals(Language.KOTLIN, fileLanguage("Main.kt")) assertEquals(Language.KOTLIN, fileLanguage("build.gradle.kts")) assertEquals(Language.RUST, fileLanguage("files.rs")) assertEquals(Language.TOML, fileLanguage("Cargo.toml")) assertEquals(null, fileLanguage("Makefile")) assertEquals(null, fileLanguage(".bashrc")) assertEquals(null, fileLanguage("notes.txt")) } }