A structured-data shell in Gleam, inspired by Nushell
0

Configure Feed

Select the types of activity you want to include in your feed.

gleshell / test / gleshell_test.gleam
6.2 kB 234 lines
1import gleam/string 2import gleeunit 3import gleshell/color 4import gleshell/display 5import gleshell/env 6import gleshell/eval 7import gleshell/lexer 8import gleshell/parser 9import gleshell/value.{Bool, Int, List, Nothing, Record, String, Table} 10 11pub fn main() -> Nil { 12 gleeunit.main() 13} 14 15// --- lexer --- 16 17pub fn lexer_pipeline_test() { 18 let assert Ok(tokens) = lexer.tokenize("ls | where type == file") 19 let assert [lexer.Ident("ls"), lexer.Pipe, lexer.Ident("where"), ..] = tokens 20 Nil 21} 22 23pub fn lexer_string_and_number_test() { 24 let assert Ok(tokens) = lexer.tokenize("echo \"hi\" 42 3.14 true") 25 let assert [ 26 lexer.Ident("echo"), 27 lexer.StringLit("hi"), 28 lexer.IntLit(42), 29 lexer.FloatLit(3.14), 30 lexer.BoolLit(True), 31 lexer.Eof, 32 ] = tokens 33 Nil 34} 35 36// --- parser --- 37 38pub fn parse_pipeline_test() { 39 let assert Ok(parser.Expr(parser.Pipeline(cmds))) = 40 parser.parse("ls | first 3") 41 let assert [ 42 parser.Command("ls", [], False), 43 parser.Command("first", args, False), 44 ] = cmds 45 let assert [parser.ValueArg(parser.Lit(Int(3)))] = args 46 Nil 47} 48 49pub fn parse_let_test() { 50 let assert Ok(parser.Let( 51 "x", 52 parser.Pipeline([parser.Command("echo", args, False)]), 53 )) = parser.parse("let x = echo 1") 54 let assert [parser.ValueArg(parser.Lit(Int(1)))] = args 55 Nil 56} 57 58pub fn parse_where_ops_test() { 59 let assert Ok(parser.Expr(parser.Pipeline([ 60 parser.Command("where", args, False), 61 ]))) = parser.parse("where type == file") 62 let assert [ 63 parser.ValueArg(parser.Lit(String("type"))), 64 parser.ValueArg(parser.Lit(String("=="))), 65 parser.ValueArg(parser.Lit(String("file"))), 66 ] = args 67 Nil 68} 69 70pub fn parse_list_and_record_test() { 71 let assert Ok(parser.Expr(parser.Pipeline([ 72 parser.Command("echo", args, False), 73 ]))) = parser.parse("echo [1 2] {a: true}") 74 let assert [ 75 parser.ValueArg(parser.ListExpr([parser.Lit(Int(1)), parser.Lit(Int(2))])), 76 parser.ValueArg(parser.RecordExpr([#("a", parser.Lit(Bool(True)))])), 77 ] = args 78 Nil 79} 80 81// --- value helpers --- 82 83pub fn table_from_records_test() { 84 let rows = [ 85 Record([#("name", String("a")), #("n", Int(1))]), 86 Record([#("name", String("b")), #("n", Int(2))]), 87 ] 88 let assert Table( 89 ["name", "n"], 90 [[String("a"), Int(1)], [String("b"), Int(2)]], 91 ) = value.table_from_records(rows) 92 Nil 93} 94 95// --- eval --- 96 97pub fn eval_echo_and_range_test() { 98 let env = env.new() 99 let assert eval.Continue(_, List([Int(0), Int(1), Int(2)])) = 100 eval.eval_source(env, "range 3") 101 let assert eval.Continue(_, String("hello")) = 102 eval.eval_source(env, "echo hello") 103 Nil 104} 105 106pub fn eval_pipeline_reverse_first_test() { 107 let env = env.new() 108 let assert eval.Continue(_, Int(2)) = 109 eval.eval_source(env, "range 3 | reverse | first") 110 Nil 111} 112 113pub fn eval_let_and_var_test() { 114 let env = env.new() 115 let assert eval.Continue(env2, Int(7)) = 116 eval.eval_source(env, "let n = echo 7") 117 let assert eval.Continue(_, Int(7)) = eval.eval_source(env2, "echo $n") 118 Nil 119} 120 121pub fn eval_where_select_test() { 122 let env = env.new() 123 // build table via records in a list, convert with table 124 let assert eval.Continue(_, result) = 125 eval.eval_source( 126 env, 127 "echo [{name: a, n: 1} {name: b, n: 2} {name: c, n: 3}] | table | where n > 1 | select name", 128 ) 129 let assert Table(["name"], rows) = result 130 let assert [[String("b")], [String("c")]] = rows 131 Nil 132} 133 134pub fn eval_from_json_test() { 135 let env = env.new() 136 let assert eval.Continue(_, Record(fields)) = 137 eval.eval_source(env, "echo \"{\\\"x\\\": 1}\" | from json") 138 let assert True = list_has_field(fields, "x", Int(1)) 139 Nil 140} 141 142pub fn eval_to_json_pretty_test() { 143 let env = env.new() 144 // Nushell-style multi-word `to json` — pretty by default 145 let assert eval.Continue(_, String(pretty)) = 146 eval.eval_source(env, "echo [1 2 3] | to json") 147 let assert True = string.contains(pretty, "\n") 148 let assert True = string.contains(pretty, "1") 149 // `--raw` matches Nu: compact, no trailing newline 150 let assert eval.Continue(_, String(raw)) = 151 eval.eval_source(env, "echo [1 2 3] | to json --raw") 152 let assert "[1,2,3]" = raw 153 Nil 154} 155 156pub fn eval_to_json_record_test() { 157 let env = env.new() 158 let assert eval.Continue(_, String(raw)) = 159 eval.eval_source(env, "echo {a: 1, b: true} | to json -r") 160 let assert True = string.contains(raw, "\"a\":1") 161 let assert True = string.contains(raw, "\"b\":true") 162 Nil 163} 164 165fn list_has_field( 166 fields: List(#(String, value.Value)), 167 key: String, 168 expected: value.Value, 169) -> Bool { 170 case fields { 171 [] -> False 172 [#(k, v), ..rest] -> 173 case k == key { 174 True -> value.equals(v, expected) 175 False -> list_has_field(rest, key, expected) 176 } 177 } 178} 179 180pub fn eval_length_test() { 181 let env = env.new() 182 let assert eval.Continue(_, Int(4)) = 183 eval.eval_source(env, "range 4 | length") 184 Nil 185} 186 187pub fn value_nothing_falsey_test() { 188 let assert False = value.is_truthy(Nothing) 189 let assert True = value.is_truthy(Int(1)) 190 Nil 191} 192 193// --- display / color --- 194 195pub fn display_plain_no_ansi_test() { 196 let text = display.render_with(False, Bool(True)) 197 let assert "true" = text 198 let assert False = string_contains(text, "\u{001b}") 199 Nil 200} 201 202pub fn display_colored_has_ansi_test() { 203 let text = display.render_with(True, Bool(True)) 204 let assert True = string_contains(text, "\u{001b}") 205 let assert True = string_contains(text, "true") 206 Nil 207} 208 209pub fn display_table_headers_colored_test() { 210 let text = 211 display.render_with( 212 True, 213 Table(["name", "type"], [[String("src"), String("dir")]]), 214 ) 215 // bold green header + bright-blue dir name 216 let assert True = string_contains(text, "\u{001b}[1;32m") 217 let assert True = string_contains(text, "\u{001b}[94m") 218 let assert True = string_contains(text, "src") 219 Nil 220} 221 222pub fn color_visible_length_strips_ansi_test() { 223 let painted = color.paint(True, "\u{001b}[32m", "hi") 224 let assert 2 = color.visible_length(painted) 225 let assert 2 = color.visible_length("hi") 226 Nil 227} 228 229fn string_contains(haystack: String, needle: String) -> Bool { 230 case string.split(haystack, needle) { 231 [_] -> False 232 _ -> True 233 } 234}