From 1146b8b84afb88aa6a4d2315bd970945aded7205 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 18 Jun 2015 19:38:25 -0700 Subject: separate jq, oniguruma, sh, and man tests --- tests/all.test | 1250 ------------------------------------------------------- tests/jq.test | 1163 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/jqtest | 5 + tests/mantest | 5 + tests/onig.test | 85 ++++ tests/onigtest | 5 + tests/run | 226 ---------- tests/setup | 34 ++ tests/shtest | 196 +++++++++ 9 files changed, 1493 insertions(+), 1476 deletions(-) delete mode 100644 tests/all.test create mode 100644 tests/jq.test create mode 100755 tests/jqtest create mode 100755 tests/mantest create mode 100644 tests/onig.test create mode 100755 tests/onigtest delete mode 100755 tests/run create mode 100755 tests/setup create mode 100755 tests/shtest (limited to 'tests') diff --git a/tests/all.test b/tests/all.test deleted file mode 100644 index 8213f8be..00000000 --- a/tests/all.test +++ /dev/null @@ -1,1250 +0,0 @@ -# Tests are groups of three lines: program, input, expected output -# Blank lines and lines starting with # are ignored - -# -# Simple value tests to check parser. Input is irrelevant -# - -true -null -true - -false -null -false - -null -42 -null - -1 -null -1 - - --1 -null --1 - -# FIXME: much more number testing needed - -{} -null -{} - -[] -null -[] - -{x: -1} -null -{"x": -1} - -# The input line starts with a 0xFEFF (byte order mark) codepoint -# No, there is no reason to have a byte order mark in UTF8 text. -# But apparently people do, so jq shouldn't break on it. -. -"byte order mark" -"byte order mark" - -# We test escapes by matching them against Unicode codepoints -# FIXME: more tests needed for weird unicode stuff (e.g. utf16 pairs) -"Aa\r\n\t\b\f\u03bc" -null -"Aa\u000d\u000a\u0009\u0008\u000c\u03bc" - -. -"Aa\r\n\t\b\f\u03bc" -"Aa\u000d\u000a\u0009\u0008\u000c\u03bc" - -"inter\("pol" + "ation")" -null -"interpolation" - -@text,@json,([1,.] | (@csv, @tsv)),@html,@uri,@sh,@base64 -"<>&'\"\t" -"<>&'\"\t" -"\"<>&'\\\"\\t\"" -"1,\"<>&'\"\"\t\"" -"1\t<>&'\"\\t" -"<>&'"\t" -"%3C%3E%26'%22%09" -"'<>&'\\''\"\t'" -"PD4mJyIJ" - -# regression test for #436 -@base64 -"foóbar\n" -"Zm/Ds2Jhcgo=" - -@uri -"\u03bc" -"%CE%BC" - -@html "\(.)" -"" -"<script>hax</script>" - -[.[]|tojson|fromjson] -["foo", 1, ["a", 1, "b", 2, {"foo":"bar"}]] -["foo",1,["a",1,"b",2,{"foo":"bar"}]] - -# -# Dictionary construction syntax -# - -{a: 1} -null -{"a":1} - -{a,b,(.d):.a,e:.b} -{"a":1, "b":2, "c":3, "d":"c"} -{"a":1, "b":2, "c":1, "e":2} - -{"a",b,"a$\(1+1)"} -{"a":1, "b":2, "c":3, "a$2":4} -{"a":1, "b":2, "a$2":4} - -# -# Field access, piping -# - -.foo -{"foo": 42, "bar": 43} -42 - -.foo | .bar -{"foo": {"bar": 42}, "bar": "badvalue"} -42 - -.foo.bar -{"foo": {"bar": 42}, "bar": "badvalue"} -42 - -.foo_bar -{"foo_bar": 2} -2 - -.["foo"].bar -{"foo": {"bar": 42}, "bar": "badvalue"} -42 - -."foo"."bar" -{"foo": {"bar": 20}} -20 - -[.[]|.foo?] -[1,[2],{"foo":3,"bar":4},{},{"foo":5}] -[3,null,5] - -[.[]|.foo?.bar?] -[1,[2],[],{"foo":3},{"foo":{"bar":4}},{}] -[4,null] - -[..] -[1,[[2]],{ "a":[1]}] -[[1,[[2]],{"a":[1]}],1,[[2]],[2],2,{"a":[1]},[1],1] - -[.[]|.[]?] -[1,null,[],[1,[2,[[3]]]],[{}],[{"a":[1,[2]]}]] -[1,[2,[[3]]],{},{"a":[1,[2]]}] - -[.[]|.[1:3]?] -[1,null,true,false,"abcdef",{},{"a":1,"b":2},[],[1,2,3,4,5],[1,2]] -[null,"bc",[],[2,3],[2]] - -# -# Negative array indices -# - -try (.foo[-1] = 0) catch . -null -"Out of bounds negative array index" - -try (.foo[-2] = 0) catch . -null -"Out of bounds negative array index" - -.[-1] = 5 -[0,1,2] -[0,1,5] - -.[-2] = 5 -[0,1,2] -[0,5,2] - -# -# Multiple outputs, iteration -# - -.[] -[1,2,3] -1 -2 -3 - -1,1 -[] -1 -1 - -1,. -[] -1 -[] - -[.] -[2] -[[2]] - -[[2]] -[3] -[[2]] - -[{}] -[2] -[{}] - -[.[]] -["a"] -["a"] - -[(.,1),((.,.[]),(2,3))] -["a","b"] -[["a","b"],1,["a","b"],"a","b",2,3] - -[([5,5][]),.,.[]] -[1,2,3] -[5,5,[1,2,3],1,2,3] - -{x: (1,2)},{x:3} | .x -null -1 -2 -3 - -.[-2] -[1,2,3] -2 - -[range(0;10)] -null -[0,1,2,3,4,5,6,7,8,9] - -[range(0;10;3)] -null -[0,3,6,9] - -[range(0;10;-1)] -null -[] - -[range(0;-5;-1)] -null -[0,-1,-2,-3,-4] - -[while(.<100; .*2)] -1 -[1,2,4,8,16,32,64] - -[(label $here | .[] | if .>1 then break $here else . end), "hi!"] -[0,1,2] -[0,1,"hi!"] - -[(label $here | .[] | if .>1 then break $here else . end), "hi!"] -[0,2,1] -[0,"hi!"] - -%%FAIL -. as $foo | break $foo -jq: error: *label-foo/0 is not defined at , line 1: - -[.[]|[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]] -[1,2,3,4,5] -[1,2,6,24,120] - -[label $out | foreach .[] as $item ([3, null]; if .[0] < 1 then break $out else [.[0] -1, $item] end; .[1])] -[11,22,33,44,55,66,77,88,99] -[11,22,33] - -[foreach range(5) as $item (0; $item)] -null -[0,1,2,3,4] - -[foreach .[] as [$i, $j] (0; . + $i - $j)] -[[2,1], [5,3], [6,4]] -[1,3,5] - -[foreach .[] as {a:$a} (0; . + $a; -.)] -[{"a":1}, {"b":2}, {"a":3, "b":4}] -[-1, -1, -4] - -[limit(3; .[])] -[11,22,33,44,55,66,77,88,99] -[11,22,33] - -[first(range(.)), last(range(.)), nth(0; range(.)), nth(5; range(.)), try nth(-1; range(.)) catch .] -10 -[0,9,0,5,"nth doesn't support negative indices"] - -# -# Check that various builtins evalute all arguments where appropriate, -# doing cartesian products where appropriate. -# - -# Check that limit does work for each value produced by n! -[limit(5,7; range(9))] -null -[0,1,2,3,4,0,1,2,3,4,5,6] - -# Same check for nth -[nth(5,7; range(9;0;-1))] -null -[4,2] - -# Same check for range/3 -[range(0,1,2;4,3,2;2,3)] -null -[0,2,0,3,0,2,0,0,0,1,3,1,1,1,1,1,2,2,2,2] - -# Same check for range/1 -[range(3,5)] -null -[0,1,2,0,1,2,3,4] - -# Same check for index/1, rindex/1, indices/1 -[(index(",","|"), rindex(",","|")), indices(",","|")] -"a,b|c,d,e||f,g,h,|,|,i,j" -[1,3,22,19,[1,5,7,12,14,16,18,20,22],[3,9,10,17,19]] - -# Same check for join/1 -join(",","/") -["a","b","c","d"] -"a,b,c,d" -"a/b/c/d" - -[.[]|join("a")] -[[],[""],["",""],["","",""]] -["","","a","aa"] - -# Same check for flatten/1 -flatten(-1,3,2,1) -[0, [1], [[2]], [[[3]]]] -[0,[1],[[2]],[[[3]]]] -[0,1,2,3] -[0,1,2,[3]] -[0,1,[2],[[3]]] - - -# -# Slices -# - -[.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]] -[0,1,2,3,4,5,6] -[[], [2,3], [0,1,2,3,4], [5,6], [], []] - -[.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]] -"abcdefghi" -["","","abcdefg","hi","",""] - -del(.[2:4],.[0],.[-2:]) -[0,1,2,3,4,5,6,7] -[1,4,5] - -.[2:4] = ([], ["a","b"], ["a","b","c"]) -[0,1,2,3,4,5,6,7] -[0,1,4,5,6,7] -[0,1,"a","b",4,5,6,7] -[0,1,"a","b","c",4,5,6,7] - - -# -# Variables -# - -1 as $x | 2 as $y | [$x,$y,$x] -null -[1,2,1] - -[1,2,3][] as $x | [[4,5,6,7][$x]] -null -[5] -[6] -[7] - -42 as $x | . | . | . + 432 | $x + 1 -34324 -43 - -1 as $x | [$x,$x,$x as $x | $x] -null -[1,1,1] - -[1, {c:3, d:4}] as [$a, {c:$b, b:$c}] | $a, $b, $c -null -1 -3 -null - -. as {as: $kw, "str": $str, ("e"+"x"+"p"): $exp} | [$kw, $str, $exp] -{"as": 1, "str": 2, "exp": 3} -[1, 2, 3] - -.[] as [$a, $b] | [$b, $a] -[[1], [1, 2, 3]] -[null, 1] -[2, 1] - -. as $i | . as [$i] | $i -[0] -0 - -. as [$i] | . as $i | $i -[0] -[0] - -%%FAIL -. as [] | null -jq: error: syntax error, unexpected ']', expecting '$' or '[' or '{' (Unix shell quoting issues?) at , line 1: - -%%FAIL -. as {} | null -jq: error: syntax error, unexpected '}' (Unix shell quoting issues?) at , line 1: - -# [.,(.[] | {x:.},.),.,.[]] - -# -# Builtin functions -# - -1+1 -null -2 - -1+1 -"wtasdf" -2.0 - -2-1 -null -1 - -2-(-1) -null -3 - -1e+0+0.001e3 -"I wonder what this will be?" -20e-1 - -.+4 -15 -19.0 - -.+null -{"a":42} -{"a":42} - -null+. -null -null - -.a+.b -{"a":42} -42 - -[1,2,3] + [.] -null -[1,2,3,null] - -{"a":1} + {"b":2} + {"c":3} -"asdfasdf" -{"a":1, "b":2, "c":3} - -"asdf" + "jkl;" + . + . + . -"some string" -"asdfjkl;some stringsome stringsome string" - -"\u0000\u0020\u0000" + . -"\u0000\u0020\u0000" -"\u0000 \u0000\u0000 \u0000" - -42 - . -11 -31 - -[1,2,3,4,1] - [.,3] -1 -[2,4] - -[10 * 20, 20 / .] -4 -[200, 5] - -1 + 2 * 2 + 10 / 2 -null -10 - -[16 / 4 / 2, 16 / 4 * 2, 16 - 4 - 2, 16 - 4 + 2] -null -[2, 8, 10, 14] - -25 % 7 -null -4 - -49732 % 472 -null -172 - -1 + tonumber + ("10" | tonumber) -4 -15 - -[{"a":42},.object,10,.num,false,true,null,"b",[1,4]] | .[] as $x | [$x == .[]] -{"object": {"a":42}, "num":10.0} -[true, true, false, false, false, false, false, false, false] -[true, true, false, false, false, false, false, false, false] -[false, false, true, true, false, false, false, false, false] -[false, false, true, true, false, false, false, false, false] -[false, false, false, false, true, false, false, false, false] -[false, false, false, false, false, true, false, false, false] -[false, false, false, false, false, false, true, false, false] -[false, false, false, false, false, false, false, true, false] -[false, false, false, false, false, false, false, false, true ] - -[.[] | length] -[[], {}, [1,2], {"a":42}, "asdf", "\u03bc"] -[0, 0, 2, 1, 4, 1] - -map(keys) -[{}, {"abcd":1,"abc":2,"abcde":3}, {"x":1, "z": 3, "y":2}] -[[], ["abc","abcd","abcde"], ["x","y","z"]] - -[1,2,empty,3,empty,4] -null -[1,2,3,4] - -map(add) -[[], [1,2,3], ["a","b","c"], [[3],[4,5],[6]], [{"a":1}, {"b":2}, {"a":3}]] -[null, 6, "abc", [3,4,5,6], {"a":3, "b": 2}] - -map_values(.+1) -[0,1,2] -[1,2,3] - -# -# User-defined functions -# Oh god. -# - -def f: . + 1; def g: def g: . + 100; f | g | f; (f | g), g -3.0 -106.0 -105.0 - -def f: (1000,2000); f -123412345 -1000 -2000 - -def f(a;b;c;d;e;f): [a+1,b,c,d,e,f]; f(.[0];.[1];.[0];.[0];.[0];.[0]) -[1,2] -[2,2,1,1,1,1] - -# Many arguments -def f(a;b;c;d;e;f;g;h;i;j): [j,i,h,g,f,e,d,c,b,a]; f(.[0];.[1];.[2];.[3];.[4];.[5];.[6];.[7];.[8];.[9]) -[0,1,2,3,4,5,6,7,8,9] -[9,8,7,6,5,4,3,2,1,0] - -([1,2] + [4,5]) -[1,2,3] -[1,2,4,5] - -true -[1] -true - -null,1,null -"hello" -null -1 -null - -[1,2,3] -[5,6] -[1,2,3] - -[.[]|floor] -[-1.1,1.1,1.9] -[-2, 1, 1] - -[.[]|sqrt] -[4,9] -[2,3] - -(add / length) as $m | map((. - $m) as $d | $d * $d) | add / length | sqrt -[2,4,4,4,5,5,7,9] -2 - -# Should write a test that calls the -lm function from C (or bc(1)) to -# check that they match the corresponding jq functions. However, -# there's so little template code standing between that it suffices to -# test a handful of these. The results were checked by eye against -# bc(1). -atan * 4 * 1000000|floor / 1000000 -1 -3.141592 - -[(3.141592 / 2) * (range(0;20) / 20)|cos * 1000000|floor / 1000000] -null -[1,0.996917,0.987688,0.972369,0.951056,0.923879,0.891006,0.85264,0.809017,0.760406,0.707106,0.649448,0.587785,0.522498,0.45399,0.382683,0.309017,0.233445,0.156434,0.078459] - -[(3.141592 / 2) * (range(0;20) / 20)|sin * 1000000|floor / 1000000] -null -[0,0.078459,0.156434,0.233445,0.309016,0.382683,0.45399,0.522498,0.587785,0.649447,0.707106,0.760405,0.809016,0.85264,0.891006,0.923879,0.951056,0.972369,0.987688,0.996917] - - -def f(x): x | x; f([.], . + [42]) -[1,2,3] -[[[1,2,3]]] -[[1,2,3],42] -[[1,2,3,42]] -[1,2,3,42,42] - -# test multiple function arities and redefinition -def f: .+1; def g: f; def f: .+100; def f(a):a+.+11; [(g|f(20)), f] -1 -[33,101] - -# test closures and lexical scoping -def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x) -"more testing" -[1,100,2100.0,100,2100.0] - -# test def f($a) syntax -def x(a;b): a as $a | b as $b | $a + $b; def y($a;$b): $a + $b; def check(a;b): [x(a;b)] == [y(a;b)]; check(.[];.[]*2) -[1,2,3] -true - -# test backtracking through function calls and returns -# this test is *evil* -[[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | g; f[0] | [f][0][1] | f] -999999999 -[[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]] - -# test recursion -def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac] -[1,2,3,4] -[1,2,6,24] - -# test stack overflow and reallocation -# this test is disabled for now, it takes a realllllly long time. -# def f: if length > 1000 then . else .+[1]|f end; f | length -# [] -# 1001 - -reduce .[] as $x (0; . + $x) -[1,2,4] -7 - -reduce .[] as [$i, {j:$j}] (0; . + $i - $j) -[[2,{"j":1}], [5,{"j":3}], [6,{"j":4}]] -5 - -reduce [[1,2,10], [3,4,10]][] as [$i,$j] (0; . + $i * $j) -null -14 - -. as $dot|any($dot[];not) -[1,2,3,4,true,false,1,2,3,4,5] -true - -. as $dot|any($dot[];not) -[1,2,3,4,true] -false - -. as $dot|all($dot[];.) -[1,2,3,4,true,false,1,2,3,4,5] -false - -. as $dot|all($dot[];.) -[1,2,3,4,true] -true - -# -# Paths -# - -path(.foo[0,1]) -null -["foo", 0] -["foo", 1] - -path(.[] | select(.>3)) -[1,5,3] -[1] - -path(.) -42 -[] - -[paths] -[1,[[],{"a":2}]] -[[0],[1],[1,0],[1,1],[1,1,"a"]] - -[leaf_paths] -[1,[[],{"a":2}]] -[[0],[1,1,"a"]] - -["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) -{"bar": 42, "foo": ["a", "b", "c", "d"]} -"b" -{"bar": 42, "foo": ["a", 20, "c", "d"]} -{"bar": 42, "foo": ["a", "c", "d"]} - -map(getpath([2])), map(setpath([2]; 42)), map(delpaths([[2]])) -[[0], [0,1], [0,1,2]] -[null, null, 2] -[[0,null,42], [0,1,42], [0,1,42]] -[[0], [0,1], [0,1]] - -map(delpaths([[0,"foo"]])) -[[{"foo":2, "x":1}], [{"bar":2}]] -[[{"x":1}], [{"bar":2}]] - -["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) -{"bar":false} -null -{"bar":false, "foo": [null, 20]} -{"bar":false} - -delpaths([[-200]]) -[1,2,3] -[1,2,3] - -del(.), del(empty), del((.foo,.bar,.baz) | .[2,3,0]), del(.foo[0], .bar[0], .foo, .baz.bar[0].x) -{"foo": [0,1,2,3,4], "bar": [0,1]} -null -{"foo": [0,1,2,3,4], "bar": [0,1]} -{"foo": [1,4], "bar": [1]} -{"bar": [1]} - -# -# Assignment -# -.message = "goodbye" -{"message": "hello"} -{"message": "goodbye"} - -.foo = .bar -{"bar":42} -{"foo":42, "bar":42} - -.foo |= .+1 -{"foo": 42} -{"foo": 43} - -.[] += 2, .[] *= 2, .[] -= 2, .[] /= 2, .[] %=2 -[1,3,5] -[3,5,7] -[2,6,10] -[-1,1,3] -[0.5, 1.5, 2.5] -[1,1,1] - -[.[] % 7] -[-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7] -[0,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,0] - -.foo += .foo -{"foo":2} -{"foo":4} - -.[0].a |= {"old":., "new":(.+1)} -[{"a":1,"b":2}] -[{"a":{"old":1, "new":2},"b":2}] - -def inc(x): x |= .+1; inc(.[].a) -[{"a":1,"b":2},{"a":2,"b":4},{"a":7,"b":8}] -[{"a":2,"b":2},{"a":3,"b":4},{"a":8,"b":8}] - -.[2][3] = 1 -[4] -[4, null, [null, null, null, 1]] - -.foo[2].bar = 1 -{"foo":[11], "bar":42} -{"foo":[11,null,{"bar":1}], "bar":42} - -# -# Conditionals -# - -[.[] | if .foo then "yep" else "nope" end] -[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}] -["yep","yep","yep","yep","nope","nope","yep","nope"] - -[.[] | if .baz then "strange" elif .foo then "yep" else "nope" end] -[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}] -["yep","yep","yep","yep","nope","nope","yep","nope"] - - -# FIXME: define/test behaviour of 'if (.foo,.bar) then A else B end' - -[.[] | [.foo[] // .bar]] -[{"foo":[1,2], "bar": 42}, {"foo":[1], "bar": null}, {"foo":[null,false,3], "bar": 18}, {"foo":[], "bar":42}, {"foo": [null,false,null], "bar": 41}] -[[1,2], [1], [3], [42], [41]] - -.[] //= .[0] -["hello",true,false,[false],null] -["hello",true,"hello",[false],"hello"] - -.[] | [.[0] and .[1], .[0] or .[1]] -[[true,[]], [false,1], [42,null], [null,false]] -[true,true] -[false,true] -[false,true] -[false,false] - -[.[] | not] -[1,0,false,null,true,"hello"] -[false,false,true,true,false,false] - -# Check numeric comparison binops -[10 > 0, 10 > 10, 10 > 20, 10 < 0, 10 < 10, 10 < 20] -{} -[true,false,false,false,false,true] - -[10 >= 0, 10 >= 10, 10 >= 20, 10 <= 0, 10 <= 10, 10 <= 20] -{} -[true,true,false,false,true,true] - -# And some in/equality tests -[ 10 == 10, 10 != 10, 10 != 11, 10 == 11] -{} -[true,false,true,false] - -["hello" == "hello", "hello" != "hello", "hello" == "world", "hello" != "world" ] -{} -[true,false,false,true] - -[[1,2,3] == [1,2,3], [1,2,3] != [1,2,3], [1,2,3] == [4,5,6], [1,2,3] != [4,5,6]] -{} -[true,false,false,true] - -[{"foo":42} == {"foo":42},{"foo":42} != {"foo":42}, {"foo":42} != {"bar":42}, {"foo":42} == {"bar":42}] -{} -[true,false,true,false] - -# ugly complicated thing -[{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":18},"world"]},{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":19},"world"]}] -{} -[true,false] - -# containment operator -[("foo" | contains("foo")), ("foobar" | contains("foo")), ("foo" | contains("foobar"))] -{} -[true, true, false] - -# Try/catch and general `?` operator -[.[]|try if . == 0 then error("foo") elif . == 1 then .a elif . == 2 then empty else . end catch .] -[0,1,2,3] -["foo","Cannot index number with string \"a\"",3] - -[.[]|(.a, .a)?] -[null,true,{"a":1}] -[null,null,1,1] - -[[.[]|[.a,.a]]?] -[null,true,{"a":1}] -[] - -try error("\($__loc__)") catch . -null -"{\"file\":\"\",\"line\":1}" - -# string operations -[.[]|startswith("foo")] -["fo", "foo", "barfoo", "foobar", "barfoob"] -[false, true, false, true, false] - -[.[]|endswith("foo")] -["fo", "foo", "barfoo", "foobar", "barfoob"] -[false, true, true, false, false] - -# match builtin -[match("( )*"; "g")] -"abc" -[{"offset":0, "length":0, "string":"", "captures":[]},{"offset":1, "length":0, "string":"", "captures":[]},{"offset":2, "length":0, "string":"", "captures":[]}] - -[match("( )*"; "gn")] -"abc" -[] - -[match("a"; "gi")] -"āáàä" -[] - -[match(["(bar)"])] -"foo bar" -[{"offset": 4, "length": 3, "string": "bar", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": null}]}] - -# offsets account for combining codepoints and multi-byte UTF-8 -[match("bar")] -"ā bar with a combining codepoint U+0304" -[{"offset": 3, "length": 3, "string": "bar", "captures":[]}] - -# matches with combining codepoints still count them in their length -[match("bār")] -"a bār" -[{"offset": 2, "length": 4, "string": "bār", "captures":[]}] - -[match(".+?\\b")] -"ā two-codepoint grapheme" -[{"offset": 0, "length": 2, "string": "ā", "captures":[]}] - -[match(["foo (?bar)? foo", "ig"])] -"foo bar foo foo foo" -[{"offset": 0, "length": 11, "string": "foo bar foo", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]},{"offset":12, "length": 8, "string": "foo foo", "captures":[{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}] - -#test builtin -[test("( )*"; "gn")] -"abc" -[false] - -[test("ā")] -"ā" -[true] - -capture("(?[a-z]+)-(?[0-9]+)") -"xyzzy-14" -{"a":"xyzzy","n":"14"} - - -# jq-coded utilities built on match: -# -# The second element in these tests' inputs tests the case where the -# fromstring matches both the head and tail of the string -[.[] | sub(", "; ":")] -["a,b, c, d, e,f", ", a,b, c, d, e,f, "] -["a,b:c, d, e,f",":a,b, c, d, e,f, "] - -sub("^(?.)"; "Head=\(.head) Tail=") -"abcdef" -"Head=a Tail=bcdef" - -[.[] | gsub(", "; ":")] -["a,b, c, d, e,f",", a,b, c, d, e,f, "] -["a,b:c:d:e,f",":a,b:c:d:e,f:"] - -gsub("(?\\d)"; ":\(.d);") -"a1b2" -"a:1;b:2;" - -[.[] | scan(", ")] -["a,b, c, d, e,f",", a,b, c, d, e,f, "] -[", ",", ",", ",", ",", ",", ",", ",", "] - -[.[] | split(", ")] -["a,b, c, d, e,f",", a,b, c, d, e,f, "] -[["a,b","c","d","e,f"],["","a,b","c","d","e,f",""]] - -split("") -"abc" -["a","b","c"] - -######################## -[.[]|[[sub(", *";":")], [gsub(", *";":")], [scan(", *")]]] -["a,b, c, d, e,f",", a,b, c, d, e,f, "] -[[["a:b, c, d, e,f"],["a:b:c:d:e:f"],[",",", ",", ",", ",","]],[[":a,b, c, d, e,f, "],[":a:b:c:d:e:f:"],[", ",",",", ",", ",", ",",",", "]]] - -[.[]|[[sub(", +";":")], [gsub(", +";":")], [scan(", +")]]] -["a,b, c, d, e,f",", a,b, c, d, e,f, "] -[[["a,b:c, d, e,f"],["a,b:c:d:e,f"],[", ",", ",", "]],[[":a,b, c, d, e,f, "],[":a,b:c:d:e,f:"],[", ",", ",", ",", ",", "]]] - -# reference to named captures -gsub("(?.)[^a]*"; "+\(.x)-") -"Abcabc" -"+A-+a-" - -[.[]|ltrimstr("foo")] -["fo", "foo", "barfoo", "foobar", "afoo"] -["fo","","barfoo","bar","afoo"] - -[.[]|rtrimstr("foo")] -["fo", "foo", "barfoo", "foobar", "foob"] -["fo","","bar","foobar","foob"] - -[(index(","), rindex(",")), indices(",")] -"a,bc,def,ghij,klmno" -[1,13,[1,4,8,13]] - -indices(1) -[0,1,1,2,3,4,1,5] -[1,2,6] - -indices([1,2]) -[0,1,2,3,1,4,2,5,1,2,6,7] -[1,8] - -indices([1,2]) -[1] -[] - -indices(", ") -"a,b, cd,e, fgh, ijkl" -[3,9,14] - -[.[]|split(",")] -["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] -[["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]] - -[.[]|split(", ")] -["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] -[["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]] - -[.[] * 3] -["a", "ab", "abc"] -["aaa", "ababab", "abcabcabc"] - -[.[] / ","] -["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] -[["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]] - -[.[] / ", "] -["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] -[["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]] - -map(.[1] as $needle | .[0] | contains($needle)) -[[[],[]], [[1,2,3], [1,2]], [[1,2,3], [3,1]], [[1,2,3], [4]], [[1,2,3], [1,4]]] -[true, true, true, false, false] - -map(.[1] as $needle | .[0] | contains($needle)) -[[["foobar", "foobaz"], ["baz", "bar"]], [["foobar", "foobaz"], ["foo"]], [["foobar", "foobaz"], ["blap"]]] -[true, true, false] - -[({foo: 12, bar:13} | contains({foo: 12})), ({foo: 12} | contains({})), ({foo: 12, bar:13} | contains({baz:14}))] -{} -[true, true, false] - -{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {}}}) -{} -true - -{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {bar: 14}}}) -{} -false - -sort -[42,[2,5,3,11],10,{"a":42,"b":2},{"a":42},true,2,[2,6],"hello",null,[2,5,6],{"a":[],"b":1},"abc","ab",[3,10],{},false,"abcd",null] -[null,null,false,true,2,10,42,"ab","abc","abcd","hello",[2,5,3,11],[2,5,6],[2,6],[3,10],{},{"a":42},{"a":42,"b":2},{"a":[],"b":1}] - -(sort_by(.b) | sort_by(.a)), sort_by(.a, .b), sort_by(.b, .c), group_by(.b), group_by(.a + .b - .c == 2) -[{"a": 1, "b": 4, "c": 14}, {"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}, {"a": 0, "b": 2, "c": 43}] -[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}] -[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}] -[{"a": 4, "b": 1, "c": 3}, {"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 3}, {"a": 1, "b": 4, "c": 14}] -[[{"a": 4, "b": 1, "c": 3}], [{"a": 0, "b": 2, "c": 43}], [{"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}]] -[[{"a": 1, "b": 4, "c": 14}, {"a": 0, "b": 2, "c": 43}], [{"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}]] - -unique -[1,2,5,3,5,3,1,3] -[1,2,3,5] - -unique -[] -[] - -[min, max, min_by(.[1]), max_by(.[1]), min_by(.[2]), max_by(.[2])] -[[4,2,"a"],[3,1,"a"],[2,4,"a"],[1,3,"a"]] -[[1,3,"a"],[4,2,"a"],[3,1,"a"],[2,4,"a"],[4,2,"a"],[1,3,"a"]] - -[min,max,min_by(.),max_by(.)] -[] -[null,null,null,null] - -.foo[.baz] -{"foo":{"bar":4},"baz":"bar"} -4 - -.[] | .error = "no, it's OK" -[{"error":true}] -{"error": "no, it's OK"} - -[{a:1}] | .[] | .a=999 -null -{"a": 999} - -to_entries -{"a": 1, "b": 2} -[{"key":"a", "value":1}, {"key":"b", "value":2}] - -from_entries -[{"key":"a", "value":1}, {"Key":"b", "value":2}] -{"a": 1, "b": 2} - -from_entries -[{"key":"a", "Value":1}, {"Name":"b", "value":2}] -{"a": 1, "b": 2} - -with_entries(.key |= "KEY_" + .) -{"a": 1, "b": 2} -{"KEY_a": 1, "KEY_b": 2} - -map(has("foo")) -[{"foo": 42}, {}] -[true, false] - -map(has(2)) -[[0,1], ["a","b","c"]] -[false, true] - -keys -[42,3,35] -[0,1,2] - -[][.] -1000000000000000000 -null - -map([1,2][0:.]) -[-1, 1, 2, 3, 1000000000000000000] -[[1], [1], [1,2], [1,2], [1,2]] - -# Test recursive object merge - -{"k": {"a": 1, "b": 2}} * . -{"k": {"a": 0,"c": 3}} -{"k": {"a": 0, "b": 2, "c": 3}} - -{"k": {"a": 1, "b": 2}, "hello": {"x": 1}} * . -{"k": {"a": 0,"c": 3}, "hello": 1} -{"k": {"a": 0, "b": 2, "c": 3}, "hello": 1} - -{"k": {"a": 1, "b": 2}, "hello": 1} * . -{"k": {"a": 0,"c": 3}, "hello": {"x": 1}} -{"k": {"a": 0, "b": 2, "c": 3}, "hello": {"x": 1}} - -{"a": {"b": 1}, "c": {"d": 2}, "e": 5} * . -{"a": {"b": 2}, "c": {"d": 3, "f": 9}} -{"a": {"b": 2}, "c": {"d": 3, "f": 9}, "e": 5} - -[.[]|arrays] -[1,2,"foo",[],[3,[]],{},true,false,null] -[[],[3,[]]] - -[.[]|objects] -[1,2,"foo",[],[3,[]],{},true,false,null] -[{}] - -[.[]|iterables] -[1,2,"foo",[],[3,[]],{},true,false,null] -[[],[3,[]],{}] - -[.[]|scalars] -[1,2,"foo",[],[3,[]],{},true,false,null] -[1,2,"foo",true,false,null] - -[.[]|values] -[1,2,"foo",[],[3,[]],{},true,false,null] -[1,2,"foo",[],[3,[]],{},true,false] - -[.[]|booleans] -[1,2,"foo",[],[3,[]],{},true,false,null] -[true,false] - -[.[]|nulls] -[1,2,"foo",[],[3,[]],{},true,false,null] -[null] - -flatten -[0, [1], [[2]], [[[3]]]] -[0, 1, 2, 3] - -flatten(2) -[0, [1], [[2]], [[[3]]]] -[0, 1, 2, [3]] - -flatten(2) -[0, [1, [2]], [1, [[3], 2]]] -[0, 1, 2, 1, [3], 2] - -transpose -[[1], [2,3]] -[[1,2],[null,3]] - -ascii_upcase -"useful but not for é" -"USEFUL BUT NOT FOR é" - -bsearch(4) -[1,2,3] --4 - -[strptime("%Y-%m-%dT%H:%M:%SZ")|(.,mktime)] -"2015-03-05T23:51:47Z" -[[2015,2,5,23,51,47,4,63],1425599507] - -strftime("%Y-%m-%dT%H:%M:%SZ") -[2015,2,5,23,51,47,4,63] -"2015-03-05T23:51:47Z" - -gmtime -1425599507 -[2015,2,5,23,51,47,4,63] - -# module system -import "a" as foo; import "b" as bar; def fooa: foo::a; [fooa, bar::a, bar::b, foo::a] -null -["a","b","c","a"] - -import "c" as foo; [foo::a, foo::c] -null -[0,"acmehbah"] - -modulemeta -"c" -{"whatever":null,"deps":[{"as":"foo","is_data":false,"relpath":"a"},{"search":"./","as":"d","is_data":false,"relpath":"d"},{"search":"./","as":"d2","is_data":false,"relpath":"d"},{"search":"./../lib/jq","as":"e","is_data":false,"relpath":"e"},{"search":"./../lib/jq","as":"f","is_data":false,"relpath":"f"},{"as":"d","is_data":true,"relpath":"data"}]} - -%%FAIL IGNORE MSG -import "syntaxerror" as e; . -jq: error: syntax error, unexpected ';', expecting $end (Unix shell quoting issues?) at /home/nico/ws/jq/tests/modules/syntaxerror/syntaxerror.jq, line 1: - -%%FAIL -%::wat -jq: error: syntax error, unexpected '%', expecting $end (Unix shell quoting issues?) at , line 1: - -import "test_bind_order" as check; check::check -null -true - -try -. catch . -"very-long-string" -"string (\"very-long-...) cannot be negated" - -try join(",") catch . -["1",2] -"string (\",\") and number (2) cannot be added" - -try join(",") catch . -["1","2",{"a":{"b":{"c":33}}}] -"string (\",\") and object ({\"a\":{\"b\":{...) cannot be added" - -{if:0,and:1,or:2,then:3,else:4,elif:5,end:6,as:7,def:8,reduce:9,foreach:10,try:11,catch:12,label:13,import:14,module:15} -null -{"if":0,"and":1,"or":2,"then":3,"else":4,"elif":5,"end":6,"as":7,"def":8,"reduce":9,"foreach":10,"try":11,"catch":12,"label":13,"import":14,"module":15} - -try (1/.) catch . -0 -"number (1) and number (0) cannot be divided because the divisor is zero" - -try (1%.) catch . -0 -"number (1) and number (0) cannot be divided (remainder) because the divisor is zero" - -%%FAIL -1/0 -jq: error: Division by zero? at , line 1: - diff --git a/tests/jq.test b/tests/jq.test new file mode 100644 index 00000000..84daddf9 --- /dev/null +++ b/tests/jq.test @@ -0,0 +1,1163 @@ +# Tests are groups of three lines: program, input, expected output +# Blank lines and lines starting with # are ignored + +# +# Simple value tests to check parser. Input is irrelevant +# + +true +null +true + +false +null +false + +null +42 +null + +1 +null +1 + + +-1 +null +-1 + +# FIXME: much more number testing needed + +{} +null +{} + +[] +null +[] + +{x: -1} +null +{"x": -1} + +# The input line starts with a 0xFEFF (byte order mark) codepoint +# No, there is no reason to have a byte order mark in UTF8 text. +# But apparently people do, so jq shouldn't break on it. +. +"byte order mark" +"byte order mark" + +# We test escapes by matching them against Unicode codepoints +# FIXME: more tests needed for weird unicode stuff (e.g. utf16 pairs) +"Aa\r\n\t\b\f\u03bc" +null +"Aa\u000d\u000a\u0009\u0008\u000c\u03bc" + +. +"Aa\r\n\t\b\f\u03bc" +"Aa\u000d\u000a\u0009\u0008\u000c\u03bc" + +"inter\("pol" + "ation")" +null +"interpolation" + +@text,@json,([1,.] | (@csv, @tsv)),@html,@uri,@sh,@base64 +"<>&'\"\t" +"<>&'\"\t" +"\"<>&'\\\"\\t\"" +"1,\"<>&'\"\"\t\"" +"1\t<>&'\"\\t" +"<>&'"\t" +"%3C%3E%26'%22%09" +"'<>&'\\''\"\t'" +"PD4mJyIJ" + +# regression test for #436 +@base64 +"foóbar\n" +"Zm/Ds2Jhcgo=" + +@uri +"\u03bc" +"%CE%BC" + +@html "\(.)" +"" +"<script>hax</script>" + +[.[]|tojson|fromjson] +["foo", 1, ["a", 1, "b", 2, {"foo":"bar"}]] +["foo",1,["a",1,"b",2,{"foo":"bar"}]] + +# +# Dictionary construction syntax +# + +{a: 1} +null +{"a":1} + +{a,b,(.d):.a,e:.b} +{"a":1, "b":2, "c":3, "d":"c"} +{"a":1, "b":2, "c":1, "e":2} + +{"a",b,"a$\(1+1)"} +{"a":1, "b":2, "c":3, "a$2":4} +{"a":1, "b":2, "a$2":4} + +# +# Field access, piping +# + +.foo +{"foo": 42, "bar": 43} +42 + +.foo | .bar +{"foo": {"bar": 42}, "bar": "badvalue"} +42 + +.foo.bar +{"foo": {"bar": 42}, "bar": "badvalue"} +42 + +.foo_bar +{"foo_bar": 2} +2 + +.["foo"].bar +{"foo": {"bar": 42}, "bar": "badvalue"} +42 + +."foo"."bar" +{"foo": {"bar": 20}} +20 + +[.[]|.foo?] +[1,[2],{"foo":3,"bar":4},{},{"foo":5}] +[3,null,5] + +[.[]|.foo?.bar?] +[1,[2],[],{"foo":3},{"foo":{"bar":4}},{}] +[4,null] + +[..] +[1,[[2]],{ "a":[1]}] +[[1,[[2]],{"a":[1]}],1,[[2]],[2],2,{"a":[1]},[1],1] + +[.[]|.[]?] +[1,null,[],[1,[2,[[3]]]],[{}],[{"a":[1,[2]]}]] +[1,[2,[[3]]],{},{"a":[1,[2]]}] + +[.[]|.[1:3]?] +[1,null,true,false,"abcdef",{},{"a":1,"b":2},[],[1,2,3,4,5],[1,2]] +[null,"bc",[],[2,3],[2]] + +# +# Negative array indices +# + +try (.foo[-1] = 0) catch . +null +"Out of bounds negative array index" + +try (.foo[-2] = 0) catch . +null +"Out of bounds negative array index" + +.[-1] = 5 +[0,1,2] +[0,1,5] + +.[-2] = 5 +[0,1,2] +[0,5,2] + +# +# Multiple outputs, iteration +# + +.[] +[1,2,3] +1 +2 +3 + +1,1 +[] +1 +1 + +1,. +[] +1 +[] + +[.] +[2] +[[2]] + +[[2]] +[3] +[[2]] + +[{}] +[2] +[{}] + +[.[]] +["a"] +["a"] + +[(.,1),((.,.[]),(2,3))] +["a","b"] +[["a","b"],1,["a","b"],"a","b",2,3] + +[([5,5][]),.,.[]] +[1,2,3] +[5,5,[1,2,3],1,2,3] + +{x: (1,2)},{x:3} | .x +null +1 +2 +3 + +.[-2] +[1,2,3] +2 + +[range(0;10)] +null +[0,1,2,3,4,5,6,7,8,9] + +[range(0;10;3)] +null +[0,3,6,9] + +[range(0;10;-1)] +null +[] + +[range(0;-5;-1)] +null +[0,-1,-2,-3,-4] + +[while(.<100; .*2)] +1 +[1,2,4,8,16,32,64] + +[(label $here | .[] | if .>1 then break $here else . end), "hi!"] +[0,1,2] +[0,1,"hi!"] + +[(label $here | .[] | if .>1 then break $here else . end), "hi!"] +[0,2,1] +[0,"hi!"] + +%%FAIL +. as $foo | break $foo +jq: error: *label-foo/0 is not defined at , line 1: + +[.[]|[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]] +[1,2,3,4,5] +[1,2,6,24,120] + +[label $out | foreach .[] as $item ([3, null]; if .[0] < 1 then break $out else [.[0] -1, $item] end; .[1])] +[11,22,33,44,55,66,77,88,99] +[11,22,33] + +[foreach range(5) as $item (0; $item)] +null +[0,1,2,3,4] + +[foreach .[] as [$i, $j] (0; . + $i - $j)] +[[2,1], [5,3], [6,4]] +[1,3,5] + +[foreach .[] as {a:$a} (0; . + $a; -.)] +[{"a":1}, {"b":2}, {"a":3, "b":4}] +[-1, -1, -4] + +[limit(3; .[])] +[11,22,33,44,55,66,77,88,99] +[11,22,33] + +[first(range(.)), last(range(.)), nth(0; range(.)), nth(5; range(.)), try nth(-1; range(.)) catch .] +10 +[0,9,0,5,"nth doesn't support negative indices"] + +# +# Check that various builtins evalute all arguments where appropriate, +# doing cartesian products where appropriate. +# + +# Check that limit does work for each value produced by n! +[limit(5,7; range(9))] +null +[0,1,2,3,4,0,1,2,3,4,5,6] + +# Same check for nth +[nth(5,7; range(9;0;-1))] +null +[4,2] + +# Same check for range/3 +[range(0,1,2;4,3,2;2,3)] +null +[0,2,0,3,0,2,0,0,0,1,3,1,1,1,1,1,2,2,2,2] + +# Same check for range/1 +[range(3,5)] +null +[0,1,2,0,1,2,3,4] + +# Same check for index/1, rindex/1, indices/1 +[(index(",","|"), rindex(",","|")), indices(",","|")] +"a,b|c,d,e||f,g,h,|,|,i,j" +[1,3,22,19,[1,5,7,12,14,16,18,20,22],[3,9,10,17,19]] + +# Same check for join/1 +join(",","/") +["a","b","c","d"] +"a,b,c,d" +"a/b/c/d" + +[.[]|join("a")] +[[],[""],["",""],["","",""]] +["","","a","aa"] + +# Same check for flatten/1 +flatten(-1,3,2,1) +[0, [1], [[2]], [[[3]]]] +[0,[1],[[2]],[[[3]]]] +[0,1,2,3] +[0,1,2,[3]] +[0,1,[2],[[3]]] + + +# +# Slices +# + +[.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]] +[0,1,2,3,4,5,6] +[[], [2,3], [0,1,2,3,4], [5,6], [], []] + +[.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]] +"abcdefghi" +["","","abcdefg","hi","",""] + +del(.[2:4],.[0],.[-2:]) +[0,1,2,3,4,5,6,7] +[1,4,5] + +.[2:4] = ([], ["a","b"], ["a","b","c"]) +[0,1,2,3,4,5,6,7] +[0,1,4,5,6,7] +[0,1,"a","b",4,5,6,7] +[0,1,"a","b","c",4,5,6,7] + + +# +# Variables +# + +1 as $x | 2 as $y | [$x,$y,$x] +null +[1,2,1] + +[1,2,3][] as $x | [[4,5,6,7][$x]] +null +[5] +[6] +[7] + +42 as $x | . | . | . + 432 | $x + 1 +34324 +43 + +1 as $x | [$x,$x,$x as $x | $x] +null +[1,1,1] + +[1, {c:3, d:4}] as [$a, {c:$b, b:$c}] | $a, $b, $c +null +1 +3 +null + +. as {as: $kw, "str": $str, ("e"+"x"+"p"): $exp} | [$kw, $str, $exp] +{"as": 1, "str": 2, "exp": 3} +[1, 2, 3] + +.[] as [$a, $b] | [$b, $a] +[[1], [1, 2, 3]] +[null, 1] +[2, 1] + +. as $i | . as [$i] | $i +[0] +0 + +. as [$i] | . as $i | $i +[0] +[0] + +%%FAIL +. as [] | null +jq: error: syntax error, unexpected ']', expecting '$' or '[' or '{' (Unix shell quoting issues?) at , line 1: + +%%FAIL +. as {} | null +jq: error: syntax error, unexpected '}' (Unix shell quoting issues?) at , line 1: + +# [.,(.[] | {x:.},.),.,.[]] + +# +# Builtin functions +# + +1+1 +null +2 + +1+1 +"wtasdf" +2.0 + +2-1 +null +1 + +2-(-1) +null +3 + +1e+0+0.001e3 +"I wonder what this will be?" +20e-1 + +.+4 +15 +19.0 + +.+null +{"a":42} +{"a":42} + +null+. +null +null + +.a+.b +{"a":42} +42 + +[1,2,3] + [.] +null +[1,2,3,null] + +{"a":1} + {"b":2} + {"c":3} +"asdfasdf" +{"a":1, "b":2, "c":3} + +"asdf" + "jkl;" + . + . + . +"some string" +"asdfjkl;some stringsome stringsome string" + +"\u0000\u0020\u0000" + . +"\u0000\u0020\u0000" +"\u0000 \u0000\u0000 \u0000" + +42 - . +11 +31 + +[1,2,3,4,1] - [.,3] +1 +[2,4] + +[10 * 20, 20 / .] +4 +[200, 5] + +1 + 2 * 2 + 10 / 2 +null +10 + +[16 / 4 / 2, 16 / 4 * 2, 16 - 4 - 2, 16 - 4 + 2] +null +[2, 8, 10, 14] + +25 % 7 +null +4 + +49732 % 472 +null +172 + +1 + tonumber + ("10" | tonumber) +4 +15 + +[{"a":42},.object,10,.num,false,true,null,"b",[1,4]] | .[] as $x | [$x == .[]] +{"object": {"a":42}, "num":10.0} +[true, true, false, false, false, false, false, false, false] +[true, true, false, false, false, false, false, false, false] +[false, false, true, true, false, false, false, false, false] +[false, false, true, true, false, false, false, false, false] +[false, false, false, false, true, false, false, false, false] +[false, false, false, false, false, true, false, false, false] +[false, false, false, false, false, false, true, false, false] +[false, false, false, false, false, false, false, true, false] +[false, false, false, false, false, false, false, false, true ] + +[.[] | length] +[[], {}, [1,2], {"a":42}, "asdf", "\u03bc"] +[0, 0, 2, 1, 4, 1] + +map(keys) +[{}, {"abcd":1,"abc":2,"abcde":3}, {"x":1, "z": 3, "y":2}] +[[], ["abc","abcd","abcde"], ["x","y","z"]] + +[1,2,empty,3,empty,4] +null +[1,2,3,4] + +map(add) +[[], [1,2,3], ["a","b","c"], [[3],[4,5],[6]], [{"a":1}, {"b":2}, {"a":3}]] +[null, 6, "abc", [3,4,5,6], {"a":3, "b": 2}] + +map_values(.+1) +[0,1,2] +[1,2,3] + +# +# User-defined functions +# Oh god. +# + +def f: . + 1; def g: def g: . + 100; f | g | f; (f | g), g +3.0 +106.0 +105.0 + +def f: (1000,2000); f +123412345 +1000 +2000 + +def f(a;b;c;d;e;f): [a+1,b,c,d,e,f]; f(.[0];.[1];.[0];.[0];.[0];.[0]) +[1,2] +[2,2,1,1,1,1] + +# Many arguments +def f(a;b;c;d;e;f;g;h;i;j): [j,i,h,g,f,e,d,c,b,a]; f(.[0];.[1];.[2];.[3];.[4];.[5];.[6];.[7];.[8];.[9]) +[0,1,2,3,4,5,6,7,8,9] +[9,8,7,6,5,4,3,2,1,0] + +([1,2] + [4,5]) +[1,2,3] +[1,2,4,5] + +true +[1] +true + +null,1,null +"hello" +null +1 +null + +[1,2,3] +[5,6] +[1,2,3] + +[.[]|floor] +[-1.1,1.1,1.9] +[-2, 1, 1] + +[.[]|sqrt] +[4,9] +[2,3] + +(add / length) as $m | map((. - $m) as $d | $d * $d) | add / length | sqrt +[2,4,4,4,5,5,7,9] +2 + +# Should write a test that calls the -lm function from C (or bc(1)) to +# check that they match the corresponding jq functions. However, +# there's so little template code standing between that it suffices to +# test a handful of these. The results were checked by eye against +# bc(1). +atan * 4 * 1000000|floor / 1000000 +1 +3.141592 + +[(3.141592 / 2) * (range(0;20) / 20)|cos * 1000000|floor / 1000000] +null +[1,0.996917,0.987688,0.972369,0.951056,0.923879,0.891006,0.85264,0.809017,0.760406,0.707106,0.649448,0.587785,0.522498,0.45399,0.382683,0.309017,0.233445,0.156434,0.078459] + +[(3.141592 / 2) * (range(0;20) / 20)|sin * 1000000|floor / 1000000] +null +[0,0.078459,0.156434,0.233445,0.309016,0.382683,0.45399,0.522498,0.587785,0.649447,0.707106,0.760405,0.809016,0.85264,0.891006,0.923879,0.951056,0.972369,0.987688,0.996917] + + +def f(x): x | x; f([.], . + [42]) +[1,2,3] +[[[1,2,3]]] +[[1,2,3],42] +[[1,2,3,42]] +[1,2,3,42,42] + +# test multiple function arities and redefinition +def f: .+1; def g: f; def f: .+100; def f(a):a+.+11; [(g|f(20)), f] +1 +[33,101] + +# test closures and lexical scoping +def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x) +"more testing" +[1,100,2100.0,100,2100.0] + +# test def f($a) syntax +def x(a;b): a as $a | b as $b | $a + $b; def y($a;$b): $a + $b; def check(a;b): [x(a;b)] == [y(a;b)]; check(.[];.[]*2) +[1,2,3] +true + +# test backtracking through function calls and returns +# this test is *evil* +[[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | g; f[0] | [f][0][1] | f] +999999999 +[[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]] + +# test recursion +def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac] +[1,2,3,4] +[1,2,6,24] + +# test stack overflow and reallocation +# this test is disabled for now, it takes a realllllly long time. +# def f: if length > 1000 then . else .+[1]|f end; f | length +# [] +# 1001 + +reduce .[] as $x (0; . + $x) +[1,2,4] +7 + +reduce .[] as [$i, {j:$j}] (0; . + $i - $j) +[[2,{"j":1}], [5,{"j":3}], [6,{"j":4}]] +5 + +reduce [[1,2,10], [3,4,10]][] as [$i,$j] (0; . + $i * $j) +null +14 + +. as $dot|any($dot[];not) +[1,2,3,4,true,false,1,2,3,4,5] +true + +. as $dot|any($dot[];not) +[1,2,3,4,true] +false + +. as $dot|all($dot[];.) +[1,2,3,4,true,false,1,2,3,4,5] +false + +. as $dot|all($dot[];.) +[1,2,3,4,true] +true + +# +# Paths +# + +path(.foo[0,1]) +null +["foo", 0] +["foo", 1] + +path(.[] | select(.>3)) +[1,5,3] +[1] + +path(.) +42 +[] + +[paths] +[1,[[],{"a":2}]] +[[0],[1],[1,0],[1,1],[1,1,"a"]] + +[leaf_paths] +[1,[[],{"a":2}]] +[[0],[1,1,"a"]] + +["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) +{"bar": 42, "foo": ["a", "b", "c", "d"]} +"b" +{"bar": 42, "foo": ["a", 20, "c", "d"]} +{"bar": 42, "foo": ["a", "c", "d"]} + +map(getpath([2])), map(setpath([2]; 42)), map(delpaths([[2]])) +[[0], [0,1], [0,1,2]] +[null, null, 2] +[[0,null,42], [0,1,42], [0,1,42]] +[[0], [0,1], [0,1]] + +map(delpaths([[0,"foo"]])) +[[{"foo":2, "x":1}], [{"bar":2}]] +[[{"x":1}], [{"bar":2}]] + +["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p]) +{"bar":false} +null +{"bar":false, "foo": [null, 20]} +{"bar":false} + +delpaths([[-200]]) +[1,2,3] +[1,2,3] + +del(.), del(empty), del((.foo,.bar,.baz) | .[2,3,0]), del(.foo[0], .bar[0], .foo, .baz.bar[0].x) +{"foo": [0,1,2,3,4], "bar": [0,1]} +null +{"foo": [0,1,2,3,4], "bar": [0,1]} +{"foo": [1,4], "bar": [1]} +{"bar": [1]} + +# +# Assignment +# +.message = "goodbye" +{"message": "hello"} +{"message": "goodbye"} + +.foo = .bar +{"bar":42} +{"foo":42, "bar":42} + +.foo |= .+1 +{"foo": 42} +{"foo": 43} + +.[] += 2, .[] *= 2, .[] -= 2, .[] /= 2, .[] %=2 +[1,3,5] +[3,5,7] +[2,6,10] +[-1,1,3] +[0.5, 1.5, 2.5] +[1,1,1] + +[.[] % 7] +[-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7] +[0,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,0] + +.foo += .foo +{"foo":2} +{"foo":4} + +.[0].a |= {"old":., "new":(.+1)} +[{"a":1,"b":2}] +[{"a":{"old":1, "new":2},"b":2}] + +def inc(x): x |= .+1; inc(.[].a) +[{"a":1,"b":2},{"a":2,"b":4},{"a":7,"b":8}] +[{"a":2,"b":2},{"a":3,"b":4},{"a":8,"b":8}] + +.[2][3] = 1 +[4] +[4, null, [null, null, null, 1]] + +.foo[2].bar = 1 +{"foo":[11], "bar":42} +{"foo":[11,null,{"bar":1}], "bar":42} + +# +# Conditionals +# + +[.[] | if .foo then "yep" else "nope" end] +[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}] +["yep","yep","yep","yep","nope","nope","yep","nope"] + +[.[] | if .baz then "strange" elif .foo then "yep" else "nope" end] +[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}] +["yep","yep","yep","yep","nope","nope","yep","nope"] + + +# FIXME: define/test behaviour of 'if (.foo,.bar) then A else B end' + +[.[] | [.foo[] // .bar]] +[{"foo":[1,2], "bar": 42}, {"foo":[1], "bar": null}, {"foo":[null,false,3], "bar": 18}, {"foo":[], "bar":42}, {"foo": [null,false,null], "bar": 41}] +[[1,2], [1], [3], [42], [41]] + +.[] //= .[0] +["hello",true,false,[false],null] +["hello",true,"hello",[false],"hello"] + +.[] | [.[0] and .[1], .[0] or .[1]] +[[true,[]], [false,1], [42,null], [null,false]] +[true,true] +[false,true] +[false,true] +[false,false] + +[.[] | not] +[1,0,false,null,true,"hello"] +[false,false,true,true,false,false] + +# Check numeric comparison binops +[10 > 0, 10 > 10, 10 > 20, 10 < 0, 10 < 10, 10 < 20] +{} +[true,false,false,false,false,true] + +[10 >= 0, 10 >= 10, 10 >= 20, 10 <= 0, 10 <= 10, 10 <= 20] +{} +[true,true,false,false,true,true] + +# And some in/equality tests +[ 10 == 10, 10 != 10, 10 != 11, 10 == 11] +{} +[true,false,true,false] + +["hello" == "hello", "hello" != "hello", "hello" == "world", "hello" != "world" ] +{} +[true,false,false,true] + +[[1,2,3] == [1,2,3], [1,2,3] != [1,2,3], [1,2,3] == [4,5,6], [1,2,3] != [4,5,6]] +{} +[true,false,false,true] + +[{"foo":42} == {"foo":42},{"foo":42} != {"foo":42}, {"foo":42} != {"bar":42}, {"foo":42} == {"bar":42}] +{} +[true,false,true,false] + +# ugly complicated thing +[{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":18},"world"]},{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":19},"world"]}] +{} +[true,false] + +# containment operator +[("foo" | contains("foo")), ("foobar" | contains("foo")), ("foo" | contains("foobar"))] +{} +[true, true, false] + +# Try/catch and general `?` operator +[.[]|try if . == 0 then error("foo") elif . == 1 then .a elif . == 2 then empty else . end catch .] +[0,1,2,3] +["foo","Cannot index number with string \"a\"",3] + +[.[]|(.a, .a)?] +[null,true,{"a":1}] +[null,null,1,1] + +[[.[]|[.a,.a]]?] +[null,true,{"a":1}] +[] + +try error("\($__loc__)") catch . +null +"{\"file\":\"\",\"line\":1}" + +# string operations +[.[]|startswith("foo")] +["fo", "foo", "barfoo", "foobar", "barfoob"] +[false, true, false, true, false] + +[.[]|endswith("foo")] +["fo", "foo", "barfoo", "foobar", "barfoob"] +[false, true, true, false, false] + +[.[] | split(", ")] +["a,b, c, d, e,f",", a,b, c, d, e,f, "] +[["a,b","c","d","e,f"],["","a,b","c","d","e,f",""]] + +split("") +"abc" +["a","b","c"] + +[.[]|ltrimstr("foo")] +["fo", "foo", "barfoo", "foobar", "afoo"] +["fo","","barfoo","bar","afoo"] + +[.[]|rtrimstr("foo")] +["fo", "foo", "barfoo", "foobar", "foob"] +["fo","","bar","foobar","foob"] + +[(index(","), rindex(",")), indices(",")] +"a,bc,def,ghij,klmno" +[1,13,[1,4,8,13]] + +indices(1) +[0,1,1,2,3,4,1,5] +[1,2,6] + +indices([1,2]) +[0,1,2,3,1,4,2,5,1,2,6,7] +[1,8] + +indices([1,2]) +[1] +[] + +indices(", ") +"a,b, cd,e, fgh, ijkl" +[3,9,14] + +[.[]|split(",")] +["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] +[["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]] + +[.[]|split(", ")] +["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] +[["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]] + +[.[] * 3] +["a", "ab", "abc"] +["aaa", "ababab", "abcabcabc"] + +[.[] / ","] +["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] +[["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]] + +[.[] / ", "] +["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"] +[["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]] + +map(.[1] as $needle | .[0] | contains($needle)) +[[[],[]], [[1,2,3], [1,2]], [[1,2,3], [3,1]], [[1,2,3], [4]], [[1,2,3], [1,4]]] +[true, true, true, false, false] + +map(.[1] as $needle | .[0] | contains($needle)) +[[["foobar", "foobaz"], ["baz", "bar"]], [["foobar", "foobaz"], ["foo"]], [["foobar", "foobaz"], ["blap"]]] +[true, true, false] + +[({foo: 12, bar:13} | contains({foo: 12})), ({foo: 12} | contains({})), ({foo: 12, bar:13} | contains({baz:14}))] +{} +[true, true, false] + +{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {}}}) +{} +true + +{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {bar: 14}}}) +{} +false + +sort +[42,[2,5,3,11],10,{"a":42,"b":2},{"a":42},true,2,[2,6],"hello",null,[2,5,6],{"a":[],"b":1},"abc","ab",[3,10],{},false,"abcd",null] +[null,null,false,true,2,10,42,"ab","abc","abcd","hello",[2,5,3,11],[2,5,6],[2,6],[3,10],{},{"a":42},{"a":42,"b":2},{"a":[],"b":1}] + +(sort_by(.b) | sort_by(.a)), sort_by(.a, .b), sort_by(.b, .c), group_by(.b), group_by(.a + .b - .c == 2) +[{"a": 1, "b": 4, "c": 14}, {"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}, {"a": 0, "b": 2, "c": 43}] +[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}] +[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}] +[{"a": 4, "b": 1, "c": 3}, {"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 3}, {"a": 1, "b": 4, "c": 14}] +[[{"a": 4, "b": 1, "c": 3}], [{"a": 0, "b": 2, "c": 43}], [{"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}]] +[[{"a": 1, "b": 4, "c": 14}, {"a": 0, "b": 2, "c": 43}], [{"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}]] + +unique +[1,2,5,3,5,3,1,3] +[1,2,3,5] + +unique +[] +[] + +[min, max, min_by(.[1]), max_by(.[1]), min_by(.[2]), max_by(.[2])] +[[4,2,"a"],[3,1,"a"],[2,4,"a"],[1,3,"a"]] +[[1,3,"a"],[4,2,"a"],[3,1,"a"],[2,4,"a"],[4,2,"a"],[1,3,"a"]] + +[min,max,min_by(.),max_by(.)] +[] +[null,null,null,null] + +.foo[.baz] +{"foo":{"bar":4},"baz":"bar"} +4 + +.[] | .error = "no, it's OK" +[{"error":true}] +{"error": "no, it's OK"} + +[{a:1}] | .[] | .a=999 +null +{"a": 999} + +to_entries +{"a": 1, "b": 2} +[{"key":"a", "value":1}, {"key":"b", "value":2}] + +from_entries +[{"key":"a", "value":1}, {"Key":"b", "value":2}] +{"a": 1, "b": 2} + +from_entries +[{"key":"a", "Value":1}, {"Name":"b", "value":2}] +{"a": 1, "b": 2} + +with_entries(.key |= "KEY_" + .) +{"a": 1, "b": 2} +{"KEY_a": 1, "KEY_b": 2} + +map(has("foo")) +[{"foo": 42}, {}] +[true, false] + +map(has(2)) +[[0,1], ["a","b","c"]] +[false, true] + +keys +[42,3,35] +[0,1,2] + +[][.] +1000000000000000000 +null + +map([1,2][0:.]) +[-1, 1, 2, 3, 1000000000000000000] +[[1], [1], [1,2], [1,2], [1,2]] + +# Test recursive object merge + +{"k": {"a": 1, "b": 2}} * . +{"k": {"a": 0,"c": 3}} +{"k": {"a": 0, "b": 2, "c": 3}} + +{"k": {"a": 1, "b": 2}, "hello": {"x": 1}} * . +{"k": {"a": 0,"c": 3}, "hello": 1} +{"k": {"a": 0, "b": 2, "c": 3}, "hello": 1} + +{"k": {"a": 1, "b": 2}, "hello": 1} * . +{"k": {"a": 0,"c": 3}, "hello": {"x": 1}} +{"k": {"a": 0, "b": 2, "c": 3}, "hello": {"x": 1}} + +{"a": {"b": 1}, "c": {"d": 2}, "e": 5} * . +{"a": {"b": 2}, "c": {"d": 3, "f": 9}} +{"a": {"b": 2}, "c": {"d": 3, "f": 9}, "e": 5} + +[.[]|arrays] +[1,2,"foo",[],[3,[]],{},true,false,null] +[[],[3,[]]] + +[.[]|objects] +[1,2,"foo",[],[3,[]],{},true,false,null] +[{}] + +[.[]|iterables] +[1,2,"foo",[],[3,[]],{},true,false,null] +[[],[3,[]],{}] + +[.[]|scalars] +[1,2,"foo",[],[3,[]],{},true,false,null] +[1,2,"foo",true,false,null] + +[.[]|values] +[1,2,"foo",[],[3,[]],{},true,false,null] +[1,2,"foo",[],[3,[]],{},true,false] + +[.[]|booleans] +[1,2,"foo",[],[3,[]],{},true,false,null] +[true,false] + +[.[]|nulls] +[1,2,"foo",[],[3,[]],{},true,false,null] +[null] + +flatten +[0, [1], [[2]], [[[3]]]] +[0, 1, 2, 3] + +flatten(2) +[0, [1], [[2]], [[[3]]]] +[0, 1, 2, [3]] + +flatten(2) +[0, [1, [2]], [1, [[3], 2]]] +[0, 1, 2, 1, [3], 2] + +transpose +[[1], [2,3]] +[[1,2],[null,3]] + +ascii_upcase +"useful but not for é" +"USEFUL BUT NOT FOR é" + +bsearch(4) +[1,2,3] +-4 + +[strptime("%Y-%m-%dT%H:%M:%SZ")|(.,mktime)] +"2015-03-05T23:51:47Z" +[[2015,2,5,23,51,47,4,63],1425599507] + +strftime("%Y-%m-%dT%H:%M:%SZ") +[2015,2,5,23,51,47,4,63] +"2015-03-05T23:51:47Z" + +gmtime +1425599507 +[2015,2,5,23,51,47,4,63] + +# module system +import "a" as foo; import "b" as bar; def fooa: foo::a; [fooa, bar::a, bar::b, foo::a] +null +["a","b","c","a"] + +import "c" as foo; [foo::a, foo::c] +null +[0,"acmehbah"] + +modulemeta +"c" +{"whatever":null,"deps":[{"as":"foo","is_data":false,"relpath":"a"},{"search":"./","as":"d","is_data":false,"relpath":"d"},{"search":"./","as":"d2","is_data":false,"relpath":"d"},{"search":"./../lib/jq","as":"e","is_data":false,"relpath":"e"},{"search":"./../lib/jq","as":"f","is_data":false,"relpath":"f"},{"as":"d","is_data":true,"relpath":"data"}]} + +%%FAIL IGNORE MSG +import "syntaxerror" as e; . +jq: error: syntax error, unexpected ';', expecting $end (Unix shell quoting issues?) at /home/nico/ws/jq/tests/modules/syntaxerror/syntaxerror.jq, line 1: + +%%FAIL +%::wat +jq: error: syntax error, unexpected '%', expecting $end (Unix shell quoting issues?) at , line 1: + +import "test_bind_order" as check; check::check +null +true + +try -. catch . +"very-long-string" +"string (\"very-long-...) cannot be negated" + +try join(",") catch . +["1",2] +"string (\",\") and number (2) cannot be added" + +try join(",") catch . +["1","2",{"a":{"b":{"c":33}}}] +"string (\",\") and object ({\"a\":{\"b\":{...) cannot be added" + +{if:0,and:1,or:2,then:3,else:4,elif:5,end:6,as:7,def:8,reduce:9,foreach:10,try:11,catch:12,label:13,import:14,module:15} +null +{"if":0,"and":1,"or":2,"then":3,"else":4,"elif":5,"end":6,"as":7,"def":8,"reduce":9,"foreach":10,"try":11,"catch":12,"label":13,"import":14,"module":15} + +try (1/.) catch . +0 +"number (1) and number (0) cannot be divided because the divisor is zero" + +try (1%.) catch . +0 +"number (1) and number (0) cannot be divided (remainder) because the divisor is zero" + +%%FAIL +1/0 +jq: error: Division by zero? at , line 1: + diff --git a/tests/jqtest b/tests/jqtest new file mode 100755 index 00000000..8df5f110 --- /dev/null +++ b/tests/jqtest @@ -0,0 +1,5 @@ +#!/bin/sh + +. "${0%/*}/setup" + +$VALGRIND $Q $JQ -L "$mods" --run-tests $JQTESTDIR/jq.test diff --git a/tests/mantest b/tests/mantest new file mode 100755 index 00000000..b3740971 --- /dev/null +++ b/tests/mantest @@ -0,0 +1,5 @@ +#!/bin/sh + +. "${0%/*}/setup" + +(cd $JQBASEDIR/docs && rake mantests) | $VALGRIND $Q $JQ -L "$mods" --run-tests diff --git a/tests/onig.test b/tests/onig.test new file mode 100644 index 00000000..ed6cd3b0 --- /dev/null +++ b/tests/onig.test @@ -0,0 +1,85 @@ +# match builtin +[match("( )*"; "g")] +"abc" +[{"offset":0, "length":0, "string":"", "captures":[]},{"offset":1, "length":0, "string":"", "captures":[]},{"offset":2, "length":0, "string":"", "captures":[]}] + +[match("( )*"; "gn")] +"abc" +[] + +[match("a"; "gi")] +"āáàä" +[] + +[match(["(bar)"])] +"foo bar" +[{"offset": 4, "length": 3, "string": "bar", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": null}]}] + +# offsets account for combining codepoints and multi-byte UTF-8 +[match("bar")] +"ā bar with a combining codepoint U+0304" +[{"offset": 3, "length": 3, "string": "bar", "captures":[]}] + +# matches with combining codepoints still count them in their length +[match("bār")] +"a bār" +[{"offset": 2, "length": 4, "string": "bār", "captures":[]}] + +[match(".+?\\b")] +"ā two-codepoint grapheme" +[{"offset": 0, "length": 2, "string": "ā", "captures":[]}] + +[match(["foo (?bar)? foo", "ig"])] +"foo bar foo foo foo" +[{"offset": 0, "length": 11, "string": "foo bar foo", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]},{"offset":12, "length": 8, "string": "foo foo", "captures":[{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}] + +#test builtin +[test("( )*"; "gn")] +"abc" +[false] + +[test("ā")] +"ā" +[true] + +capture("(?[a-z]+)-(?[0-9]+)") +"xyzzy-14" +{"a":"xyzzy","n":"14"} + + +# jq-coded utilities built on match: +# +# The second element in these tests' inputs tests the case where the +# fromstring matches both the head and tail of the string +[.[] | sub(", "; ":")] +["a,b, c, d, e,f", ", a,b, c, d, e,f, "] +["a,b:c, d, e,f",":a,b, c, d, e,f, "] + +sub("^(?.)"; "Head=\(.head) Tail=") +"abcdef" +"Head=a Tail=bcdef" + +[.[] | gsub(", "; ":")] +["a,b, c, d, e,f",", a,b, c, d, e,f, "] +["a,b:c:d:e,f",":a,b:c:d:e,f:"] + +gsub("(?\\d)"; ":\(.d);") +"a1b2" +"a:1;b:2;" + +[.[] | scan(", ")] +["a,b, c, d, e,f",", a,b, c, d, e,f, "] +[", ",", ",", ",", ",", ",", ",", ",", "] + +[.[]|[[sub(", *";":")], [gsub(", *";":")], [scan(", *")]]] +["a,b, c, d, e,f",", a,b, c, d, e,f, "] +[[["a:b, c, d, e,f"],["a:b:c:d:e:f"],[",",", ",", ",", ",","]],[[":a,b, c, d, e,f, "],[":a:b:c:d:e:f:"],[", ",",",", ",", ",", ",",",", "]]] + +[.[]|[[sub(", +";":")], [gsub(", +";":")], [scan(", +")]]] +["a,b, c, d, e,f",", a,b, c, d, e,f, "] +[[["a,b:c, d, e,f"],["a,b:c:d:e,f"],[", ",", ",", "]],[[":a,b, c, d, e,f, "],[":a,b:c:d:e,f:"],[", ",", ",", ",", ",", "]]] + +# reference to named captures +gsub("(?.)[^a]*"; "+\(.x)-") +"Abcabc" +"+A-+a-" diff --git a/tests/onigtest b/tests/onigtest new file mode 100755 index 00000000..55c26bd2 --- /dev/null +++ b/tests/onigtest @@ -0,0 +1,5 @@ +#!/bin/sh + +. "${0%/*}/setup" + +$VALGRIND $Q $JQ -L "$mods" --run-tests $JQTESTDIR/onig.test diff --git a/tests/run b/tests/run deleted file mode 100755 index 0dc966f4..00000000 --- a/tests/run +++ /dev/null @@ -1,226 +0,0 @@ -#!/bin/sh - -set -e - -if which valgrind > /dev/null; then - VALGRIND='valgrind --error-exitcode=1 --leak-check=full --suppressions=tests/onig.supp' - Q=-q -else - VALGRIND= - Q= -fi - -mods=$PWD/tests/modules - -# jq-coded tests here: -cat $@ | $VALGRIND $Q ./jq -L "$mods" --run-tests - -clean=true -d= -clean () { - if ! $clean; then - echo "See temp files in $d!" - elif [ -n "$d" ]; then - rm -rf "$d" - fi -} -trap clean EXIT -d=`mktemp -d -t || true` -if [ -z "$d" ]; then - echo "Your OS does not support mktemp(1) -d" 1>&2 - exit 0 -fi - -if [ -f $PWD/.libs/libinject_errors.so ]; then - # Do some simple error injection tests to check that we're handling - # I/O errors correctly. - ( - jq=$PWD/jq - libinject=$PWD/.libs/libinject_errors.so - cd $d - LD_PRELOAD=$libinject $jq . /dev/null - touch fail_read - LD_PRELOAD=$libinject $jq . fail_read && exit 2 - touch fail_close - LD_PRELOAD=$libinject $jq . fail_close && exit 2 - true - ) -fi - -printf 'a\0b\nc\0d\ne' > $d/input -$VALGRIND $Q ./jq -Rse '. == "a\u0000b\nc\u0000d\ne"' $d/input -$VALGRIND $Q ./jq -Rne '[inputs] == ["a\u0000b", "c\u0000d", "e"]' $d/input - -## Test constant folding - -## XXX If we add a builtin to list the program's disassembly then we can -## move all of these into tests/all.test - -# String constant folding (addition only) -n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '"foo"' | wc -l` -if [ $n -ne 5 ]; then - echo "Constant expression folding for strings didn't work" - exit 1 -fi - -# Numeric constant folding (not all ops yet) -n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '1+1' | wc -l` -if [ $n -ne 5 ]; then - echo "Constant expression folding for strings didn't work" - exit 1 -fi -n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '1-1' | wc -l` -if [ $n -ne 5 ]; then - echo "Constant expression folding for strings didn't work" - exit 1 -fi -n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '2*3' | wc -l` -if [ $n -ne 5 ]; then - echo "Constant expression folding for strings didn't work" - exit 1 -fi -n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9/3' | wc -l` -if [ $n -ne 5 ]; then - echo "Constant expression folding for strings didn't work" - exit 1 -fi -n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9==3' | wc -l` -if [ $n -ne 5 ]; then - echo "Constant expression folding for strings didn't work" - exit 1 -fi -n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9!=3' | wc -l` -if [ $n -ne 5 ]; then - echo "Constant expression folding for strings didn't work" - exit 1 -fi -n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9<=3' | wc -l` -if [ $n -ne 5 ]; then - echo "Constant expression folding for strings didn't work" - exit 1 -fi -n=`$VALGRIND $Q ./jq -n --debug-dump-disasm '9>=3' | wc -l` -if [ $n -ne 5 ]; then - echo "Constant expression folding for strings didn't work" - exit 1 -fi - -## Test JSON sequence support - -## XXX If we add a `stream_fromjson` builtin then we can move these tests -## into tests/all.test - -cat > $d/expected < /dev/null 2> $d/out -cmp $d/out $d/expected - -cat > $d/expected < /dev/null 2> $d/out -cmp $d/out $d/expected - -# Note that here jq sees no inputs at all but it still succeeds because -# --seq ignores parse errors -cat > $d/expected < $d/out 2>&1 -cmp $d/out $d/expected - -# Numeric values truncated by EOF are ignored -cat > $d/expected < $d/out 2>&1 -cmp $d/out $d/expected - -cat > $d/expected </dev/null 2> $d/out -cmp $d/out $d/expected - -## Test streaming parser - -## If we add an option to stream to the `import ... as $symbol;` directive -## then we can move these tests into tests/all.test. -$VALGRIND $Q ./jq -c '. as $d|path(..) as $p|$d|getpath($p)|scalars_or_empty|[$p,.]' < "$PWD/tests/torture/input0.json" > $d/out0 -$VALGRIND $Q ./jq --stream -c '.|select(length==2)' < "$PWD/tests/torture/input0.json" > $d/out1 -diff $d/out0 $d/out1 - -## XXX This test can be moved to tests/all.test _now_ -clean=false -if which seq > /dev/null 2>&1; then - # XXX We should try every prefix of input0.json, but that makes this - # test very, very slow when run with valgrind, and the whole point - # is to run it with valgrind. - # - #len=`wc -c < "$PWD/tests/torture/input0.json"` - if [ -z "$VALGRIND" ]; then - start=1 - end=`wc -c < "$PWD/tests/torture/input0.json"` - else - start=120 - end=151 - fi - for i in `seq $start $end`; do - dd "if=tests/torture/input0.json" bs=$i count=1 2>/dev/null | - $VALGRIND ./jq -c . > $d/out0 2>$d/err || true - if [ -n "$VALGRIND" ]; then - grep '^==[0-9][0-9]*== ERROR SUMMARY: 0 errors' $d/err > /dev/null - else - tail -1 $d/err | egrep -i 'assert|abort|core' && false - fi - - dd "if=tests/torture/input0.json" bs=$i count=1 2>/dev/null | - $VALGRIND ./jq -cn --stream -L "$mods" 'import "streaming" as streaming; streaming::tovalues(inputs)' > $d/out1 2>$d/err || true - if [ -n "$VALGRIND" ]; then - grep '^==[0-9][0-9]*== ERROR SUMMARY: 0 errors' $d/err > /dev/null - else - tail -1 $d/err | egrep -i 'assert|abort|core' && false - fi - - diff $d/out0 $d/out1 - done -else - echo "Not doing torture tests" -fi - -## Fuzz parser - -## XXX With a `urandom` builtin we could move this test into tests/all.test -clean=false -if dd if=/dev/urandom bs=16 count=1024 > $d/rand 2>/dev/null; then - # Have a /dev/urandom, good - $VALGRIND $Q ./jq --seq . $d/rand >/dev/null 2>&1 - $VALGRIND $Q ./jq --seq --stream . $d/rand >/dev/null 2>&1 - dd if=/dev/urandom bs=16 count=1024 > $d/rand 2>/dev/null - $VALGRIND $Q ./jq --seq . $d/rand >/dev/null 2>&1 - $VALGRIND $Q ./jq --seq --stream . $d/rand >/dev/null 2>&1 - dd if=/dev/urandom bs=16 count=1024 > $d/rand 2>/dev/null - $VALGRIND $Q ./jq --seq . $d/rand >/dev/null 2>&1 - $VALGRIND $Q ./jq --seq --stream . $d/rand >/dev/null 2>&1 -fi -clean=true - -## Test library/module system - -# Check handling of ~/.jq; these can't move into jq_test.c yet because -# they depend on $HOME -if [ "`HOME="$mods" $VALGRIND $Q ./jq -nr fg`" != foobar ]; then - echo "Bug #479 appears to be back" 1>&2 - exit 1 -fi - -if [ `HOME="$mods" $VALGRIND $Q ./jq --debug-dump-disasm -n fg | grep '^[a-z]' | wc -l` -gt 3 ]; then - echo "Binding too many defs into program" 1>&2 - exit 1 -fi - -exit 0 diff --git a/tests/setup b/tests/setup new file mode 100755 index 00000000..56b7c0b4 --- /dev/null +++ b/tests/setup @@ -0,0 +1,34 @@ +#!/bin/sh + +set -e + +JQTESTDIR=$(cd "$(dirname "$0")" && pwd) +JQBASEDIR=$JQTESTDIR/.. +JQ=$JQBASEDIR/jq + +if which valgrind > /dev/null; then + VALGRIND="valgrind --error-exitcode=1 --leak-check=full \ + --suppressions=$JQTESTDIR/onig.supp" + Q=-q +else + VALGRIND= + Q= +fi + +mods=$JQTESTDIR/modules + +clean=true +d= +clean () { + if ! $clean; then + echo "See temp files in $d!" + elif [ -n "$d" ]; then + rm -rf "$d" + fi +} +trap clean EXIT +d=`mktemp -d -t || true` +if [ -z "$d" ]; then + echo "Your OS does not support mktemp(1) -d" 1>&2 + exit 0 +fi diff --git a/tests/shtest b/tests/shtest new file mode 100755 index 00000000..3840565e --- /dev/null +++ b/tests/shtest @@ -0,0 +1,196 @@ +#!/bin/sh + +. "${0%/*}/setup" + +if [ -f "$JQBASEDIR/.libs/libinject_errors.so" ]; then + # Do some simple error injection tests to check that we're handling + # I/O errors correctly. + ( + libinject=$JQBASEDIR/.libs/libinject_errors.so + cd $d + LD_PRELOAD=$libinject $JQ . /dev/null + touch fail_read + LD_PRELOAD=$libinject $JQ . fail_read && exit 2 + touch fail_close + LD_PRELOAD=$libinject $JQ . fail_close && exit 2 + true + ) +fi + +printf 'a\0b\nc\0d\ne' > $d/input +$VALGRIND $Q $JQ -Rse '. == "a\u0000b\nc\u0000d\ne"' $d/input +$VALGRIND $Q $JQ -Rne '[inputs] == ["a\u0000b", "c\u0000d", "e"]' $d/input + +## Test constant folding + +## XXX If we add a builtin to list the program's disassembly then we can +## move all of these into tests/all.test + +# String constant folding (addition only) +n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '"foo"' | wc -l` +if [ $n -ne 5 ]; then + echo "Constant expression folding for strings didn't work" + exit 1 +fi + +# Numeric constant folding (not all ops yet) +n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '1+1' | wc -l` +if [ $n -ne 5 ]; then + echo "Constant expression folding for strings didn't work" + exit 1 +fi +n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '1-1' | wc -l` +if [ $n -ne 5 ]; then + echo "Constant expression folding for strings didn't work" + exit 1 +fi +n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '2*3' | wc -l` +if [ $n -ne 5 ]; then + echo "Constant expression folding for strings didn't work" + exit 1 +fi +n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '9/3' | wc -l` +if [ $n -ne 5 ]; then + echo "Constant expression folding for strings didn't work" + exit 1 +fi +n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '9==3' | wc -l` +if [ $n -ne 5 ]; then + echo "Constant expression folding for strings didn't work" + exit 1 +fi +n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '9!=3' | wc -l` +if [ $n -ne 5 ]; then + echo "Constant expression folding for strings didn't work" + exit 1 +fi +n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '9<=3' | wc -l` +if [ $n -ne 5 ]; then + echo "Constant express