Codebase list jp / abdf188
Import Upstream version 0.1.3 Clint Adams 5 years ago
29 changed file(s) with 6551 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 jp
1 build/*
0 language: go
1
2 go:
3 - 1.5
4
5 install:
6 - go build jp.go
7
8 script: make test
0 Apache License
1 Version 2.0, January 2004
2 http://www.apache.org/licenses/
3
4 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
5
6 1. Definitions.
7
8 "License" shall mean the terms and conditions for use, reproduction,
9 and distribution as defined by Sections 1 through 9 of this document.
10
11 "Licensor" shall mean the copyright owner or entity authorized by
12 the copyright owner that is granting the License.
13
14 "Legal Entity" shall mean the union of the acting entity and all
15 other entities that control, are controlled by, or are under common
16 control with that entity. For the purposes of this definition,
17 "control" means (i) the power, direct or indirect, to cause the
18 direction or management of such entity, whether by contract or
19 otherwise, or (ii) ownership of fifty percent (50%) or more of the
20 outstanding shares, or (iii) beneficial ownership of such entity.
21
22 "You" (or "Your") shall mean an individual or Legal Entity
23 exercising permissions granted by this License.
24
25 "Source" form shall mean the preferred form for making modifications,
26 including but not limited to software source code, documentation
27 source, and configuration files.
28
29 "Object" form shall mean any form resulting from mechanical
30 transformation or translation of a Source form, including but
31 not limited to compiled object code, generated documentation,
32 and conversions to other media types.
33
34 "Work" shall mean the work of authorship, whether in Source or
35 Object form, made available under the License, as indicated by a
36 copyright notice that is included in or attached to the work
37 (an example is provided in the Appendix below).
38
39 "Derivative Works" shall mean any work, whether in Source or Object
40 form, that is based on (or derived from) the Work and for which the
41 editorial revisions, annotations, elaborations, or other modifications
42 represent, as a whole, an original work of authorship. For the purposes
43 of this License, Derivative Works shall not include works that remain
44 separable from, or merely link (or bind by name) to the interfaces of,
45 the Work and Derivative Works thereof.
46
47 "Contribution" shall mean any work of authorship, including
48 the original version of the Work and any modifications or additions
49 to that Work or Derivative Works thereof, that is intentionally
50 submitted to Licensor for inclusion in the Work by the copyright owner
51 or by an individual or Legal Entity authorized to submit on behalf of
52 the copyright owner. For the purposes of this definition, "submitted"
53 means any form of electronic, verbal, or written communication sent
54 to the Licensor or its representatives, including but not limited to
55 communication on electronic mailing lists, source code control systems,
56 and issue tracking systems that are managed by, or on behalf of, the
57 Licensor for the purpose of discussing and improving the Work, but
58 excluding communication that is conspicuously marked or otherwise
59 designated in writing by the copyright owner as "Not a Contribution."
60
61 "Contributor" shall mean Licensor and any individual or Legal Entity
62 on behalf of whom a Contribution has been received by Licensor and
63 subsequently incorporated within the Work.
64
65 2. Grant of Copyright License. Subject to the terms and conditions of
66 this License, each Contributor hereby grants to You a perpetual,
67 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
68 copyright license to reproduce, prepare Derivative Works of,
69 publicly display, publicly perform, sublicense, and distribute the
70 Work and such Derivative Works in Source or Object form.
71
72 3. Grant of Patent License. Subject to the terms and conditions of
73 this License, each Contributor hereby grants to You a perpetual,
74 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
75 (except as stated in this section) patent license to make, have made,
76 use, offer to sell, sell, import, and otherwise transfer the Work,
77 where such license applies only to those patent claims licensable
78 by such Contributor that are necessarily infringed by their
79 Contribution(s) alone or by combination of their Contribution(s)
80 with the Work to which such Contribution(s) was submitted. If You
81 institute patent litigation against any entity (including a
82 cross-claim or counterclaim in a lawsuit) alleging that the Work
83 or a Contribution incorporated within the Work constitutes direct
84 or contributory patent infringement, then any patent licenses
85 granted to You under this License for that Work shall terminate
86 as of the date such litigation is filed.
87
88 4. Redistribution. You may reproduce and distribute copies of the
89 Work or Derivative Works thereof in any medium, with or without
90 modifications, and in Source or Object form, provided that You
91 meet the following conditions:
92
93 (a) You must give any other recipients of the Work or
94 Derivative Works a copy of this License; and
95
96 (b) You must cause any modified files to carry prominent notices
97 stating that You changed the files; and
98
99 (c) You must retain, in the Source form of any Derivative Works
100 that You distribute, all copyright, patent, trademark, and
101 attribution notices from the Source form of the Work,
102 excluding those notices that do not pertain to any part of
103 the Derivative Works; and
104
105 (d) If the Work includes a "NOTICE" text file as part of its
106 distribution, then any Derivative Works that You distribute must
107 include a readable copy of the attribution notices contained
108 within such NOTICE file, excluding those notices that do not
109 pertain to any part of the Derivative Works, in at least one
110 of the following places: within a NOTICE text file distributed
111 as part of the Derivative Works; within the Source form or
112 documentation, if provided along with the Derivative Works; or,
113 within a display generated by the Derivative Works, if and
114 wherever such third-party notices normally appear. The contents
115 of the NOTICE file are for informational purposes only and
116 do not modify the License. You may add Your own attribution
117 notices within Derivative Works that You distribute, alongside
118 or as an addendum to the NOTICE text from the Work, provided
119 that such additional attribution notices cannot be construed
120 as modifying the License.
121
122 You may add Your own copyright statement to Your modifications and
123 may provide additional or different license terms and conditions
124 for use, reproduction, or distribution of Your modifications, or
125 for any such Derivative Works as a whole, provided Your use,
126 reproduction, and distribution of the Work otherwise complies with
127 the conditions stated in this License.
128
129 5. Submission of Contributions. Unless You explicitly state otherwise,
130 any Contribution intentionally submitted for inclusion in the Work
131 by You to the Licensor shall be under the terms and conditions of
132 this License, without any additional terms or conditions.
133 Notwithstanding the above, nothing herein shall supersede or modify
134 the terms of any separate license agreement you may have executed
135 with Licensor regarding such Contributions.
136
137 6. Trademarks. This License does not grant permission to use the trade
138 names, trademarks, service marks, or product names of the Licensor,
139 except as required for reasonable and customary use in describing the
140 origin of the Work and reproducing the content of the NOTICE file.
141
142 7. Disclaimer of Warranty. Unless required by applicable law or
143 agreed to in writing, Licensor provides the Work (and each
144 Contributor provides its Contributions) on an "AS IS" BASIS,
145 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
146 implied, including, without limitation, any warranties or conditions
147 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
148 PARTICULAR PURPOSE. You are solely responsible for determining the
149 appropriateness of using or redistributing the Work and assume any
150 risks associated with Your exercise of permissions under this License.
151
152 8. Limitation of Liability. In no event and under no legal theory,
153 whether in tort (including negligence), contract, or otherwise,
154 unless required by applicable law (such as deliberate and grossly
155 negligent acts) or agreed to in writing, shall any Contributor be
156 liable to You for damages, including any direct, indirect, special,
157 incidental, or consequential damages of any character arising as a
158 result of this License or out of the use or inability to use the
159 Work (including but not limited to damages for loss of goodwill,
160 work stoppage, computer failure or malfunction, or any and all
161 other commercial damages or losses), even if such Contributor
162 has been advised of the possibility of such damages.
163
164 9. Accepting Warranty or Additional Liability. While redistributing
165 the Work or Derivative Works thereof, You may choose to offer,
166 and charge a fee for, acceptance of support, warranty, indemnity,
167 or other liability obligations and/or rights consistent with this
168 License. However, in accepting such obligations, You may act only
169 on Your own behalf and on Your sole responsibility, not on behalf
170 of any other Contributor, and only if You agree to indemnify,
171 defend, and hold each Contributor harmless for any liability
172 incurred by, or claims asserted against, such Contributor by reason
173 of your accepting any such warranty or additional liability.
174
175 END OF TERMS AND CONDITIONS
176
177 APPENDIX: How to apply the Apache License to your work.
178
179 To apply the Apache License to your work, attach the following
180 boilerplate notice, with the fields enclosed by brackets "[]"
181 replaced with your own identifying information. (Don't include
182 the brackets!) The text should be enclosed in the appropriate
183 comment syntax for the file format. We also recommend that a
184 file or class name and description of purpose be included on the
185 same "printed page" as the copyright notice for easier
186 identification within third-party archives.
187
188 Copyright [yyyy] [name of copyright owner]
189
190 Licensed under the Apache License, Version 2.0 (the "License");
191 you may not use this file except in compliance with the License.
192 You may obtain a copy of the License at
193
194 http://www.apache.org/licenses/LICENSE-2.0
195
196 Unless required by applicable law or agreed to in writing, software
197 distributed under the License is distributed on an "AS IS" BASIS,
198 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
199 See the License for the specific language governing permissions and
200 limitations under the License.
0 JP_VERSION=""
1
2 help:
3 @echo "Please use \`make <target>' where <target> is one of"
4 @echo " test to run all the tests."
5
6 test:
7 # CLI specific test cases.
8 test/vendor/bats/libexec/bats test/cases
9 # JMESPath compliance tests, using the jp-compliance
10 # runner from github.com/jmespath/jmespath.test
11 test/jp-compliance -d test/compliance/ -e ./jp
12
13 # This will create/tag a new release locally, but not push anything.
14 # The workflow for a new release is:
15 #
16 # 1. $ make new-release JP_VERSION=1.0.0
17 # < you'll get prompted for a few things >
18 #
19
20 # 2. $ git push origin master --tags
21 #
22 #
23 # 3. Go to github and create a release
24 # Right now, the last step isn't automated. You still
25 # have to manually upload the release assets from build/.
26 new-release:
27 scripts/bump-version $(JP_VERSION)
28 git add jp.go && git commit -m "Bumping version to $(JP_VERSION)"
29 git tag -s -m "Tagging $(JP_VERSION) release" $(JP_VERSION)
30 scripts/build-all-platforms
31 scripts/sign-all
32
33 .PHONY: help test
0 jp: The JMESPath Command Line Interface
1 Copyright 2015 James Saryerwinnie. All Rights Reserved.
0 jp
1 ==
2
3 The ``jp`` command is a command line interface to
4 [JMESPath](http://jmespath.org), an expression
5 language for manipulating JSON:
6
7 ```
8 $ echo '{"foo": {"bar": ["a", "b", "c"]}}' | jp foo.bar[1]
9 "b"
10 ```
11
12
13 # Installing
14
15 If you're a Mac user, you can install via homebrew from the JMESPath
16 Homebrew tap:
17
18 ```
19 brew tap jmespath/jmespath
20 brew install jp
21 ```
22
23 You can download prebuilt binaries if you prefer.
24 Check the [Release page](https://github.com/jmespath/jp/releases)
25 to download the latest ``jp`` executable. There are binaries
26 available for Windows, Linux, Mac, FreeBSD.
27
28 For example, to install version 0.1.2 on a 64 bit Linux environment use:
29
30 ```
31 sudo wget https://github.com/jmespath/jp/releases/download/0.1.2/jp-linux-amd64 -O /usr/local/bin/jp \
32 && sudo chmod +x /usr/local/bin/jp
33 ```
34
35 ## Building from Source
36
37 If you have a Go environment installed you can also run:
38 ``go get -u github.com/jmespath/jp`` to get the latest version
39 of jmespath. If you have the repo checked out locally you can also
40 just ``go build`` the project:
41
42 ```
43 git clone git://github.com/jmespath/jp
44 cd jp
45 go build
46 ./jp --help
47 ```
48
49 And finally, if you have a go environment setup, but don't
50 have a workspace/GOPATH configured, you can just run
51 ``scripts/build-self-contained`` and it will build the
52 ``jp`` executable for you:
53
54 ```
55 git clone git://github.com/jmespath/jp
56 cd jp
57 scripts/build-self-contained
58 ./jp --help
59 ```
60
61 ## Cross platform binaries
62
63 If you have a go 1.5 environment setup, you can build cross platform binaries
64 by running ``scripts/build-all-platforms``.
65 This will put executables in the ``build/`` directory and each
66 executable will be named ``jp-<platform>``, e.g
67 ``jp-darwin-amd64``, ``jp-linux-386``, etc.
68
69
70 # Usage
71
72 The most basic usage of ``jp`` is to accept input JSON data through stdin,
73 apply the JMESPath expression you've provided as an argument to ``jp``, and
74 print the resulting JSON data to stdout.
75
76 ```
77 $ echo '{"key": "value"}' | jp key
78 "value"
79
80 $ echo '{"foo": {"bar": ["a", "b", "c"]}}' | jp foo.bar[1]
81 "b"
82 ```
83
84 Note the argument after ``jp``. This is a JMESPath expression.
85 If you have no idea what that is, there's a
86 [JMESPath Tutorial](http://jmespath.org/tutorial.html) that
87 will take you through the JMESPath language.
88
89 ## Input From a File
90
91 In addition to this basic usage, there's also other ways to use
92 ``jp``. First, instead of reading from stdin, you can provide
93 a JSON file as input using the ``-f/--filename`` option:
94
95
96 ```
97 $ echo '{"foo": {"bar": "baz"}}' > /tmp/input.json
98 $ jp -f /tmp/input.json foo.bar
99 "baz"
100 ```
101
102 ## Unquoted Output
103
104 [[Notice]] the output of the above command is ``"baz"``, that is,
105 a double quote ``"``, followed by baz, followed by another
106 a final double quote. This can be problematic if you're
107 trying to use this with other commands that just want
108 the string and *not* the quoted string. For example:
109
110
111 ```
112 $ curl -s https://api.github.com/repos/golang/go/events | jp [0].actor.url
113 "https://api.github.com/users/robpike"
114 ```
115
116 Now let's suppose we want to then curl the above URL. Our first
117 attempt might look something like this:
118
119 ```
120 $ curl $(curl -s https://api.github.com/repos/golang/go/events | ./jp [0].actor.url)
121 ```
122
123 And it would fail with:
124
125 ```
126 curl: (1) Protocol "https not supported or disabled in libcurl
127 ```
128
129 To fix this, we can use the ``-u/--unquoted`` option to specify that
130 any result that is a string will be printed without quotes. Note
131 that the result is not surrounded by double quotes:
132
133 ```
134 $ curl -s https://api.github.com/repos/golang/go/events | jp --unquoted [0].actor.url
135 https://api.github.com/users/robpike
136 ```
137
138 If this is a common enough occurance for you, you can set the ``JP_UNQUOTED`` environment
139 variable to make this the default behavior:
140
141 ```
142 $ export JP_UNQUOTED=true
143 $ curl -s https://api.github.com/repos/golang/go/events | jp --unquoted [0].actor.url
144 https://api.github.com/users/robpike
145 ```
146
147
148 Also keep in mind that this behavior
149 only applies if the result of evaluating the JMESPath expression is a string:
150
151 ```
152 $ echo '{"foo": ["bar", "baz"]}' | jp -u foo[0]
153 bar
154 # But -u does nothing here because the result is an array, not a string:
155 $ echo '{"foo": ["bar", "baz"]}' | jp -u foo
156 [
157 "bar",
158 "baz"
159 ]
160 ```
161
162 You can also use the ``-u/--unquoted`` option along with the
163 [join](http://jmespath.org/specification.html#join) function to create a list
164 of strings that can be piped into other POSIX text tools. For example:
165
166 ```
167 $ echo '{"foo": {"bar": ["a", "b", "c"]}}' | jp foo.bar
168 [
169 "a",
170 "b",
171 "c"
172 ]
173 ```
174
175 Suppose we want to iterate over the 3 values in the list and
176 run some bash code for each value. We can do this by running:
177
178 ```
179 $ for name in $(echo '{"foo": {"bar": ["a", "b", "c"]}}' | \
180 jp -u 'join(`"\n"`, foo.bar)');
181 do
182 echo "Processing: $name";
183 done
184 Processing: a
185 Processing: b
186 Processing: c
187 ```
188
189
190 ## Examples
191
192 If you're new to the JMESPath language, or just want to see what the language is
193 capable of, you can check out the [JMESPath tutorial](http://jmespath.org/tutorial.html)
194 as well as the [JMESPath examples](http://jmespath.org/examples.html), which contains
195 a curated set of JMESPath examples. But for now, here's a real world example.
196 Let's say you wanted to see what the latest activity was with regard to the issue
197 tracker for one of your github issues. Here's a simple way to do this:
198
199 ```
200 $ curl -s https://api.github.com/repos/golang/go/events | jp \
201 "[?type=='IssuesEvent'].payload.\
202 {Title: issue.title, URL: issue.url, User: issue.user.login, Event: action}"
203
204 [
205 {
206 "Event": "opened",
207 "Title": "release: cherry pick changes for 1.5 to release branch",
208 "URL": "https://api.github.com/repos/golang/go/issues/12093",
209 "User": "adg"
210 },
211 {
212 "Event": "closed",
213 "Title": "fmt: x format verb for []byte fails in a recursive call to Fscanf from a scanln call in go1.5rc1",
214 "URL": "https://api.github.com/repos/golang/go/issues/12090",
215 "User": "hubslave"
216 },
217 {
218 "Event": "closed",
219 "Title": "doc: release notes recommend wrong version of NaCl",
220 "URL": "https://api.github.com/repos/golang/go/issues/12062",
221 "User": "davecheney"
222 },
223 {
224 "Event": "opened",
225 "Title": "cmd/godoc: show internal packages when explicitly requested",
226 "URL": "https://api.github.com/repos/golang/go/issues/12092",
227 "User": "jacobsa"
228 }
229 ]
230 ```
231
232 Try it for your own repo, instead of ``/golang/go``, replace it with your own
233 ``/owner/repo`` value. In words, this expression says:
234
235 * For each element in the top level list, select only the elements where the
236 ``type`` key is equal to the string ``IssueEvent``
237 * For each of those filtered elements select the ``payload`` hash.
238 * Each each ``payload`` hash, we're going to create our own hash that has
239 4 keys: ``Title``, ``URL``, ``User``, ``Event``. The value for each of key
240 is the result of evaluating these expressions in their respective order:
241 ``issue.title``, ``issue.url``, ``issue.user.login``, ``action``.
242
243 Ensure that if your expression has spaces you surround the expression
244 in quotes, as shown in the example above.
245
246 ## Testing
247
248 The parsing and evaluation of JMESPath expression is done in the
249 go-jmespath library, which is a dependency of this project. ``go-jmespath``
250 has extensive testing to ensure it is parsing and evaluating JMESPath
251 expressions correctly.
252
253 To ensure that there are no regressions between `go-imespath` and `jp`,
254 the entire suite of [JMESPath compliance tests](https://github.com/jmespath/jmespath.test)
255 are run against the `jp` executable.
256
257 This repo also include CLI specific test that verify the command line
258 options and output work as intended.
259
260 You can run all of these tests for `jp` by running `make test`:
261
262 ```
263 $ make test
264 # CLI specific test cases.
265 test/vendor/bats/libexec/bats test/cases
266 ✓ Has valid help output
267 ✓ Can display version
268 ✓ Can search basic expression
269 ✓ Can search subexpr expression
270 ✓ Can read input from file
271 ✓ Can print result unquoted
272 ✓ Bad JMESPath expression has non zero rc
273 - Large numbers are not printed with scientific notation (skipped)
274 ✓ Can accept expression from file
275 ✓ Can pretty print expr AST
276 ✓ Can sort int array
277
278 X tests, 0 failures, 1 skipped
279 # JMESPath compliance tests, using the jp-compliance
280 # runner from github.com/jmespath/jmespath.test
281 test/jp-compliance -d test/compliance/ -e ./jp
282 ............................................................
283 ............................................................
284 ............................................................
285 ............................................................
286 ............................................................
287 ............................................................
288 ............................................................
289 ............................................................
290 OK
291 ```
+123
-0
jp.go less more
0 package main
1
2 import (
3 "encoding/json"
4 "fmt"
5 "io/ioutil"
6 "os"
7
8 "github.com/jmespath/jp/Godeps/_workspace/src/github.com/codegangsta/cli"
9 "github.com/jmespath/jp/Godeps/_workspace/src/github.com/jmespath/go-jmespath"
10 )
11
12 const version = "0.1.3"
13
14 func main() {
15 app := cli.NewApp()
16 app.Name = "jp"
17 app.Version = version
18 app.Usage = "jp [<options>] <expression>"
19 app.Author = ""
20 app.Email = ""
21 app.Flags = []cli.Flag{
22 cli.StringFlag{
23 Name: "filename, f",
24 Usage: "Read input JSON from a file instead of stdin.",
25 },
26 cli.StringFlag{
27 Name: "expr-file, e",
28 Usage: "Read JMESPath expression from the specified file.",
29 },
30 cli.BoolFlag{
31 Name: "unquoted, u",
32 Usage: "If the final result is a string, it will be printed without quotes.",
33 EnvVar: "JP_UNQUOTED",
34 },
35 cli.BoolFlag{
36 Name: "ast",
37 Usage: "Only print the AST of the parsed expression. Do not rely on this output, only useful for debugging purposes.",
38 },
39 }
40 app.Action = runMainAndExit
41
42 app.Run(os.Args)
43 }
44
45 func runMainAndExit(c *cli.Context) {
46 os.Exit(runMain(c))
47 }
48
49 func errMsg(msg string, a ...interface{}) int {
50 fmt.Fprintf(os.Stderr, msg, a...)
51 fmt.Fprintln(os.Stderr)
52 return 1
53 }
54
55 func runMain(c *cli.Context) int {
56 var expression string
57 if c.String("expr-file") != "" {
58 byteExpr, err := ioutil.ReadFile(c.String("expr-file"))
59 expression = string(byteExpr)
60 if err != nil {
61 return errMsg("Error opening expression file: %s", err)
62 }
63 } else {
64 if len(c.Args()) == 0 {
65 return errMsg("Must provide at least one argument.")
66 }
67 expression = c.Args()[0]
68 }
69 if c.Bool("ast") {
70 parser := jmespath.NewParser()
71 parsed, err := parser.Parse(expression)
72 if err != nil {
73 if syntaxError, ok := err.(jmespath.SyntaxError); ok {
74 return errMsg("%s\n%s\n",
75 syntaxError,
76 syntaxError.HighlightLocation())
77 }
78 return errMsg("%s", err)
79 }
80 fmt.Println("")
81 fmt.Printf("%s\n", parsed)
82 return 0
83 }
84 var input interface{}
85 var jsonParser *json.Decoder
86 if c.String("filename") != "" {
87 f, err := os.Open(c.String("filename"))
88 if err != nil {
89 return errMsg("Error opening input file: %s", err)
90 }
91 jsonParser = json.NewDecoder(f)
92
93 } else {
94 jsonParser = json.NewDecoder(os.Stdin)
95 }
96 if err := jsonParser.Decode(&input); err != nil {
97 errMsg("Error parsing input json: %s\n", err)
98 return 2
99 }
100 result, err := jmespath.Search(expression, input)
101 if err != nil {
102 if syntaxError, ok := err.(jmespath.SyntaxError); ok {
103 return errMsg("%s\n%s\n",
104 syntaxError,
105 syntaxError.HighlightLocation())
106 }
107 return errMsg("Error evaluating JMESPath expression: %s", err)
108 }
109 converted, isString := result.(string)
110 if c.Bool("unquoted") && isString {
111 os.Stdout.WriteString(converted)
112 } else {
113 toJSON, err := json.MarshalIndent(result, "", " ")
114 if err != nil {
115 errMsg("Error marshalling result to JSON: %s\n", err)
116 return 3
117 }
118 os.Stdout.Write(toJSON)
119 }
120 os.Stdout.WriteString("\n")
121 return 0
122 }
0 #!/bin/bash
1 go get ./...
2 rm -rf ./build/jp-*
3 for goos in darwin linux windows freebsd; do
4 export GOOS="$goos"
5 for goarch in 386 amd64; do
6 export GOARCH="$goarch"
7 echo "Building for $GOOS/$GOARCH"
8 go build -v -o build/jp-$GOOS-$GOARCH 2>/dev/null
9 done
10 done
11 # Also build for ARM7/linux
12 export GOOS=linux
13 export GOARCH=arm
14 export GOARM=7
15 echo "Building for $GOOS/$GOARCH/$GOARM"
16 go build -v -o build/jp-$GOOS-$GOARCH-arm$GOARM 2> /dev/null
0 #!/bin/bash
1 # This is a self contained shell script for building jp without having to set
2 # up GOPATH. You just need go installed.
3 tempdir="$(mktemp -d -t jpbuild)"
4 tempgopath="$tempdir/go"
5 jppath="${tempgopath}/src/github.com/jmespath"
6 fakerepo="$jppath/jp"
7 mkdir -p $jppath
8 ln -s "$(pwd)" "$jppath/jp"
9 export GOPATH="$tempgopath"
10 cd "$fakerepo"
11 go build
0 #!/usr/bin/env python
1 import sys
2 import re
3 import os
4
5 version_line_re = re.compile(r'^const version = "\d+\.\d+\.\d+"')
6
7
8 if len(sys.argv) == 1 or sys.argv[1] == '':
9 print "Must provide jp version to bump to. (make new-release JP_VERSION=1.0.0)"
10 sys.exit(1)
11
12 new_version = sys.argv[1]
13 source_file = os.path.join(
14 os.path.dirname(
15 os.path.dirname(os.path.abspath(__file__))),
16 'jp.go')
17
18 assert os.path.isfile(source_file)
19
20 with open(source_file, 'r') as f:
21 contents = f.readlines()
22 for i, line in enumerate(contents):
23 if version_line_re.search(line) is not None:
24 contents[i] = 'const version = "%s"\n' % new_version
25 break
26 else:
27 sys.stderr.write("Could not find version string in %s\n" % source_file)
28
29 with open(source_file, 'w') as f:
30 for line in contents:
31 f.write(line)
0 #!/bin/bash
1 # Helper script to build "jp" on all supported platforms.
2 # This script uses docker so you don't have to have the cross
3 # platform golang environment setup. Just make sure you have docker
4 # installed. The built executables will be in build/
5 docker run --rm -v "$PWD:/go/src/jp" -w /go/src/jp golang:1.4.2-cross scripts/build-all-platforms
0 #!/bin/bash
1
2 set -e
3
4 cd build
5 shasum -a 256 ./jp-* > jp-checksums.sha256
6 gpg --clearsign --output jp-checksums.sha256.asc jp-checksums.sha256
7 rm jp-checksums.sha256
0 @test "Has valid help output" {
1 run ./jp --help
2 [ "$status" -eq 0 ]
3 echo $output | grep "\-\-filename"
4 }
5
6 @test "Can display version" {
7 run ./jp --version
8 [ "$status" -eq 0 ]
9 }
10
11 @test "Can search basic expression" {
12 output=$(echo '{"foo": "bar"}' | ./jp foo)
13 [ "$output" == "\"bar\"" ]
14 }
15
16 @test "Can search subexpr expression" {
17 output=$(echo '{"foo": {"bar": "baz"}}' | ./jp foo.bar)
18 [ "$output" == "\"baz\"" ]
19 }
20
21 @test "Can read input from file" {
22 echo '{"foo": "bar"}' > "$BATS_TMPDIR/input.json"
23 run ./jp -f "$BATS_TMPDIR/input.json" foo
24 [ "$output" == "\"bar\"" ]
25 }
26
27 @test "Can print result unquoted" {
28 output=$(echo '{"foo": "bar"}' | ./jp -u foo)
29 [ "$output" == "bar" ]
30 }
31
32 @test "Bad JMESPath expression has non zero rc" {
33 echo '{"foo": "bar"}' > "$BATS_TMPDIR/input.json"
34 run ./jp -f "$BATS_TMPDIR/input.json" "bax[expre]ssion"
35 [ "$status" -eq 1 ]
36 }
37
38 @test "Large numbers are not printed with scientific notation" {
39 skip
40 echo '{"foo": 47268765}' > "$BATS_TMPDIR/input.json"
41 run ./jp -f "$BATS_TMPDIR/input.json" "foo"
42 [ "$status" -eq 0 ]
43 [ "$output" == "47268765" ]
44 }
45
46 @test "Can accept expression from file" {
47 echo 'foo.bar' > "$BATS_TMPDIR/expr"
48 echo '{"foo": {"bar": "baz"}}' > "$BATS_TMPDIR/input.json"
49 run ./jp -u -f "$BATS_TMPDIR/input.json" -e "$BATS_TMPDIR/expr"
50 [ "$output" == "baz" ]
51 }
52
53 @test "Can pretty print expr AST" {
54 run ./jp --ast "foo"
55 expected='
56 ASTField {
57 value: "foo"
58 }'
59 echo "$output"
60 echo "$expected"
61 [ "$output" == "$expected" ]
62 }
63
64 @test "Can sort int array" {
65 echo '[2,1,3,5,4]' > "$BATS_TMPDIR/input.json"
66 echo "sort(@) | map(&to_string(@), @) | join('', @)" > "$BATS_TMPDIR/expr"
67 run ./jp -u -f "$BATS_TMPDIR/input.json" -e "$BATS_TMPDIR/expr"
68 [ "$status" -eq 0 ]
69 [ "$output" == "12345" ]
70 }
0 [{
1 "given":
2 {"foo": {"bar": {"baz": "correct"}}},
3 "cases": [
4 {
5 "expression": "foo",
6 "result": {"bar": {"baz": "correct"}}
7 },
8 {
9 "expression": "foo.bar",
10 "result": {"baz": "correct"}
11 },
12 {
13 "expression": "foo.bar.baz",
14 "result": "correct"
15 },
16 {
17 "expression": "foo\n.\nbar\n.baz",
18 "result": "correct"
19 },
20 {
21 "expression": "foo.bar.baz.bad",
22 "result": null
23 },
24 {
25 "expression": "foo.bar.bad",
26 "result": null
27 },
28 {
29 "expression": "foo.bad",
30 "result": null
31 },
32 {
33 "expression": "bad",
34 "result": null
35 },
36 {
37 "expression": "bad.morebad.morebad",
38 "result": null
39 }
40 ]
41 },
42 {
43 "given":
44 {"foo": {"bar": ["one", "two", "three"]}},
45 "cases": [
46 {
47 "expression": "foo",
48 "result": {"bar": ["one", "two", "three"]}
49 },
50 {
51 "expression": "foo.bar",
52 "result": ["one", "two", "three"]
53 }
54 ]
55 },
56 {
57 "given": ["one", "two", "three"],
58 "cases": [
59 {
60 "expression": "one",
61 "result": null
62 },
63 {
64 "expression": "two",
65 "result": null
66 },
67 {
68 "expression": "three",
69 "result": null
70 },
71 {
72 "expression": "one.two",
73 "result": null
74 }
75 ]
76 },
77 {
78 "given":
79 {"foo": {"1": ["one", "two", "three"], "-1": "bar"}},
80 "cases": [
81 {
82 "expression": "foo.\"1\"",
83 "result": ["one", "two", "three"]
84 },
85 {
86 "expression": "foo.\"1\"[0]",
87 "result": "one"
88 },
89 {
90 "expression": "foo.\"-1\"",
91 "result": "bar"
92 }
93 ]
94 }
95 ]
0 [
1 {
2 "given": {
3 "outer": {
4 "foo": "foo",
5 "bar": "bar",
6 "baz": "baz"
7 }
8 },
9 "cases": [
10 {
11 "expression": "outer.foo || outer.bar",
12 "result": "foo"
13 },
14 {
15 "expression": "outer.foo||outer.bar",
16 "result": "foo"
17 },
18 {
19 "expression": "outer.bar || outer.baz",
20 "result": "bar"
21 },
22 {
23 "expression": "outer.bar||outer.baz",
24 "result": "bar"
25 },
26 {
27 "expression": "outer.bad || outer.foo",
28 "result": "foo"
29 },
30 {
31 "expression": "outer.bad||outer.foo",
32 "result": "foo"
33 },
34 {
35 "expression": "outer.foo || outer.bad",
36 "result": "foo"
37 },
38 {
39 "expression": "outer.foo||outer.bad",
40 "result": "foo"
41 },
42 {
43 "expression": "outer.bad || outer.alsobad",
44 "result": null
45 },
46 {
47 "expression": "outer.bad||outer.alsobad",
48 "result": null
49 }
50 ]
51 },
52 {
53 "given": {
54 "outer": {
55 "foo": "foo",
56 "bool": false,
57 "empty_list": [],
58 "empty_string": ""
59 }
60 },
61 "cases": [
62 {
63 "expression": "outer.empty_string || outer.foo",
64 "result": "foo"
65 },
66 {
67 "expression": "outer.nokey || outer.bool || outer.empty_list || outer.empty_string || outer.foo",
68 "result": "foo"
69 }
70 ]
71 },
72 {
73 "given": {
74 "True": true,
75 "False": false,
76 "Number": 5,
77 "EmptyList": [],
78 "Zero": 0
79 },
80 "cases": [
81 {
82 "expression": "True && False",
83 "result": false
84 },
85 {
86 "expression": "False && True",
87 "result": false
88 },
89 {
90 "expression": "True && True",
91 "result": true
92 },
93 {
94 "expression": "False && False",
95 "result": false
96 },
97 {
98 "expression": "True && Number",
99 "result": 5
100 },
101 {
102 "expression": "Number && True",
103 "result": true
104 },
105 {
106 "expression": "Number && False",
107 "result": false
108 },
109 {
110 "expression": "Number && EmptyList",
111 "result": []
112 },
113 {
114 "expression": "Number && True",
115 "result": true
116 },
117 {
118 "expression": "EmptyList && True",
119 "result": []
120 },
121 {
122 "expression": "EmptyList && False",
123 "result": []
124 },
125 {
126 "expression": "True || False",
127 "result": true
128 },
129 {
130 "expression": "True || True",
131 "result": true
132 },
133 {
134 "expression": "False || True",
135 "result": true
136 },
137 {
138 "expression": "False || False",
139 "result": false
140 },
141 {
142 "expression": "Number || EmptyList",
143 "result": 5
144 },
145 {
146 "expression": "Number || True",
147 "result": 5
148 },
149 {
150 "expression": "Number || True && False",
151 "result": 5
152 },
153 {
154 "expression": "(Number || True) && False",
155 "result": false
156 },
157 {
158 "expression": "Number || (True && False)",
159 "result": 5
160 },
161 {
162 "expression": "!True",
163 "result": false
164 },
165 {
166 "expression": "!False",
167 "result": true
168 },
169 {
170 "expression": "!Number",
171 "result": false
172 },
173 {
174 "expression": "!EmptyList",
175 "result": true
176 },
177 {
178 "expression": "True && !False",
179 "result": true
180 },
181 {
182 "expression": "True && !EmptyList",
183 "result": true
184 },
185 {
186 "expression": "!False && !EmptyList",
187 "result": true
188 },
189 {
190 "expression": "!(True && False)",
191 "result": true
192 },
193 {
194 "expression": "!Zero",
195 "result": false
196 },
197 {
198 "expression": "!!Zero",
199 "result": true
200 }
201 ]
202 },
203 {
204 "given": {
205 "one": 1,
206 "two": 2,
207 "three": 3
208 },
209 "cases": [
210 {
211 "expression": "one < two",
212 "result": true
213 },
214 {
215 "expression": "one <= two",
216 "result": true
217 },
218 {
219 "expression": "one == one",
220 "result": true
221 },
222 {
223 "expression": "one == two",
224 "result": false
225 },
226 {
227 "expression": "one > two",
228 "result": false
229 },
230 {
231 "expression": "one >= two",
232 "result": false
233 },
234 {
235 "expression": "one != two",
236 "result": true
237 },
238 {
239 "expression": "one < two && three > one",
240 "result": true
241 },
242 {
243 "expression": "one < two || three > one",
244 "result": true
245 },
246 {
247 "expression": "one < two || three < one",
248 "result": true
249 },
250 {
251 "expression": "two < one || three < one",
252 "result": false
253 }
254 ]
255 }
256 ]
0 [
1 {
2 "given": {
3 "foo": [{"name": "a"}, {"name": "b"}],
4 "bar": {"baz": "qux"}
5 },
6 "cases": [
7 {
8 "expression": "@",
9 "result": {
10 "foo": [{"name": "a"}, {"name": "b"}],
11 "bar": {"baz": "qux"}
12 }
13 },
14 {
15 "expression": "@.bar",
16 "result": {"baz": "qux"}
17 },
18 {
19 "expression": "@.foo[0]",
20 "result": {"name": "a"}
21 }
22 ]
23 }
24 ]
0 [{
1 "given": {
2 "foo.bar": "dot",
3 "foo bar": "space",
4 "foo\nbar": "newline",
5 "foo\"bar": "doublequote",
6 "c:\\\\windows\\path": "windows",
7 "/unix/path": "unix",
8 "\"\"\"": "threequotes",
9 "bar": {"baz": "qux"}
10 },
11 "cases": [
12 {
13 "expression": "\"foo.bar\"",
14 "result": "dot"
15 },
16 {
17 "expression": "\"foo bar\"",
18 "result": "space"
19 },
20 {
21 "expression": "\"foo\\nbar\"",
22 "result": "newline"
23 },
24 {
25 "expression": "\"foo\\\"bar\"",
26 "result": "doublequote"
27 },
28 {
29 "expression": "\"c:\\\\\\\\windows\\\\path\"",
30 "result": "windows"
31 },
32 {
33 "expression": "\"/unix/path\"",
34 "result": "unix"
35 },
36 {
37 "expression": "\"\\\"\\\"\\\"\"",
38 "result": "threequotes"
39 },
40 {
41 "expression": "\"bar\".\"baz\"",
42 "result": "qux"
43 }
44 ]
45 }]
0 [
1 {
2 "given": {"foo": [{"name": "a"}, {"name": "b"}]},
3 "cases": [
4 {
5 "comment": "Matching a literal",
6 "expression": "foo[?name == 'a']",
7 "result": [{"name": "a"}]
8 }
9 ]
10 },
11 {
12 "given": {"foo": [0, 1], "bar": [2, 3]},
13 "cases": [
14 {
15 "comment": "Matching a literal",
16 "expression": "*[?[0] == `0`]",
17 "result": [[], []]
18 }
19 ]
20 },
21 {
22 "given": {"foo": [{"first": "foo", "last": "bar"},
23 {"first": "foo", "last": "foo"},
24 {"first": "foo", "last": "baz"}]},
25 "cases": [
26 {
27 "comment": "Matching an expression",
28 "expression": "foo[?first == last]",
29 "result": [{"first": "foo", "last": "foo"}]
30 },
31 {
32 "comment": "Verify projection created from filter",
33 "expression": "foo[?first == last].first",
34 "result": ["foo"]
35 }
36 ]
37 },
38 {
39 "given": {"foo": [{"age": 20},
40 {"age": 25},
41 {"age": 30}]},
42 "cases": [
43 {
44 "comment": "Greater than with a number",
45 "expression": "foo[?age > `25`]",
46 "result": [{"age": 30}]
47 },
48 {
49 "expression": "foo[?age >= `25`]",
50 "result": [{"age": 25}, {"age": 30}]
51 },
52 {
53 "comment": "Greater than with a number",
54 "expression": "foo[?age > `30`]",
55 "result": []
56 },
57 {
58 "comment": "Greater than with a number",
59 "expression": "foo[?age < `25`]",
60 "result": [{"age": 20}]
61 },
62 {
63 "comment": "Greater than with a number",
64 "expression": "foo[?age <= `25`]",
65 "result": [{"age": 20}, {"age": 25}]
66 },
67 {
68 "comment": "Greater than with a number",
69 "expression": "foo[?age < `20`]",
70 "result": []
71 },
72 {
73 "expression": "foo[?age == `20`]",
74 "result": [{"age": 20}]
75 },
76 {
77 "expression": "foo[?age != `20`]",
78 "result": [{"age": 25}, {"age": 30}]
79 }
80 ]
81 },
82 {
83 "given": {"foo": [{"top": {"name": "a"}},
84 {"top": {"name": "b"}}]},
85 "cases": [
86 {
87 "comment": "Filter with subexpression",
88 "expression": "foo[?top.name == 'a']",
89 "result": [{"top": {"name": "a"}}]
90 }
91 ]
92 },
93 {
94 "given": {"foo": [{"top": {"first": "foo", "last": "bar"}},
95 {"top": {"first": "foo", "last": "foo"}},
96 {"top": {"first": "foo", "last": "baz"}}]},
97 "cases": [
98 {
99 "comment": "Matching an expression",
100 "expression": "foo[?top.first == top.last]",
101 "result": [{"top": {"first": "foo", "last": "foo"}}]
102 },
103 {
104 "comment": "Matching a JSON array",
105 "expression": "foo[?top == `{\"first\": \"foo\", \"last\": \"bar\"}`]",
106 "result": [{"top": {"first": "foo", "last": "bar"}}]
107 }
108 ]
109 },
110 {
111 "given": {"foo": [
112 {"key": true},
113 {"key": false},
114 {"key": 0},
115 {"key": 1},
116 {"key": [0]},
117 {"key": {"bar": [0]}},
118 {"key": null},
119 {"key": [1]},
120 {"key": {"a":2}}
121 ]},
122 "cases": [
123 {
124 "expression": "foo[?key == `true`]",
125 "result": [{"key": true}]
126 },
127 {
128 "expression": "foo[?key == `false`]",
129 "result": [{"key": false}]
130 },
131 {
132 "expression": "foo[?key == `0`]",
133 "result": [{"key": 0}]
134 },
135 {
136 "expression": "foo[?key == `1`]",
137 "result": [{"key": 1}]
138 },
139 {
140 "expression": "foo[?key == `[0]`]",
141 "result": [{"key": [0]}]
142 },
143 {
144 "expression": "foo[?key == `{\"bar\": [0]}`]",
145 "result": [{"key": {"bar": [0]}}]
146 },
147 {
148 "expression": "foo[?key == `null`]",
149 "result": [{"key": null}]
150 },
151 {
152 "expression": "foo[?key == `[1]`]",
153 "result": [{"key": [1]}]
154 },
155 {
156 "expression": "foo[?key == `{\"a\":2}`]",
157 "result": [{"key": {"a":2}}]
158 },
159 {
160 "expression": "foo[?`true` == key]",
161 "result": [{"key": true}]
162 },
163 {
164 "expression": "foo[?`false` == key]",
165 "result": [{"key": false}]
166 },
167 {
168 "expression": "foo[?`0` == key]",
169 "result": [{"key": 0}]
170 },
171 {
172 "expression": "foo[?`1` == key]",
173 "result": [{"key": 1}]
174 },
175 {
176 "expression": "foo[?`[0]` == key]",
177 "result": [{"key": [0]}]
178 },
179 {
180 "expression": "foo[?`{\"bar\": [0]}` == key]",
181 "result": [{"key": {"bar": [0]}}]
182 },
183 {
184 "expression": "foo[?`null` == key]",
185 "result": [{"key": null}]
186 },
187 {
188 "expression": "foo[?`[1]` == key]",
189 "result": [{"key": [1]}]
190 },
191 {
192 "expression": "foo[?`{\"a\":2}` == key]",
193 "result": [{"key": {"a":2}}]
194 },
195 {
196 "expression": "foo[?key != `true`]",
197 "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
198 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
199 },
200 {
201 "expression": "foo[?key != `false`]",
202 "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]},
203 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
204 },
205 {
206 "expression": "foo[?key != `0`]",
207 "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]},
208 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
209 },
210 {
211 "expression": "foo[?key != `1`]",
212 "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]},
213 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
214 },
215 {
216 "expression": "foo[?key != `null`]",
217 "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
218 {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}]
219 },
220 {
221 "expression": "foo[?key != `[1]`]",
222 "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
223 {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}]
224 },
225 {
226 "expression": "foo[?key != `{\"a\":2}`]",
227 "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
228 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}]
229 },
230 {
231 "expression": "foo[?`true` != key]",
232 "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
233 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
234 },
235 {
236 "expression": "foo[?`false` != key]",
237 "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]},
238 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
239 },
240 {
241 "expression": "foo[?`0` != key]",
242 "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]},
243 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
244 },
245 {
246 "expression": "foo[?`1` != key]",
247 "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]},
248 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
249 },
250 {
251 "expression": "foo[?`null` != key]",
252 "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
253 {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}]
254 },
255 {
256 "expression": "foo[?`[1]` != key]",
257 "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
258 {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}]
259 },
260 {
261 "expression": "foo[?`{\"a\":2}` != key]",
262 "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
263 {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}]
264 }
265 ]
266 },
267 {
268 "given": {"reservations": [
269 {"instances": [
270 {"foo": 1, "bar": 2}, {"foo": 1, "bar": 3},
271 {"foo": 1, "bar": 2}, {"foo": 2, "bar": 1}]}]},
272 "cases": [
273 {
274 "expression": "reservations[].instances[?bar==`1`]",
275 "result": [[{"foo": 2, "bar": 1}]]
276 },
277 {
278 "expression": "reservations[*].instances[?bar==`1`]",
279 "result": [[{"foo": 2, "bar": 1}]]
280 },
281 {
282 "expression": "reservations[].instances[?bar==`1`][]",
283 "result": [{"foo": 2, "bar": 1}]
284 }
285 ]
286 },
287 {
288 "given": {
289 "baz": "other",
290 "foo": [
291 {"bar": 1}, {"bar": 2}, {"bar": 3}, {"bar": 4}, {"bar": 1, "baz": 2}
292 ]
293 },
294 "cases": [
295 {
296 "expression": "foo[?bar==`1`].bar[0]",
297 "result": []
298 }
299 ]
300 },
301 {
302 "given": {
303 "foo": [
304 {"a": 1, "b": {"c": "x"}},
305 {"a": 1, "b": {"c": "y"}},
306 {"a": 1, "b": {"c": "z"}},
307 {"a": 2, "b": {"c": "z"}},
308 {"a": 1, "baz": 2}
309 ]
310 },
311 "cases": [
312 {
313 "expression": "foo[?a==`1`].b.c",
314 "result": ["x", "y", "z"]
315 }
316 ]
317 },
318 {
319 "given": {"foo": [{"name": "a"}, {"name": "b"}, {"name": "c"}]},
320 "cases": [
321 {
322 "comment": "Filter with or expression",
323 "expression": "foo[?name == 'a' || name == 'b']",
324 "result": [{"name": "a"}, {"name": "b"}]
325 },
326 {
327 "expression": "foo[?name == 'a' || name == 'e']",
328 "result": [{"name": "a"}]
329 },
330 {
331 "expression": "foo[?name == 'a' || name == 'b' || name == 'c']",
332 "result": [{"name": "a"}, {"name": "b"}, {"name": "c"}]
333 }
334 ]
335 },
336 {
337 "given": {"foo": [{"a": 1, "b": 2}, {"a": 1, "b": 3}]},
338 "cases": [
339 {
340 "comment": "Filter with and expression",
341 "expression": "foo[?a == `1` && b == `2`]",
342 "result": [{"a": 1, "b": 2}]
343 },
344 {
345 "expression": "foo[?a == `1` && b == `4`]",
346 "result": []
347 }
348 ]
349 },
350 {
351 "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]},
352 "cases": [
353 {
354 "comment": "Filter with Or and And expressions",
355 "expression": "foo[?c == `3` || a == `1` && b == `4`]",
356 "result": [{"a": 1, "b": 2, "c": 3}]
357 },
358 {
359 "expression": "foo[?b == `2` || a == `3` && b == `4`]",
360 "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
361 },
362 {
363 "expression": "foo[?a == `3` && b == `4` || b == `2`]",
364 "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
365 },
366 {
367 "expression": "foo[?(a == `3` && b == `4`) || b == `2`]",
368 "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
369 },
370 {
371 "expression": "foo[?((a == `3` && b == `4`)) || b == `2`]",
372 "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
373 },
374 {
375 "expression": "foo[?a == `3` && (b == `4` || b == `2`)]",
376 "result": [{"a": 3, "b": 4}]
377 },
378 {
379 "expression": "foo[?a == `3` && ((b == `4` || b == `2`))]",
380 "result": [{"a": 3, "b": 4}]
381 }
382 ]
383 },
384 {
385 "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]},
386 "cases": [
387 {
388 "comment": "Verify precedence of or/and expressions",
389 "expression": "foo[?a == `1` || b ==`2` && c == `5`]",
390 "result": [{"a": 1, "b": 2, "c": 3}]
391 },
392 {
393 "comment": "Parentheses can alter precedence",
394 "expression": "foo[?(a == `1` || b ==`2`) && c == `5`]",
395 "result": []
396 },
397 {
398 "comment": "Not expressions combined with and/or",
399 "expression": "foo[?!(a == `1` || b ==`2`)]",
400 "result": [{"a": 3, "b": 4}]
401 }
402 ]
403 },
404 {
405 "given": {
406 "foo": [
407 {"key": true},
408 {"key": false},
409 {"key": []},
410 {"key": {}},
411 {"key": [0]},
412 {"key": {"a": "b"}},
413 {"key": 0},
414 {"key": 1},
415 {"key": null},
416 {"notkey": true}
417 ]
418 },
419 "cases": [
420 {
421 "comment": "Unary filter expression",
422 "expression": "foo[?key]",
423 "result": [
424 {"key": true}, {"key": [0]}, {"key": {"a": "b"}},
425 {"key": 0}, {"key": 1}
426 ]
427 },
428 {
429 "comment": "Unary not filter expression",
430 "expression": "foo[?!key]",
431 "result": [
432 {"key": false}, {"key": []}, {"key": {}},
433 {"key": null}, {"notkey": true}
434 ]
435 },
436 {
437 "comment": "Equality with null RHS",
438 "expression": "foo[?key == `null`]",
439 "result": [
440 {"key": null}, {"notkey": true}
441 ]
442 }
443 ]
444 },
445 {
446 "given": {
447 "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
448 },
449 "cases": [
450 {
451 "comment": "Using @ in a filter expression",
452 "expression": "foo[?@ < `5`]",
453 "result": [0, 1, 2, 3, 4]
454 },
455 {
456 "comment": "Using @ in a filter expression",
457 "expression": "foo[?`5` > @]",
458 "result": [0, 1, 2, 3, 4]
459 },
460 {
461 "comment": "Using @ in a filter expression",
462 "expression": "foo[?@ == @]",
463 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
464 }
465 ]
466 }
467 ]
0 [{
1 "given":
2 {
3 "foo": -1,
4 "zero": 0,
5 "numbers": [-1, 3, 4, 5],
6 "array": [-1, 3, 4, 5, "a", "100"],
7 "strings": ["a", "b", "c"],
8 "decimals": [1.01, 1.2, -1.5],
9 "str": "Str",
10 "false": false,
11 "empty_list": [],
12 "empty_hash": {},
13 "objects": {"foo": "bar", "bar": "baz"},
14 "null_key": null
15 },
16 "cases": [
17 {
18 "expression": "abs(foo)",
19 "result": 1
20 },
21 {
22 "expression": "abs(foo)",
23 "result": 1
24 },
25 {
26 "expression": "abs(str)",
27 "error": "invalid-type"
28 },
29 {
30 "expression": "abs(array[1])",
31 "result": 3
32 },
33 {
34 "expression": "abs(array[1])",
35 "result": 3
36 },
37 {
38 "expression": "abs(`false`)",
39 "error": "invalid-type"
40 },
41 {
42 "expression": "abs(`-24`)",
43 "result": 24
44 },
45 {
46 "expression": "abs(`-24`)",
47 "result": 24
48 },
49 {
50 "expression": "abs(`1`, `2`)",
51 "error": "invalid-arity"
52 },
53 {
54 "expression": "abs()",
55 "error": "invalid-arity"
56 },
57 {
58 "expression": "unknown_function(`1`, `2`)",
59 "error": "unknown-function"
60 },
61 {
62 "expression": "avg(numbers)",
63 "result": 2.75
64 },
65 {
66 "expression": "avg(array)",
67 "error": "invalid-type"
68 },
69 {
70 "expression": "avg('abc')",
71 "error": "invalid-type"
72 },
73 {
74 "expression": "avg(foo)",
75 "error": "invalid-type"
76 },
77 {
78 "expression": "avg(@)",
79 "error": "invalid-type"
80 },
81 {
82 "expression": "avg(strings)",
83 "error": "invalid-type"
84 },
85 {
86 "expression": "ceil(`1.2`)",
87 "result": 2
88 },
89 {
90 "expression": "ceil(decimals[0])",
91 "result": 2
92 },
93 {
94 "expression": "ceil(decimals[1])",
95 "result": 2
96 },
97 {
98 "expression": "ceil(decimals[2])",
99 "result": -1
100 },
101 {
102 "expression": "ceil('string')",
103 "error": "invalid-type"
104 },
105 {
106 "expression": "contains('abc', 'a')",
107 "result": true
108 },
109 {
110 "expression": "contains('abc', 'd')",
111 "result": false
112 },
113 {
114 "expression": "contains(`false`, 'd')",
115 "error": "invalid-type"
116 },
117 {
118 "expression": "contains(strings, 'a')",
119 "result": true
120 },
121 {
122 "expression": "contains(decimals, `1.2`)",
123 "result": true
124 },
125 {
126 "expression": "contains(decimals, `false`)",
127 "result": false
128 },
129 {
130 "expression": "ends_with(str, 'r')",
131 "result": true
132 },
133 {
134 "expression": "ends_with(str, 'tr')",
135 "result": true
136 },
137 {
138 "expression": "ends_with(str, 'Str')",
139 "result": true
140 },
141 {
142 "expression": "ends_with(str, 'SStr')",
143 "result": false
144 },
145 {
146 "expression": "ends_with(str, 'foo')",
147 "result": false
148 },
149 {
150 "expression": "ends_with(str, `0`)",
151 "error": "invalid-type"
152 },
153 {
154 "expression": "floor(`1.2`)",
155 "result": 1
156 },
157 {
158 "expression": "floor('string')",
159 "error": "invalid-type"
160 },
161 {
162 "expression": "floor(decimals[0])",
163 "result": 1
164 },
165 {
166 "expression": "floor(foo)",
167 "result": -1
168 },
169 {
170 "expression": "floor(str)",
171 "error": "invalid-type"
172 },
173 {
174 "expression": "length('abc')",
175 "result": 3
176 },
177 {
178 "expression": "length('✓foo')",
179 "result": 4
180 },
181 {
182 "expression": "length('')",
183 "result": 0
184 },
185 {
186 "expression": "length(@)",
187 "result": 12
188 },
189 {
190 "expression": "length(strings[0])",
191 "result": 1
192 },
193 {
194 "expression": "length(str)",
195 "result": 3
196 },
197 {
198 "expression": "length(array)",
199 "result": 6
200 },
201 {
202 "expression": "length(objects)",
203 "result": 2
204 },
205 {
206 "expression": "length(`false`)",
207 "error": "invalid-type"
208 },
209 {
210 "expression": "length(foo)",
211 "error": "invalid-type"
212 },
213 {
214 "expression": "length(strings[0])",
215 "result": 1
216 },
217 {
218 "expression": "max(numbers)",
219 "result": 5
220 },
221 {
222 "expression": "max(decimals)",
223 "result": 1.2
224 },
225 {
226 "expression": "max(strings)",
227 "result": "c"
228 },
229 {
230 "expression": "max(abc)",
231 "error": "invalid-type"
232 },
233 {
234 "expression": "max(array)",
235 "error": "invalid-type"
236 },
237 {
238 "expression": "max(decimals)",
239 "result": 1.2
240 },
241 {
242 "expression": "max(empty_list)",
243 "result": null
244 },
245 {
246 "expression": "merge(`{}`)",
247 "result": {}
248 },
249 {
250 "expression": "merge(`{}`, `{}`)",
251 "result": {}
252 },
253 {
254 "expression": "merge(`{\"a\": 1}`, `{\"b\": 2}`)",
255 "result": {"a": 1, "b": 2}
256 },
257 {
258 "expression": "merge(`{\"a\": 1}`, `{\"a\": 2}`)",
259 "result": {"a": 2}
260 },
261 {
262 "expression": "merge(`{\"a\": 1, \"b\": 2}`, `{\"a\": 2, \"c\": 3}`, `{\"d\": 4}`)",
263 "result": {"a": 2, "b": 2, "c": 3, "d": 4}
264 },
265 {
266 "expression": "min(numbers)",
267 "result": -1
268 },
269 {
270 "expression": "min(decimals)",
271 "result": -1.5
272 },
273 {
274 "expression": "min(abc)",
275 "error": "invalid-type"
276 },
277 {
278 "expression": "min(array)",
279 "error": "invalid-type"
280 },
281 {
282 "expression": "min(empty_list)",
283 "result": null
284 },
285 {
286 "expression": "min(decimals)",
287 "result": -1.5
288 },
289 {
290 "expression": "min(strings)",
291 "result": "a"
292 },
293 {
294 "expression": "type('abc')",
295 "result": "string"
296 },
297 {
298 "expression": "type(`1.0`)",
299 "result": "number"
300 },
301 {
302 "expression": "type(`2`)",
303 "result": "number"
304 },
305 {
306 "expression": "type(`true`)",
307 "result": "boolean"
308 },
309 {
310 "expression": "type(`false`)",
311 "result": "boolean"
312 },
313 {
314 "expression": "type(`null`)",
315 "result": "null"
316 },
317 {
318 "expression": "type(`[0]`)",
319 "result": "array"
320 },
321 {
322 "expression": "type(`{\"a\": \"b\"}`)",
323 "result": "object"
324 },
325 {
326 "expression": "type(@)",
327 "result": "object"
328 },
329 {
330 "expression": "sort(keys(objects))",
331 "result": ["bar", "foo"]
332 },
333 {
334 "expression": "keys(foo)",
335 "error": "invalid-type"
336 },
337 {
338 "expression": "keys(strings)",
339 "error": "invalid-type"
340 },
341 {
342 "expression": "keys(`false`)",
343 "error": "invalid-type"
344 },
345 {
346 "expression": "sort(values(objects))",
347 "result": ["bar", "baz"]
348 },
349 {
350 "expression": "keys(empty_hash)",
351 "result": []
352 },
353 {
354 "expression": "values(foo)",
355 "error": "invalid-type"
356 },
357 {
358 "expression": "join(', ', strings)",
359 "result": "a, b, c"
360 },
361 {
362 "expression": "join(', ', strings)",
363 "result": "a, b, c"
364 },
365 {
366 "expression": "join(',', `[\"a\", \"b\"]`)",
367 "result": "a,b"
368 },
369 {
370 "expression": "join(',', `[\"a\", 0]`)",
371 "error": "invalid-type"
372 },
373 {
374 "expression": "join(', ', str)",
375 "error": "invalid-type"
376 },
377 {
378 "expression": "join('|', strings)",
379 "result": "a|b|c"
380 },
381 {
382 "expression": "join(`2`, strings)",
383 "error": "invalid-type"
384 },
385 {
386 "expression": "join('|', decimals)",
387 "error": "invalid-type"
388 },
389 {
390 "expression": "join('|', decimals[].to_string(@))",
391 "result": "1.01|1.2|-1.5"
392 },
393 {
394 "expression": "join('|', empty_list)",
395 "result": ""
396 },
397 {
398 "expression": "reverse(numbers)",
399 "result": [5, 4, 3, -1]
400 },
401 {
402 "expression": "reverse(array)",
403 "result": ["100", "a", 5, 4, 3, -1]
404 },
405 {
406 "expression": "reverse(`[]`)",
407 "result": []
408 },
409 {
410 "expression": "reverse('')",
411 "result": ""
412 },
413 {
414 "expression": "reverse('hello world')",
415 "result": "dlrow olleh"
416 },
417 {
418 "expression": "starts_with(str, 'S')",
419 "result": true
420 },
421 {
422 "expression": "starts_with(str, 'St')",
423 "result": true
424 },
425 {
426 "expression": "starts_with(str, 'Str')",
427 "result": true
428 },
429 {
430 "expression": "starts_with(str, 'String')",
431 "result": false
432 },
433 {
434 "expression": "starts_with(str, `0`)",
435 "error": "invalid-type"
436 },
437 {
438 "expression": "sum(numbers)",
439 "result": 11
440 },
441 {
442 "expression": "sum(decimals)",
443 "result": 0.71
444 },
445 {
446 "expression": "sum(array)",
447 "error": "invalid-type"
448 },
449 {
450 "expression": "sum(array[].to_number(@))",
451 "result": 111
452 },
453 {
454 "expression": "sum(`[]`)",
455 "result": 0
456 },
457 {
458 "expression": "to_array('foo')",
459 "result": ["foo"]
460 },
461 {
462 "expression": "to_array(`0`)",
463 "result": [0]
464 },
465 {
466 "expression": "to_array(objects)",
467 "result": [{"foo": "bar", "bar": "baz"}]
468 },
469 {
470 "expression": "to_array(`[1, 2, 3]`)",
471 "result": [1, 2, 3]
472 },
473 {
474 "expression": "to_array(false)",
475 "result": [false]
476 },
477 {
478 "expression": "to_string('foo')",
479 "result": "foo"
480 },
481 {
482 "expression": "to_string(`1.2`)",
483 "result": "1.2"
484 },
485 {
486 "expression": "to_string(`[0, 1]`)",
487 "result": "[0,1]"
488 },
489 {
490 "expression": "to_number('1.0')",
491 "result": 1.0
492 },
493 {
494 "expression": "to_number('1.1')",
495 "result": 1.1
496 },
497 {
498 "expression": "to_number('4')",
499 "result": 4
500 },
501 {
502 "expression": "to_number('notanumber')",
503 "result": null
504 },
505 {
506 "expression": "to_number(`false`)",
507 "result": null
508 },
509 {
510 "expression": "to_number(`null`)",
511 "result": null
512 },
513 {
514 "expression": "to_number(`[0]`)",
515 "result": null
516 },
517 {
518 "expression": "to_number(`{\"foo\": 0}`)",
519 "result": null
520 },
521 {
522 "expression": "\"to_string\"(`1.0`)",
523 "error": "syntax"
524 },
525 {
526 "expression": "sort(numbers)",
527 "result": [-1, 3, 4, 5]
528 },
529 {
530 "expression": "sort(strings)",
531 "result": ["a", "b", "c"]
532 },
533 {
534 "expression": "sort(decimals)",
535 "result": [-1.5, 1.01, 1.2]
536 },
537 {
538 "expression": "sort(array)",
539 "error": "invalid-type"
540 },
541 {
542 "expression": "sort(abc)",
543 "error": "invalid-type"
544 },
545 {
546 "expression": "sort(empty_list)",
547 "result": []
548 },
549 {
550 "expression": "sort(@)",
551 "error": "invalid-type"
552 },
553 {
554 "expression": "not_null(unknown_key, str)",
555 "result": "Str"
556 },
557 {
558 "expression": "not_null(unknown_key, foo.bar, empty_list, str)",
559 "result": []
560 },
561 {
562 "expression": "not_null(unknown_key, null_key, empty_list, str)",
563 "result": []
564 },
565 {
566 "expression": "not_null(all, expressions, are_null)",
567 "result": null
568 },
569 {
570 "expression": "not_null()",
571 "error": "invalid-arity"
572 },
573 {
574 "description": "function projection on single arg function",
575 "expression": "numbers[].to_string(@)",
576 "result": ["-1", "3", "4", "5"]
577 },
578 {
579 "description": "function projection on single arg function",
580 "expression": "array[].to_number(@)",
581 "result": [-1, 3, 4, 5, 100]
582 }
583 ]
584 }, {
585 "given":
586 {
587 "foo": [
588 {"b": "b", "a": "a"},
589 {"c": "c", "b": "b"},
590 {"d": "d", "c": "c"},
591 {"e": "e", "d": "d"},
592 {"f": "f", "e": "e"}
593 ]
594 },
595 "cases": [
596 {
597 "description": "function projection on variadic function",
598 "expression": "foo[].not_null(f, e, d, c, b, a)",
599 "result": ["b", "c", "d", "e", "f"]
600 }
601 ]
602 }, {
603 "given":
604 {
605 "people": [
606 {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
607 {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
608 {"age": 30, "age_str": "30", "bool": true, "name": "c"},
609 {"age": 50, "age_str": "50", "bool": false, "name": "d"},
610 {"age": 10, "age_str": "10", "bool": true, "name": 3}
611 ]
612 },
613 "cases": [
614 {
615 "description": "sort by field expression",
616 "expression": "sort_by(people, &age)",
617 "result": [
618 {"age": 10, "age_str": "10", "bool": true, "name": 3},
619 {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
620 {"age": 30, "age_str": "30", "bool": true, "name": "c"},
621 {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
622 {"age": 50, "age_str": "50", "bool": false, "name": "d"}
623 ]
624 },
625 {
626 "expression": "sort_by(people, &age_str)",
627 "result": [
628 {"age": 10, "age_str": "10", "bool": true, "name": 3},
629 {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
630 {"age": 30, "age_str": "30", "bool": true, "name": "c"},
631 {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
632 {"age": 50, "age_str": "50", "bool": false, "name": "d"}
633 ]
634 },
635 {
636 "description": "sort by function expression",
637 "expression": "sort_by(people, &to_number(age_str))",
638 "result": [
639 {"age": 10, "age_str": "10", "bool": true, "name": 3},
640 {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
641 {"age": 30, "age_str": "30", "bool": true, "name": "c"},
642 {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
643 {"age": 50, "age_str": "50", "bool": false, "name": "d"}
644 ]
645 },
646 {
647 "description": "function projection on sort_by function",
648 "expression": "sort_by(people, &age)[].name",
649 "result": [3, "a", "c", "b", "d"]
650 },
651 {
652 "expression": "sort_by(people, &extra)",
653 "error": "invalid-type"
654 },
655 {
656 "expression": "sort_by(people, &bool)",
657 "error": "invalid-type"
658 },
659 {
660 "expression": "sort_by(people, &name)",
661 "error": "invalid-type"
662 },
663 {
664 "expression": "sort_by(people, name)",
665 "error": "invalid-type"
666 },
667 {
668 "expression": "sort_by(people, &age)[].extra",
669 "result": ["foo", "bar"]
670 },
671 {
672 "expression": "sort_by(`[]`, &age)",
673 "result": []
674 },
675 {
676 "expression": "max_by(people, &age)",
677 "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
678 },
679 {
680 "expression": "max_by(people, &age_str)",
681 "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
682 },
683 {
684 "expression": "max_by(people, &bool)",
685 "error": "invalid-type"
686 },
687 {
688 "expression": "max_by(people, &extra)",
689 "error": "invalid-type"
690 },
691 {
692 "expression": "max_by(people, &to_number(age_str))",
693 "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
694 },
695 {
696 "expression": "min_by(people, &age)",
697 "result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
698 },
699 {
700 "expression": "min_by(people, &age_str)",
701 "result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
702 },
703 {
704 "expression": "min_by(people, &bool)",
705 "error": "invalid-type"
706 },
707 {
708 "expression": "min_by(people, &extra)",
709 "error": "invalid-type"
710 },
711 {
712 "expression": "min_by(people, &to_number(age_str))",
713 "result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
714 }
715 ]
716 }, {
717 "given":
718 {
719 "people": [
720 {"age": 10, "order": "1"},
721 {"age": 10, "order": "2"},
722 {"age": 10, "order": "3"},
723 {"age": 10, "order": "4"},
724 {"age": 10, "order": "5"},
725 {"age": 10, "order": "6"},
726 {"age": 10, "order": "7"},
727 {"age": 10, "order": "8"},
728 {"age": 10, "order": "9"},
729 {"age": 10, "order": "10"},
730 {"age": 10, "order": "11"}
731 ]
732 },
733 "cases": [
734 {
735 "description": "stable sort order",
736 "expression": "sort_by(people, &age)",
737 "result": [
738 {"age": 10, "order": "1"},
739 {"age": 10, "order": "2"},
740 {"age": 10, "order": "3"},
741 {"age": 10, "order": "4"},
742 {"age": 10, "order": "5"},
743 {"age": 10, "order": "6"},
744 {"age": 10, "order": "7"},
745 {"age": 10, "order": "8"},
746 {"age": 10, "order": "9"},
747 {"age": 10, "order": "10"},
748 {"age": 10, "order": "11"}
749 ]
750 }
751 ]
752 }, {
753 "given":
754 {
755 "people": [
756 {"a": 10, "b": 1, "c": "z"},
757 {"a": 10, "b": 2, "c": null},
758 {"a": 10, "b": 3},
759 {"a": 10, "b": 4, "c": "z"},
760 {"a": 10, "b": 5, "c": null},
761 {"a": 10, "b": 6},
762 {"a": 10, "b": 7, "c": "z"},
763 {"a": 10, "b": 8, "c": null},
764 {"a": 10, "b": 9}
765 ],
766 "empty": []
767 },
768 "cases": [
769 {
770 "expression": "map(&a, people)",
771 "result": [10, 10, 10, 10, 10, 10, 10, 10, 10]
772 },
773 {
774 "expression": "map(&c, people)",
775 "result": ["z", null, null, "z", null, null, "z", null, null]
776 },
777 {
778 "expression": "map(&a, badkey)",
779 "error": "invalid-type"
780 },
781 {
782 "expression": "map(&foo, empty)",
783 "result": []
784 }
785 ]
786 }, {
787 "given": {
788 "array": [
789 {
790 "foo": {"bar": "yes1"}
791 },
792 {
793 "foo": {"bar": "yes2"}
794 },
795 {
796 "foo1": {"bar": "no"}
797 }
798 ]},
799 "cases": [
800 {
801 "expression": "map(&foo.bar, array)",
802 "result": ["yes1", "yes2", null]
803 },
804 {
805 "expression": "map(&foo1.bar, array)",
806 "result": [null, null, "no"]
807 },
808 {
809 "expression": "map(&foo.bar.baz, array)",
810 "result": [null, null, null]
811 }
812 ]
813 }, {
814 "given": {
815 "array": [[1, 2, 3, [4]], [5, 6, 7, [8, 9]]]
816 },
817 "cases": [
818 {
819 "expression": "map(&[], array)",
820 "result": [[1, 2, 3, 4], [5, 6, 7, 8, 9]]
821 }
822 ]
823 }
824 ]
0 [
1 {
2 "given": {
3 "__L": true
4 },
5 "cases": [
6 {
7 "expression": "__L",
8 "result": true
9 }
10 ]
11 },
12 {
13 "given": {
14 "!\r": true
15 },
16 "cases": [
17 {
18 "expression": "\"!\\r\"",
19 "result": true
20 }
21 ]
22 },
23 {
24 "given": {
25 "Y_1623": true
26 },
27 "cases": [
28 {
29 "expression": "Y_1623",
30 "result": true
31 }
32 ]
33 },
34 {
35 "given": {
36 "x": true
37 },
38 "cases": [
39 {
40 "expression": "x",
41 "result": true
42 }
43 ]
44 },
45 {
46 "given": {
47 "\tF\uCebb": true
48 },
49 "cases": [
50 {
51 "expression": "\"\\tF\\uCebb\"",
52 "result": true
53 }
54 ]
55 },
56 {
57 "given": {
58 " \t": true
59 },
60 "cases": [
61 {
62 "expression": "\" \\t\"",
63 "result": true
64 }
65 ]
66 },
67 {
68 "given": {
69 " ": true
70 },
71 "cases": [
72 {
73 "expression": "\" \"",
74 "result": true
75 }
76 ]
77 },
78 {
79 "given": {
80 "v2": true
81 },
82 "cases": [
83 {
84 "expression": "v2",
85 "result": true
86 }
87 ]
88 },
89 {
90 "given": {
91 "\t": true
92 },
93 "cases": [
94 {
95 "expression": "\"\\t\"",
96 "result": true
97 }
98 ]
99 },
100 {
101 "given": {
102 "_X": true
103 },
104 "cases": [
105 {
106 "expression": "_X",
107 "result": true
108 }
109 ]
110 },
111 {
112 "given": {
113 "\t4\ud9da\udd15": true
114 },
115 "cases": [
116 {
117 "expression": "\"\\t4\\ud9da\\udd15\"",
118 "result": true
119 }
120 ]
121 },
122 {
123 "given": {
124 "v24_W": true
125 },
126 "cases": [
127 {
128 "expression": "v24_W",
129 "result": true
130 }
131 ]
132 },
133 {
134 "given": {
135 "H": true
136 },
137 "cases": [
138 {
139 "expression": "\"H\"",
140 "result": true
141 }
142 ]
143 },
144 {
145 "given": {
146 "\f": true
147 },
148 "cases": [
149 {
150 "expression": "\"\\f\"",
151 "result": true
152 }
153 ]
154 },
155 {
156 "given": {
157 "E4": true
158 },
159 "cases": [
160 {
161 "expression": "\"E4\"",
162 "result": true
163 }
164 ]
165 },
166 {
167 "given": {
168 "!": true
169 },
170 "cases": [
171 {
172 "expression": "\"!\"",
173 "result": true
174 }
175 ]
176 },
177 {
178 "given": {
179 "tM": true
180 },
181 "cases": [
182 {
183 "expression": "tM",
184 "result": true
185 }
186 ]
187 },
188 {
189 "given": {
190 " [": true
191 },
192 "cases": [
193 {
194 "expression": "\" [\"",
195 "result": true
196 }
197 ]
198 },
199 {
200 "given": {
201 "R!": true
202 },
203 "cases": [
204 {
205 "expression": "\"R!\"",
206 "result": true
207 }
208 ]
209 },
210 {
211 "given": {
212 "_6W": true
213 },
214 "cases": [
215 {
216 "expression": "_6W",
217 "result": true
218 }
219 ]
220 },
221 {
222 "given": {
223 "\uaBA1\r": true
224 },
225 "cases": [
226 {
227 "expression": "\"\\uaBA1\\r\"",
228 "result": true
229 }
230 ]
231 },
232 {
233 "given": {
234 "tL7": true
235 },
236 "cases": [
237 {
238 "expression": "tL7",
239 "result": true
240 }
241 ]
242 },
243 {
244 "given": {
245 "<<U\t": true
246 },
247 "cases": [
248 {
249 "expression": "\"<<U\\t\"",
250 "result": true
251 }
252 ]
253 },
254 {
255 "given": {
256 "\ubBcE\ufAfB": true
257 },
258 "cases": [
259 {
260 "expression": "\"\\ubBcE\\ufAfB\"",
261 "result": true
262 }
263 ]
264 },
265 {
266 "given": {
267 "sNA_": true
268 },
269 "cases": [
270 {
271 "expression": "sNA_",
272 "result": true
273 }
274 ]
275 },
276 {
277 "given": {
278 "9": true
279 },
280 "cases": [
281 {
282 "expression": "\"9\"",
283 "result": true
284 }
285 ]
286 },
287 {
288 "given": {
289 "\\\b\ud8cb\udc83": true
290 },
291 "cases": [
292 {
293 "expression": "\"\\\\\\b\\ud8cb\\udc83\"",
294 "result": true
295 }
296 ]
297 },
298 {
299 "given": {
300 "r": true
301 },
302 "cases": [
303 {
304 "expression": "\"r\"",
305 "result": true
306 }
307 ]
308 },
309 {
310 "given": {
311 "Q": true
312 },
313 "cases": [
314 {
315 "expression": "Q",
316 "result": true
317 }
318 ]
319 },
320 {
321 "given": {
322 "_Q__7GL8": true
323 },
324 "cases": [
325 {
326 "expression": "_Q__7GL8",
327 "result": true
328 }
329 ]
330 },
331 {
332 "given": {
333 "\\": true
334 },
335 "cases": [
336 {
337 "expression": "\"\\\\\"",
338 "result": true
339 }
340 ]
341 },
342 {
343 "given": {
344 "RR9_": true
345 },
346 "cases": [
347 {
348 "expression": "RR9_",
349 "result": true
350 }
351 ]
352 },
353 {
354 "given": {
355 "\r\f:": true
356 },
357 "cases": [
358 {
359 "expression": "\"\\r\\f:\"",
360 "result": true
361 }
362 ]
363 },
364 {
365 "given": {
366 "r7": true
367 },
368 "cases": [
369 {
370 "expression": "r7",
371 "result": true
372 }
373 ]
374 },
375 {
376 "given": {
377 "-": true
378 },
379 "cases": [
380 {
381 "expression": "\"-\"",
382 "result": true
383 }
384 ]
385 },
386 {
387 "given": {
388 "p9": true
389 },
390 "cases": [
391 {
392 "expression": "p9",
393 "result": true
394 }
395 ]
396 },
397 {
398 "given": {
399 "__": true
400 },
401 "cases": [
402 {
403 "expression": "__",
404 "result": true
405 }
406 ]
407 },
408 {
409 "given": {
410 "\b\t": true
411 },
412 "cases": [
413 {
414 "expression": "\"\\b\\t\"",
415 "result": true
416 }
417 ]
418 },
419 {
420 "given": {
421 "O_": true
422 },
423 "cases": [
424 {
425 "expression": "O_",
426 "result": true
427 }
428 ]
429 },
430 {
431 "given": {
432 "_r_8": true
433 },
434 "cases": [
435 {
436 "expression": "_r_8",
437 "result": true
438 }
439 ]
440 },
441 {
442 "given": {
443 "_j": true
444 },
445 "cases": [
446 {
447 "expression": "_j",
448 "result": true
449 }
450 ]
451 },
452 {
453 "given": {
454 ":": true
455 },
456 "cases": [
457 {
458 "expression": "\":\"",
459 "result": true
460 }
461 ]
462 },
463 {
464 "given": {
465 "\rB": true
466 },
467 "cases": [
468 {
469 "expression": "\"\\rB\"",
470 "result": true
471 }
472 ]
473 },
474 {
475 "given": {
476 "Obf": true
477 },
478 "cases": [
479 {
480 "expression": "Obf",
481 "result": true
482 }
483 ]
484 },
485 {
486 "given": {
487 "\n": true
488 },
489 "cases": [
490 {
491 "expression": "\"\\n\"",
492 "result": true
493 }
494 ]
495 },
496 {
497 "given": {
498 "\f\udb54\udf33": true
499 },
500 "cases": [
501 {
502 "expression": "\"\\f\udb54\udf33\"",
503 "result": true
504 }
505 ]
506 },
507 {
508 "given": {
509 "\\\u4FDc": true
510 },
511 "cases": [
512 {
513 "expression": "\"\\\\\\u4FDc\"",
514 "result": true
515 }
516 ]
517 },
518 {
519 "given": {
520 "\r": true
521 },
522 "cases": [
523 {
524 "expression": "\"\\r\"",
525 "result": true
526 }
527 ]
528 },
529 {
530 "given": {
531 "m_": true
532 },
533 "cases": [
534 {
535 "expression": "m_",
536 "result": true
537 }
538 ]
539 },
540 {
541 "given": {
542 "\r\fB ": true
543 },
544 "cases": [
545 {
546 "expression": "\"\\r\\fB \"",
547 "result": true
548 }
549 ]
550 },
551 {
552 "given": {
553 "+\"\"": true
554 },
555 "cases": [
556 {
557 "expression": "\"+\\\"\\\"\"",
558 "result": true
559 }
560 ]
561 },
562 {
563 "given": {
564 "Mg": true
565 },
566 "cases": [
567 {
568 "expression": "Mg",
569 "result": true
570 }
571 ]
572 },
573 {
574 "given": {
575 "\"!\/": true
576 },
577 "cases": [
578 {
579 "expression": "\"\\\"!\\/\"",
580 "result": true
581 }
582 ]
583 },
584 {
585 "given": {
586 "7\"": true
587 },
588 "cases": [
589 {
590 "expression": "\"7\\\"\"",
591 "result": true
592 }
593 ]
594 },
595 {
596 "given": {
597 "\\\udb3a\udca4S": true
598 },
599 "cases": [
600 {
601 "expression": "\"\\\\\udb3a\udca4S\"",
602 "result": true
603 }
604 ]
605 },
606 {
607 "given": {
608 "\"": true
609 },
610 "cases": [
611 {
612 "expression": "\"\\\"\"",
613 "result": true
614 }
615 ]
616 },
617 {
618 "given": {
619 "Kl": true
620 },
621 "cases": [
622 {
623 "expression": "Kl",
624 "result": true
625 }
626 ]
627 },
628 {
629 "given": {
630 "\b\b": true
631 },
632 "cases": [
633 {
634 "expression": "\"\\b\\b\"",
635 "result": true
636 }
637 ]
638 },
639 {
640 "given": {
641 ">": true
642 },
643 "cases": [
644 {
645 "expression": "\">\"",
646 "result": true
647 }
648 ]
649 },
650 {
651 "given": {
652 "hvu": true
653 },
654 "cases": [
655 {
656 "expression": "hvu",
657 "result": true
658 }
659 ]
660 },
661 {
662 "given": {
663 "; !": true
664 },
665 "cases": [
666 {
667 "expression": "\"; !\"",
668 "result": true
669 }
670 ]
671 },
672 {
673 "given": {
674 "hU": true
675 },
676 "cases": [
677 {
678 "expression": "hU",
679 "result": true
680 }
681 ]
682 },
683 {
684 "given": {
685 "!I\n\/": true
686 },
687 "cases": [
688 {
689 "expression": "\"!I\\n\\/\"",
690 "result": true
691 }
692 ]
693 },
694 {
695 "given": {
696 "\uEEbF": true
697 },
698 "cases": [
699 {
700 "expression": "\"\\uEEbF\"",
701 "result": true
702 }
703 ]
704 },
705 {
706 "given": {
707 "U)\t": true
708 },
709 "cases": [
710 {
711 "expression": "\"U)\\t\"",
712 "result": true
713 }
714 ]
715 },
716 {
717 "given": {
718 "fa0_9": true
719 },
720 "cases": [
721 {
722 "expression": "fa0_9",
723 "result": true
724 }
725 ]
726 },
727 {
728 "given": {
729 "/": true
730 },
731 "cases": [
732 {
733 "expression": "\"/\"",
734 "result": true
735 }
736 ]
737 },
738 {
739 "given": {
740 "Gy": true
741 },
742 "cases": [
743 {
744 "expression": "Gy",
745 "result": true
746 }
747 ]
748 },
749 {
750 "given": {
751 "\b": true
752 },
753 "cases": [
754 {
755 "expression": "\"\\b\"",
756 "result": true
757 }
758 ]
759 },
760 {
761 "given": {
762 "<": true
763 },
764 "cases": [
765 {
766 "expression": "\"<\"",
767 "result": true
768 }
769 ]
770 },
771 {
772 "given": {
773 "\t": true
774 },
775 "cases": [
776 {
777 "expression": "\"\\t\"",
778 "result": true
779 }
780 ]
781 },
782 {
783 "given": {
784 "\t&\\\r": true
785 },
786 "cases": [
787 {
788 "expression": "\"\\t&\\\\\\r\"",
789 "result": true
790 }
791 ]
792 },
793 {
794 "given": {
795 "#": true
796 },
797 "cases": [
798 {
799 "expression": "\"#\"",
800 "result": true
801 }
802 ]
803 },
804 {
805 "given": {
806 "B__": true
807 },
808 "cases": [
809 {
810 "expression": "B__",
811 "result": true
812 }
813 ]
814 },
815 {
816 "given": {
817 "\nS \n": true
818 },
819 "cases": [
820 {
821 "expression": "\"\\nS \\n\"",
822 "result": true
823 }
824 ]
825 },
826 {
827 "given": {
828 "Bp": true
829 },
830 "cases": [
831 {
832 "expression": "Bp",
833 "result": true
834 }
835 ]
836 },
837 {
838 "given": {
839 ",\t;": true
840 },
841 "cases": [
842 {
843 "expression": "\",\\t;\"",
844 "result": true
845 }
846 ]
847 },
848 {
849 "given": {
850 "B_q": true
851 },
852 "cases": [
853 {
854 "expression": "B_q",
855 "result": true
856 }
857 ]
858 },
859 {
860 "given": {
861 "\/+\t\n\b!Z": true
862 },
863 "cases": [
864 {
865 "expression": "\"\\/+\\t\\n\\b!Z\"",
866 "result": true
867 }
868 ]
869 },
870 {
871 "given": {
872 "\udadd\udfc7\\ueFAc": true
873 },
874 "cases": [
875 {
876 "expression": "\"\udadd\udfc7\\\\ueFAc\"",
877 "result": true
878 }
879 ]
880 },
881 {
882 "given": {
883 ":\f": true
884 },
885 "cases": [
886 {
887 "expression": "\":\\f\"",
888 "result": true
889 }
890 ]
891 },
892 {
893 "given": {
894 "\/": true
895 },
896 "cases": [
897 {
898 "expression": "\"\\/\"",
899 "result": true
900 }
901 ]
902 },
903 {
904 "given": {
905 "_BW_6Hg_Gl": true
906 },
907 "cases": [
908 {
909 "expression": "_BW_6Hg_Gl",
910 "result": true
911 }
912 ]
913 },
914 {
915 "given": {
916 "\udbcf\udc02": true
917 },
918 "cases": [
919 {
920 "expression": "\"\udbcf\udc02\"",
921 "result": true
922 }
923 ]
924 },
925 {
926 "given": {
927 "zs1DC": true
928 },
929 "cases": [
930 {
931 "expression": "zs1DC",
932 "result": true
933 }
934 ]
935 },
936 {
937 "given": {
938 "__434": true
939 },
940 "cases": [
941 {
942 "expression": "__434",
943 "result": true
944 }
945 ]
946 },
947 {
948 "given": {
949 "\udb94\udd41": true
950 },
951 "cases": [
952 {
953 "expression": "\"\udb94\udd41\"",
954 "result": true
955 }
956 ]
957 },
958 {
959 "given": {
960 "Z_5": true
961 },
962 "cases": [
963 {
964 "expression": "Z_5",
965 "result": true
966 }
967 ]
968 },
969 {
970 "given": {
971 "z_M_": true
972 },
973 "cases": [
974 {
975 "expression": "z_M_",
976 "result": true
977 }
978 ]
979 },
980 {
981 "given": {
982 "YU_2": true
983 },
984 "cases": [
985 {
986 "expression": "YU_2",
987 "result": true
988 }
989 ]
990 },
991 {
992 "given": {
993 "_0": true
994 },
995 "cases": [
996 {
997 "expression": "_0",
998 "result": true
999 }
1000 ]
1001 },
1002 {
1003 "given": {
1004 "\b+": true
1005 },
1006 "cases": [
1007 {
1008 "expression": "\"\\b+\"",
1009 "result": true
1010 }
1011 ]
1012 },
1013 {
1014 "given": {
1015 "\"": true
1016 },
1017 "cases": [
1018 {
1019 "expression": "\"\\\"\"",
1020 "result": true
1021 }
1022 ]
1023 },
1024 {
1025 "given": {
1026 "D7": true
1027 },
1028 "cases": [
1029 {
1030 "expression": "D7",
1031 "result": true
1032 }
1033 ]
1034 },
1035 {
1036 "given": {
1037 "_62L": true
1038 },
1039 "cases": [
1040 {
1041 "expression": "_62L",
1042 "result": true
1043 }
1044 ]
1045 },
1046 {
1047 "given": {
1048 "\tK\t": true
1049 },
1050 "cases": [
1051 {
1052 "expression": "\"\\tK\\t\"",
1053 "result": true
1054 }
1055 ]
1056 },
1057 {
1058 "given": {
1059 "\n\\\f": true
1060 },
1061 "cases": [
1062 {
1063 "expression": "\"\\n\\\\\\f\"",
1064 "result": true
1065 }
1066 ]
1067 },
1068 {
1069 "given": {
1070 "I_": true
1071 },
1072 "cases": [
1073 {
1074 "expression": "I_",
1075 "result": true
1076 }
1077 ]
1078 },
1079 {
1080 "given": {
1081 "W_a0_": true
1082 },
1083 "cases": [
1084 {
1085 "expression": "W_a0_",
1086 "result": true
1087 }
1088 ]
1089 },
1090 {
1091 "given": {
1092 "BQ": true
1093 },
1094 "cases": [
1095 {
1096 "expression": "BQ",
1097 "result": true
1098 }
1099 ]
1100 },
1101 {
1102 "given": {
1103 "\tX$\uABBb": true
1104 },
1105 "cases": [
1106 {
1107 "expression": "\"\\tX$\\uABBb\"",
1108 "result": true
1109 }
1110 ]
1111 },
1112 {
1113 "given": {
1114 "Z9": true
1115 },
1116 "cases": [
1117 {
1118 "expression": "Z9",
1119 "result": true
1120 }
1121 ]
1122 },
1123 {
1124 "given": {
1125 "\b%\"\uda38\udd0f": true
1126 },
1127 "cases": [
1128 {
1129 "expression": "\"\\b%\\\"\uda38\udd0f\"",
1130 "result": true
1131 }
1132 ]
1133 },
1134 {
1135 "given": {
1136 "_F": true
1137 },
1138 "cases": [
1139 {
1140 "expression": "_F",
1141 "result": true
1142 }
1143 ]
1144 },
1145 {
1146 "given": {
1147 "!,": true
1148 },
1149 "cases": [
1150 {
1151 "expression": "\"!,\"",
1152 "result": true
1153 }
1154 ]
1155 },
1156 {
1157 "given": {
1158 "\"!": true
1159 },
1160 "cases": [
1161 {
1162 "expression": "\"\\\"!\"",
1163 "result": true
1164 }
1165 ]
1166 },
1167 {
1168 "given": {
1169 "Hh": true
1170 },
1171 "cases": [
1172 {
1173 "expression": "Hh",
1174 "result": true
1175 }
1176 ]
1177 },
1178 {
1179 "given": {
1180 "&": true
1181 },
1182 "cases": [
1183 {
1184 "expression": "\"&\"",
1185 "result": true
1186 }
1187 ]
1188 },
1189 {
1190 "given": {
1191 "9\r\\R": true
1192 },
1193 "cases": [
1194 {
1195 "expression": "\"9\\r\\\\R\"",
1196 "result": true
1197 }
1198 ]
1199 },
1200 {
1201 "given": {
1202 "M_k": true
1203 },
1204 "cases": [
1205 {
1206 "expression": "M_k",
1207 "result": true
1208 }
1209 ]
1210 },
1211 {
1212 "given": {
1213 "!\b\n\udb06\ude52\"\"": true
1214 },
1215 "cases": [
1216 {
1217 "expression": "\"!\\b\\n\udb06\ude52\\\"\\\"\"",
1218 "result": true
1219 }
1220 ]
1221 },
1222 {
1223 "given": {
1224 "6": true
1225 },
1226 "cases": [
1227 {
1228 "expression": "\"6\"",
1229 "result": true
1230 }
1231 ]
1232 },
1233 {
1234 "given": {
1235 "_7": true
1236 },
1237 "cases": [
1238 {
1239 "expression": "_7",
1240 "result": true
1241 }
1242 ]
1243 },
1244 {
1245 "given": {
1246 "0": true
1247 },
1248 "cases": [
1249 {
1250 "expression": "\"0\"",
1251 "result": true
1252 }
1253 ]
1254 },
1255 {
1256 "given": {
1257 "\\8\\": true
1258 },
1259 "cases": [
1260 {
1261 "expression": "\"\\\\8\\\\\"",
1262 "result": true
1263 }
1264 ]
1265 },
1266 {
1267 "given": {
1268 "b7eo": true
1269 },
1270 "cases": [
1271 {
1272 "expression": "b7eo",
1273 "result": true
1274 }
1275 ]
1276 },
1277 {
1278 "given": {
1279 "xIUo9": true
1280 },
1281 "cases": [
1282 {
1283 "expression": "xIUo9",
1284 "result": true
1285 }
1286 ]
1287 },
1288 {
1289 "given": {
1290 "5": true
1291 },
1292 "cases": [
1293 {
1294 "expression": "\"5\"",
1295 "result": true
1296 }
1297 ]
1298 },
1299 {
1300 "given": {
1301 "?": true
1302 },
1303 "cases": [
1304 {
1305 "expression": "\"?\"",
1306 "result": true
1307 }
1308 ]
1309 },
1310 {
1311 "given": {
1312 "sU": true
1313 },
1314 "cases": [
1315 {
1316 "expression": "sU",
1317 "result": true
1318 }
1319 ]
1320 },
1321 {
1322 "given": {
1323 "VH2&H\\\/": true
1324 },
1325 "cases": [
1326 {
1327 "expression": "\"VH2&H\\\\\\/\"",
1328 "result": true
1329 }
1330 ]
1331 },
1332 {
1333 "given": {
1334 "_C": true
1335 },
1336 "cases": [
1337 {
1338 "expression": "_C",
1339 "result": true
1340 }
1341 ]
1342 },
1343 {
1344 "given": {
1345 "_": true
1346 },
1347 "cases": [
1348 {
1349 "expression": "_",
1350 "result": true
1351 }
1352 ]
1353 },
1354 {
1355 "given": {
1356 "<\t": true
1357 },
1358 "cases": [
1359 {
1360 "expression": "\"<\\t\"",
1361 "result": true
1362 }
1363 ]
1364 },
1365 {
1366 "given": {
1367 "\uD834\uDD1E": true
1368 },
1369 "cases": [
1370 {
1371 "expression": "\"\\uD834\\uDD1E\"",
1372 "result": true
1373 }
1374 ]
1375 }
1376 ]
0 [{
1 "given":
2 {"foo": {"bar": ["zero", "one", "two"]}},
3 "cases": [
4 {
5 "expression": "foo.bar[0]",
6 "result": "zero"
7 },
8 {
9 "expression": "foo.bar[1]",
10 "result": "one"
11 },
12 {
13 "expression": "foo.bar[2]",
14 "result": "two"
15 },
16 {
17 "expression": "foo.bar[3]",
18 "result": null
19 },
20 {
21 "expression": "foo.bar[-1]",
22 "result": "two"
23 },
24 {
25 "expression": "foo.bar[-2]",
26 "result": "one"
27 },
28 {
29 "expression": "foo.bar[-3]",
30 "result": "zero"
31 },
32 {
33 "expression": "foo.bar[-4]",
34 "result": null
35 }
36 ]
37 },
38 {
39 "given":
40 {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]},
41 "cases": [
42 {
43 "expression": "foo.bar",
44 "result": null
45 },
46 {
47 "expression": "foo[0].bar",
48 "result": "one"
49 },
50 {
51 "expression": "foo[1].bar",
52 "result": "two"
53 },
54 {
55 "expression": "foo[2].bar",
56 "result": "three"
57 },
58 {
59 "expression": "foo[3].notbar",
60 "result": "four"
61 },
62 {
63 "expression": "foo[3].bar",
64 "result": null
65 },
66 {
67 "expression": "foo[0]",
68 "result": {"bar": "one"}
69 },
70 {
71 "expression": "foo[1]",
72 "result": {"bar": "two"}
73 },
74 {
75 "expression": "foo[2]",
76 "result": {"bar": "three"}
77 },
78 {
79 "expression": "foo[3]",
80 "result": {"notbar": "four"}
81 },
82 {
83 "expression": "foo[4]",
84 "result": null
85 }
86 ]
87 },
88 {
89 "given": [
90 "one", "two", "three"
91 ],
92 "cases": [
93 {
94 "expression": "[0]",
95 "result": "one"
96 },
97 {
98 "expression": "[1]",
99 "result": "two"
100 },
101 {
102 "expression": "[2]",
103 "result": "three"
104 },
105 {
106 "expression": "[-1]",
107 "result": "three"
108 },
109 {
110 "expression": "[-2]",
111 "result": "two"
112 },
113 {
114 "expression": "[-3]",
115 "result": "one"
116 }
117 ]
118 },
119 {
120 "given": {"reservations": [
121 {"instances": [{"foo": 1}, {"foo": 2}]}
122 ]},
123 "cases": [
124 {
125 "expression": "reservations[].instances[].foo",
126 "result": [1, 2]
127 },
128 {
129 "expression": "reservations[].instances[].bar",
130 "result": []
131 },
132 {
133 "expression": "reservations[].notinstances[].foo",
134 "result": []
135 },
136 {
137 "expression": "reservations[].notinstances[].foo",
138 "result": []
139 }
140 ]
141 },
142 {
143 "given": {"reservations": [{
144 "instances": [
145 {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]},
146 {"foo": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]},
147 {"foo": "bar"},
148 {"notfoo": [{"bar": 20}, {"bar": 21}, {"notbar": [7]}, {"bar": 22}]},
149 {"bar": [{"baz": [1]}, {"baz": [2]}, {"baz": [3]}, {"baz": [4]}]},
150 {"baz": [{"baz": [1, 2]}, {"baz": []}, {"baz": []}, {"baz": [3, 4]}]},
151 {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]}
152 ],
153 "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}
154 }, {
155 "instances": [
156 {"a": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]},
157 {"b": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]},
158 {"c": "bar"},
159 {"notfoo": [{"bar": 23}, {"bar": 24}, {"notbar": [7]}, {"bar": 25}]},
160 {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]}
161 ],
162 "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}
163 }
164 ]},
165 "cases": [
166 {
167 "expression": "reservations[].instances[].foo[].bar",
168 "result": [1, 2, 4, 5, 6, 8]
169 },
170 {
171 "expression": "reservations[].instances[].foo[].baz",
172 "result": []
173 },
174 {
175 "expression": "reservations[].instances[].notfoo[].bar",
176 "result": [20, 21, 22, 23, 24, 25]
177 },
178 {
179 "expression": "reservations[].instances[].notfoo[].notbar",
180 "result": [[7], [7]]
181 },
182 {
183 "expression": "reservations[].notinstances[].foo",
184 "result": []
185 },
186 {
187 "expression": "reservations[].instances[].foo[].notbar",
188 "result": [3, [7]]
189 },
190 {
191 "expression": "reservations[].instances[].bar[].baz",
192 "result": [[1], [2], [3], [4]]
193 },
194 {
195 "expression": "reservations[].instances[].baz[].baz",
196 "result": [[1, 2], [], [], [3, 4]]
197 },
198 {
199 "expression": "reservations[].instances[].qux[].baz",
200 "result": [[], [1, 2, 3], [4], [], [], [1, 2, 3], [4], []]
201 },
202 {
203 "expression": "reservations[].instances[].qux[].baz[]",
204 "result": [1, 2, 3, 4, 1, 2, 3, 4]
205 }
206 ]
207 },
208 {
209 "given": {
210 "foo": [
211 [["one", "two"], ["three", "four"]],
212 [["five", "six"], ["seven", "eight"]],
213 [["nine"], ["ten"]]
214 ]
215 },
216 "cases": [
217 {
218 "expression": "foo[]",
219 "result": [["one", "two"], ["three", "four"], ["five", "six"],
220 ["seven", "eight"], ["nine"], ["ten"]]
221 },
222 {
223 "expression": "foo[][0]",
224 "result": ["one", "three", "five", "seven", "nine", "ten"]
225 },
226 {
227 "expression": "foo[][1]",
228 "result": ["two", "four", "six", "eight"]
229 },
230 {
231 "expression": "foo[][0][0]",
232 "result": []
233 },
234 {
235 "expression": "foo[][2][2]",
236 "result": []
237 },
238 {
239 "expression": "foo[][0][0][100]",
240 "result": []
241 }
242 ]
243 },
244 {
245 "given": {
246 "foo": [{
247 "bar": [
248 {
249 "qux": 2,
250 "baz": 1
251 },
252 {
253 "qux": 4,
254 "baz": 3
255 }
256 ]
257 },
258 {
259 "bar": [
260 {
261 "qux": 6,
262 "baz": 5
263 },
264 {
265 "qux": 8,
266 "baz": 7
267 }
268 ]
269 }
270 ]
271 },
272 "cases": [
273 {
274 "expression": "foo",
275 "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
276 {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
277 },
278 {
279 "expression": "foo[]",
280 "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
281 {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
282 },
283 {
284 "expression": "foo[].bar",
285 "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}],
286 [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]]
287 },
288 {
289 "expression": "foo[].bar[]",
290 "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3},
291 {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]
292 },
293 {
294 "expression": "foo[].bar[].baz",
295 "result": [1, 3, 5, 7]
296 }
297 ]
298 },
299 {
300 "given": {
301 "string": "string",
302 "hash": {"foo": "bar", "bar": "baz"},
303 "number": 23,
304 "nullvalue": null
305 },
306 "cases": [
307 {
308 "expression": "string[]",
309 "result": null
310 },
311 {
312 "expression": "hash[]",
313 "result": null
314 },
315 {
316 "expression": "number[]",
317 "result": null
318 },
319 {
320 "expression": "nullvalue[]",
321 "result": null
322 },
323 {
324 "expression": "string[].foo",
325 "result": null
326 },
327 {
328 "expression": "hash[].foo",
329 "result": null
330 },
331 {
332 "expression": "number[].foo",
333 "result": null
334 },
335 {
336 "expression": "nullvalue[].foo",
337 "result": null
338 },
339 {
340 "expression": "nullvalue[].foo[].bar",
341 "result": null
342 }
343 ]
344 }
345 ]
0 [
1 {
2 "given": {
3 "foo": [{"name": "a"}, {"name": "b"}],
4 "bar": {"baz": "qux"}
5 },
6 "cases": [
7 {
8 "expression": "`\"foo\"`",
9 "result": "foo"
10 },
11 {
12 "comment": "Interpret escaped unicode.",
13 "expression": "`\"\\u03a6\"`",
14 "result": "Φ"
15 },
16 {
17 "expression": "`\"✓\"`",
18 "result": "✓"
19 },
20 {
21 "expression": "`[1, 2, 3]`",
22 "result": [1, 2, 3]
23 },
24 {
25 "expression": "`{\"a\": \"b\"}`",
26 "result": {"a": "b"}
27 },
28 {
29 "expression": "`true`",
30 "result": true
31 },
32 {
33 "expression": "`false`",
34 "result": false
35 },
36 {
37 "expression": "`null`",
38 "result": null
39 },
40 {
41 "expression": "`0`",
42 "result": 0
43 },
44 {
45 "expression": "`1`",
46 "result": 1
47 },
48 {
49 "expression": "`2`",
50 "result": 2
51 },
52 {
53 "expression": "`3`",
54 "result": 3
55 },
56 {
57 "expression": "`4`",
58 "result": 4
59 },
60 {
61 "expression": "`5`",
62 "result": 5
63 },
64 {
65 "expression": "`6`",
66 "result": 6
67 },
68 {
69 "expression": "`7`",
70 "result": 7
71 },
72 {
73 "expression": "`8`",
74 "result": 8
75 },
76 {
77 "expression": "`9`",
78 "result": 9
79 },
80 {
81 "comment": "Escaping a backtick in quotes",
82 "expression": "`\"foo\\`bar\"`",
83 "result": "foo`bar"
84 },
85 {
86 "comment": "Double quote in literal",
87 "expression": "`\"foo\\\"bar\"`",
88 "result": "foo\"bar"
89 },
90 {
91 "expression": "`\"1\\`\"`",
92 "result": "1`"
93 },
94 {
95 "comment": "Multiple literal expressions with escapes",
96 "expression": "`\"\\\\\"`.{a:`\"b\"`}",
97 "result": {"a": "b"}
98 },
99 {
100 "comment": "literal . identifier",
101 "expression": "`{\"a\": \"b\"}`.a",
102 "result": "b"
103 },
104 {
105 "comment": "literal . identifier . identifier",
106 "expression": "`{\"a\": {\"b\": \"c\"}}`.a.b",
107 "result": "c"
108 },
109 {
110 "comment": "literal . identifier bracket-expr",
111 "expression": "`[0, 1, 2]`[1]",
112 "result": 1
113 }
114 ]
115 },
116 {
117 "comment": "Literals",
118 "given": {"type": "object"},
119 "cases": [
120 {
121 "comment": "Literal with leading whitespace",
122 "expression": "` {\"foo\": true}`",
123 "result": {"foo": true}
124 },
125 {
126 "comment": "Literal with trailing whitespace",
127 "expression": "`{\"foo\": true} `",
128 "result": {"foo": true}
129 },
130 {
131 "comment": "Literal on RHS of subexpr not allowed",
132 "expression": "foo.`\"bar\"`",
133 "error": "syntax"
134 }
135 ]
136 },
137 {
138 "comment": "Raw String Literals",
139 "given": {},
140 "cases": [
141 {
142 "expression": "'foo'",
143 "result": "foo"
144 },
145 {
146 "expression": "' foo '",
147 "result": " foo "
148 },
149 {
150 "expression": "'0'",
151 "result": "0"
152 },
153 {
154 "expression": "'newline\n'",
155 "result": "newline\n"
156 },
157 {
158 "expression": "'\n'",
159 "result": "\n"
160 },
161 {
162 "expression": "'✓'",
163 "result": "✓"
164 },
165 {
166 "expression": "'𝄞'",
167 "result": "𝄞"
168 },
169 {
170 "expression": "' [foo] '",
171 "result": " [foo] "
172 },
173 {
174 "expression": "'[foo]'",
175 "result": "[foo]"
176 },
177 {
178 "comment": "Do not interpret escaped unicode.",
179 "expression": "'\\u03a6'",
180 "result": "\\u03a6"
181 },
182 {
183 "comment": "Can escape the single quote",
184 "expression": "'foo\\'bar'",
185 "result": "foo'bar"
186 }
187 ]
188 }
189 ]
0 [{
1 "given": {
2 "foo": {
3 "bar": "bar",
4 "baz": "baz",
5 "qux": "qux",
6 "nested": {
7 "one": {
8 "a": "first",
9 "b": "second",
10 "c": "third"
11 },
12 "two": {
13 "a": "first",
14 "b": "second",
15 "c": "third"
16 },
17 "three": {
18 "a": "first",
19 "b": "second",
20 "c": {"inner": "third"}
21 }
22 }
23 },
24 "bar": 1,
25 "baz": 2,
26 "qux\"": 3
27 },
28 "cases": [
29 {
30 "expression": "foo.{bar: bar}",
31 "result": {"bar": "bar"}
32 },
33 {
34 "expression": "foo.{\"bar\": bar}",
35 "result": {"bar": "bar"}
36 },
37 {
38 "expression": "foo.{\"foo.bar\": bar}",
39 "result": {"foo.bar": "bar"}
40 },
41 {
42 "expression": "foo.{bar: bar, baz: baz}",
43 "result": {"bar": "bar", "baz": "baz"}
44 },
45 {
46 "expression": "foo.{\"bar\": bar, \"baz\": baz}",
47 "result": {"bar": "bar", "baz": "baz"}
48 },
49 {
50 "expression": "{\"baz\": baz, \"qux\\\"\": \"qux\\\"\"}",
51 "result": {"baz": 2, "qux\"": 3}
52 },
53 {
54 "expression": "foo.{bar:bar,baz:baz}",
55 "result": {"bar": "bar", "baz": "baz"}
56 },
57 {
58 "expression": "foo.{bar: bar,qux: qux}",
59 "result": {"bar": "bar", "qux": "qux"}
60 },
61 {
62 "expression": "foo.{bar: bar, noexist: noexist}",
63 "result": {"bar": "bar", "noexist": null}
64 },
65 {
66 "expression": "foo.{noexist: noexist, alsonoexist: alsonoexist}",
67 "result": {"noexist": null, "alsonoexist": null}
68 },
69 {
70 "expression": "foo.badkey.{nokey: nokey, alsonokey: alsonokey}",
71 "result": null
72 },
73 {
74 "expression": "foo.nested.*.{a: a,b: b}",
75 "result": [{"a": "first", "b": "second"},
76 {"a": "first", "b": "second"},
77 {"a": "first", "b": "second"}]
78 },
79 {
80 "expression": "foo.nested.three.{a: a, cinner: c.inner}",
81 "result": {"a": "first", "cinner": "third"}
82 },
83 {
84 "expression": "foo.nested.three.{a: a, c: c.inner.bad.key}",
85 "result": {"a": "first", "c": null}
86 },
87 {
88 "expression": "foo.{a: nested.one.a, b: nested.two.b}",
89 "result": {"a": "first", "b": "second"}
90 },
91 {
92 "expression": "{bar: bar, baz: baz}",
93 "result": {"bar": 1, "baz": 2}
94 },
95 {
96 "expression": "{bar: bar}",
97 "result": {"bar": 1}
98 },
99 {
100 "expression": "{otherkey: bar}",
101 "result": {"otherkey": 1}
102 },
103 {
104 "expression": "{no: no, exist: exist}",
105 "result": {"no": null, "exist": null}
106 },
107 {
108 "expression": "foo.[bar]",
109 "result": ["bar"]
110 },
111 {
112 "expression": "foo.[bar,baz]",
113 "result": ["bar", "baz"]
114 },
115 {
116 "expression": "foo.[bar,qux]",
117 "result": ["bar", "qux"]
118 },
119 {
120 "expression": "foo.[bar,noexist]",
121 "result": ["bar", null]
122 },
123 {
124 "expression": "foo.[noexist,alsonoexist]",
125 "result": [null, null]
126 }
127 ]
128 }, {
129 "given": {
130 "foo": {"bar": 1, "baz": [2, 3, 4]}
131 },
132 "cases": [
133 {
134 "expression": "foo.{bar:bar,baz:baz}",
135 "result": {"bar": 1, "baz": [2, 3, 4]}
136 },
137 {
138 "expression": "foo.[bar,baz[0]]",
139 "result": [1, 2]
140 },
141 {
142 "expression": "foo.[bar,baz[1]]",
143 "result": [1, 3]
144 },
145 {
146 "expression": "foo.[bar,baz[2]]",
147 "result": [1, 4]
148 },
149 {
150 "expression": "foo.[bar,baz[3]]",
151 "result": [1, null]
152 },
153 {
154 "expression": "foo.[bar[0],baz[3]]",
155 "result": [null, null]
156 }
157 ]
158 }, {
159 "given": {
160 "foo": {"bar": 1, "baz": 2}
161 },
162 "cases": [
163 {
164 "expression": "foo.{bar: bar, baz: baz}",
165 "result": {"bar": 1, "baz": 2}
166 },
167 {
168 "expression": "foo.[bar,baz]",
169 "result": [1, 2]
170 }
171 ]
172 }, {
173 "given": {
174 "foo": {
175 "bar": {"baz": [{"common": "first", "one": 1},
176 {"common": "second", "two": 2}]},
177 "ignoreme": 1,
178 "includeme": true
179 }
180 },
181 "cases": [
182 {
183 "expression": "foo.{bar: bar.baz[1],includeme: includeme}",
184 "result": {"bar": {"common": "second", "two": 2}, "includeme": true}
185 },
186 {
187 "expression": "foo.{\"bar.baz.two\": bar.baz[1].two, includeme: includeme}",
188 "result": {"bar.baz.two": 2, "includeme": true}
189 },
190 {
191 "expression": "foo.[includeme, bar.baz[*].common]",
192 "result": [true, ["first", "second"]]
193 },
194 {
195 "expression": "foo.[includeme, bar.baz[*].none]",
196 "result": [true, []]
197 },
198 {
199 "expression": "foo.[includeme, bar.baz[].common]",
200 "result": [true, ["first", "second"]]
201 }
202 ]
203 }, {
204 "given": {
205 "reservations": [{
206 "instances": [
207 {"id": "id1",
208 "name": "first"},
209 {"id": "id2",
210 "name": "second"}
211 ]}, {
212 "instances": [
213 {"id": "id3",
214 "name": "third"},
215 {"id": "id4",
216 "name": "fourth"}
217 ]}
218 ]},
219 "cases": [
220 {
221 "expression": "reservations[*].instances[*].{id: id, name: name}",
222 "result": [[{"id": "id1", "name": "first"}, {"id": "id2", "name": "second"}],
223 [{"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"}]]
224 },
225 {
226 "expression": "reservations[].instances[].{id: id, name: name}",
227 "result": [{"id": "id1", "name": "first"},
228 {"id": "id2", "name": "second"},
229 {"id": "id3", "name": "third"},
230 {"id": "id4", "name": "fourth"}]
231 },
232 {
233 "expression": "reservations[].instances[].[id, name]",
234 "result": [["id1", "first"],
235 ["id2", "second"],
236 ["id3", "third"],
237 ["id4", "fourth"]]
238 }
239 ]
240 },
241 {
242 "given": {
243 "foo": [{
244 "bar": [
245 {
246 "qux": 2,
247 "baz": 1
248 },
249 {
250 "qux": 4,
251 "baz": 3
252 }
253 ]
254 },
255 {
256 "bar": [
257 {
258 "qux": 6,
259 "baz": 5
260 },
261 {
262 "qux": 8,
263 "baz": 7
264 }
265 ]
266 }
267 ]
268 },
269 "cases": [
270 {
271 "expression": "foo",
272 "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
273 {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
274 },
275 {
276 "expression": "foo[]",
277 "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
278 {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
279 },
280 {
281 "expression": "foo[].bar",
282 "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}],
283 [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]]
284 },
285 {
286 "expression": "foo[].bar[]",
287 "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3},
288 {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]
289 },
290 {
291 "expression": "foo[].bar[].[baz, qux]",
292 "result": [[1, 2], [3, 4], [5, 6], [7, 8]]
293 },
294 {
295 "expression": "foo[].bar[].[baz]",
296 "result": [[1], [3], [5], [7]]
297 },
298 {
299 "expression": "foo[].bar[].[baz, qux][]",
300 "result": [1, 2, 3, 4, 5, 6, 7, 8]
301 }
302 ]
303 },
304 {
305 "given": {
306 "foo": {
307 "baz": [
308 {
309 "bar": "abc"
310 }, {
311 "bar": "def"
312 }
313 ],
314 "qux": ["zero"]
315 }
316 },
317 "cases": [
318 {
319 "expression": "foo.[baz[*].bar, qux[0]]",
320 "result": [["abc", "def"], "zero"]
321 }
322 ]
323 },
324 {
325 "given": {
326 "foo": {
327 "baz": [
328 {
329 "bar": "a",
330 "bam": "b",
331 "boo": "c"
332 }, {
333 "bar": "d",
334 "bam": "e",
335 "boo": "f"
336 }
337 ],
338 "qux": ["zero"]
339 }
340 },
341 "cases": [
342 {
343 "expression": "foo.[baz[*].[bar, boo], qux[0]]",
344 "result": [[["a", "c" ], ["d", "f" ]], "zero"]
345 }
346 ]
347 },
348 {
349 "given": {
350 "foo": {
351 "baz": [
352 {
353 "bar": "a",
354 "bam": "b",
355 "boo": "c"
356 }, {
357 "bar": "d",
358 "bam": "e",
359 "boo": "f"
360 }
361 ],
362 "qux": ["zero"]
363 }
364 },
365 "cases": [
366 {
367 "expression": "foo.[baz[*].not_there || baz[*].bar, qux[0]]",
368 "result": [["a", "d"], "zero"]
369 }
370 ]
371 },
372 {
373 "given": {"type": "object"},
374 "cases": [
375 {
376 "comment": "Nested multiselect",
377 "expression": "[[*],*]",
378 "result": [null, ["object"]]
379 }
380 ]
381 },
382 {
383 "given": [],
384 "cases": [
385 {
386 "comment": "Nested multiselect",
387 "expression": "[[*]]",
388 "result": [[]]
389 }
390 ]
391 }
392 ]
0 [{
1 "given": {
2 "foo": {
3 "bar": {
4 "baz": "subkey"
5 },
6 "other": {
7 "baz": "subkey"
8 },
9 "other2": {
10 "baz": "subkey"
11 },
12 "other3": {
13 "notbaz": ["a", "b", "c"]
14 },
15 "other4": {
16 "notbaz": ["a", "b", "c"]
17 }
18 }
19 },
20 "cases": [
21 {
22 "expression": "foo.*.baz | [0]",
23 "result": "subkey"
24 },
25 {
26 "expression": "foo.*.baz | [1]",
27 "result": "subkey"
28 },
29 {
30 "expression": "foo.*.baz | [2]",
31 "result": "subkey"
32 },
33 {
34 "expression": "foo.bar.* | [0]",
35 "result": "subkey"
36 },
37 {
38 "expression": "foo.*.notbaz | [*]",
39 "result": [["a", "b", "c"], ["a", "b", "c"]]
40 },
41 {
42 "expression": "{\"a\": foo.bar, \"b\": foo.other} | *.baz",
43 "result": ["subkey", "subkey"]
44 }
45 ]
46 }, {
47 "given": {
48 "foo": {
49 "bar": {
50 "baz": "one"
51 },
52 "other": {
53 "baz": "two"
54 },
55 "other2": {
56 "baz": "three"
57 },
58 "other3": {
59 "notbaz": ["a", "b", "c"]
60 },
61 "other4": {
62 "notbaz": ["d", "e", "f"]
63 }
64 }
65 },
66 "cases": [
67 {
68 "expression": "foo | bar",
69 "result": {"baz": "one"}
70 },
71 {
72 "expression": "foo | bar | baz",
73 "result": "one"
74 },
75 {
76 "expression": "foo|bar| baz",
77 "result": "one"
78 },
79 {
80 "expression": "not_there | [0]",
81 "result": null
82 },
83 {
84 "expression": "not_there | [0]",
85 "result": null
86 },
87 {
88 "expression": "[foo.bar, foo.other] | [0]",
89 "result": {"baz": "one"}
90 },
91 {
92 "expression": "{\"a\": foo.bar, \"b\": foo.other} | a",
93 "result": {"baz": "one"}
94 },
95 {
96 "expression": "{\"a\": foo.bar, \"b\": foo.other} | b",
97 "result": {"baz": "two"}
98 },
99 {
100 "expression": "foo.bam || foo.bar | baz",
101 "result": "one"
102 },
103 {
104 "expression": "foo | not_there || bar",
105 "result": {"baz": "one"}
106 }
107 ]
108 }, {
109 "given": {
110 "foo": [{
111 "bar": [{
112 "baz": "one"
113 }, {
114 "baz": "two"
115 }]
116 }, {
117 "bar": [{
118 "baz": "three"
119 }, {
120 "baz": "four"
121 }]
122 }]
123 },
124 "cases": [
125 {
126 "expression": "foo[*].bar[*] | [0][0]",
127 "result": {"baz": "one"}
128 }
129 ]
130 }]
0 [{
1 "given": {
2 "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
3 "bar": {
4 "baz": 1
5 }
6 },
7 "cases": [
8 {
9 "expression": "bar[0:10]",
10 "result": null
11 },
12 {
13 "expression": "foo[0:10:1]",
14 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
15 },
16 {
17 "expression": "foo[0:10]",
18 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
19 },
20 {
21 "expression": "foo[0:10:]",
22 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
23 },
24 {
25 "expression": "foo[0::1]",
26 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
27 },
28 {
29 "expression": "foo[0::]",
30 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
31 },
32 {
33 "expression": "foo[0:]",
34 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
35 },
36 {
37 "expression": "foo[:10:1]",
38 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
39 },
40 {
41 "expression": "foo[::1]",
42 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
43 },
44 {
45 "expression": "foo[:10:]",
46 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
47 },
48 {
49 "expression": "foo[::]",
50 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
51 },
52 {
53 "expression": "foo[:]",
54 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
55 },
56 {
57 "expression": "foo[1:9]",
58 "result": [1, 2, 3, 4, 5, 6, 7, 8]
59 },
60 {
61 "expression": "foo[0:10:2]",
62 "result": [0, 2, 4, 6, 8]
63 },
64 {
65 "expression": "foo[5:]",
66 "result": [5, 6, 7, 8, 9]
67 },
68 {
69 "expression": "foo[5::2]",
70 "result": [5, 7, 9]
71 },
72 {
73 "expression": "foo[::2]",
74 "result": [0, 2, 4, 6, 8]
75 },
76 {
77 "expression": "foo[::-1]",
78 "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
79 },
80 {
81 "expression": "foo[1::2]",
82 "result": [1, 3, 5, 7, 9]
83 },
84 {
85 "expression": "foo[10:0:-1]",
86 "result": [9, 8, 7, 6, 5, 4, 3, 2, 1]
87 },
88 {
89 "expression": "foo[10:5:-1]",
90 "result": [9, 8, 7, 6]
91 },
92 {
93 "expression": "foo[8:2:-2]",
94 "result": [8, 6, 4]
95 },
96 {
97 "expression": "foo[0:20]",
98 "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
99 },
100 {
101 "expression": "foo[10:-20:-1]",
102 "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
103 },
104 {
105 "expression": "foo[10:-20]",
106 "result": []
107 },
108 {
109 "expression": "foo[-4:-1]",
110 "result": [6, 7, 8]
111 },
112 {
113 "expression": "foo[:-5:-1]",
114 "result": [9, 8, 7, 6]
115 },
116 {
117 "expression": "foo[8:2:0]",
118 "error": "invalid-value"
119 },
120 {
121 "expression": "foo[8:2:0:1]",
122 "error": "syntax"
123 },
124 {
125 "expression": "foo[8:2&]",
126 "error": "syntax"
127 },
128 {
129 "expression": "foo[2:a:3]",
130 "error": "syntax"
131 }
132 ]
133 }, {
134 "given": {
135 "foo": [{"a": 1}, {"a": 2}, {"a": 3}],
136 "bar": [{"a": {"b": 1}}, {"a": {"b": 2}},
137 {"a": {"b": 3}}],
138 "baz": 50
139 },
140 "cases": [
141 {
142 "expression": "foo[:2].a",
143 "result": [1, 2]
144 },
145 {
146 "expression": "foo[:2].b",
147 "result": []
148 },
149 {
150 "expression": "foo[:2].a.b",
151 "result": []
152 },
153 {
154 "expression": "bar[::-1].a.b",
155 "result": [3, 2, 1]
156 },
157 {
158 "expression": "bar[:2].a.b",
159 "result": [1, 2]
160 },
161 {
162 "expression": "baz[:2].a",
163 "result": null
164 }
165 ]
166 }, {
167 "given": [{"a": 1}, {"a": 2}, {"a": 3}],
168 "cases": [
169 {
170 "expression": "[:]",
171 "result": [{"a": 1}, {"a": 2}, {"a": 3}]
172 },
173 {
174 "expression": "[:2].a",
175 "result": [1, 2]
176 },
177 {
178 "expression": "[::-1].a",
179 "result": [3, 2, 1]
180 },
181 {
182 "expression": "[:2].b",
183 "result": []
184 }
185 ]
186 }]
0 [{
1 "comment": "Dot syntax",
2 "given": {"type": "object"},
3 "cases": [
4 {
5 "expression": "foo.bar",
6 "result": null
7 },
8 {
9 "expression": "foo.1",
10 "error": "syntax"
11 },
12 {
13 "expression": "foo.-11",
14 "error": "syntax"
15 },
16 {
17 "expression": "foo",
18 "result": null
19 },
20 {
21 "expression": "foo.",
22 "error": "syntax"
23 },
24 {
25 "expression": "foo.",
26 "error": "syntax"
27 },
28 {
29 "expression": ".foo",
30 "error": "syntax"
31 },
32 {
33 "expression": "foo..bar",
34 "error": "syntax"
35 },
36 {
37 "expression": "foo.bar.",
38 "error": "syntax"
39 },
40 {
41 "expression": "foo[.]",
42 "error": "syntax"
43 }
44 ]
45 },
46 {
47 "comment": "Simple token errors",
48 "given": {"type": "object"},
49 "cases": [
50 {
51 "expression": ".",
52 "error": "syntax"
53 },
54 {
55 "expression": ":",
56 "error": "syntax"
57 },
58 {
59 "expression": ",",
60 "error": "syntax"
61 },
62 {
63 "expression": "]",
64 "error": "syntax"
65 },
66 {
67 "expression": "[",
68 "error": "syntax"
69 },
70 {
71 "expression": "}",
72 "error": "syntax"
73 },
74 {
75 "expression": "{",
76 "error": "syntax"
77 },
78 {
79 "expression": ")",
80 "error": "syntax"
81 },
82 {
83 "expression": "(",
84 "error": "syntax"
85 },
86 {
87 "expression": "((&",
88 "error": "syntax"
89 },
90 {
91 "expression": "a[",
92 "error": "syntax"
93 },
94 {
95 "expression": "a]",
96 "error": "syntax"
97 },
98 {
99 "expression": "a][",
100 "error": "syntax"
101 },
102 {
103 "expression": "!",
104 "error": "syntax"
105 }
106 ]
107 },
108 {
109 "comment": "Boolean syntax errors",
110 "given": {"type": "object"},
111 "cases": [
112 {
113 "expression": "![!(!",
114 "error": "syntax"
115 }
116 ]
117 },
118 {
119 "comment": "Wildcard syntax",
120 "given": {"type": "object"},
121 "cases": [
122 {
123 "expression": "*",
124 "result": ["object"]
125 },
126 {
127 "expression": "*.*",
128 "result": []
129 },
130 {
131 "expression": "*.foo",
132 "result": []
133 },
134 {
135 "expression": "*[0]",
136 "result": []
137 },
138 {
139 "expression": ".*",
140 "error": "syntax"
141 },
142 {
143 "expression": "*foo",
144 "error": "syntax"
145 },
146 {
147 "expression": "*0",
148 "error": "syntax"
149 },
150 {
151 "expression": "foo[*]bar",
152 "error": "syntax"
153 },
154 {
155 "expression": "foo[*]*",
156 "error": "syntax"
157 }
158 ]
159 },
160 {
161 "comment": "Flatten syntax",
162 "given": {"type": "object"},
163 "cases": [
164 {
165 "expression": "[]",
166 "result": null
167 }
168 ]
169 },
170 {
171 "comment": "Simple bracket syntax",
172 "given": {"type": "object"},
173 "cases": [
174 {
175 "expression": "[0]",
176 "result": null
177 },
178 {
179 "expression": "[*]",
180 "result": null
181 },
182 {
183 "expression": "*.[0]",
184 "error": "syntax"
185 },
186 {
187 "expression": "*.[\"0\"]",
188 "result": [[null]]
189 },
190 {
191 "expression": "[*].bar",
192 "result": null
193 },
194 {
195 "expression": "[*][0]",
196 "result": null
197 },
198 {
199 "expression": "foo[#]",
200 "error": "syntax"
201 }
202 ]
203 },
204 {
205 "comment": "Multi-select list syntax",
206 "given": {"type": "object"},
207 "cases": [
208 {
209 "expression": "foo[0]",
210 "result": null
211 },
212 {
213 "comment": "Valid multi-select of a list",
214 "expression": "foo[0, 1]",
215 "error": "syntax"
216 },
217 {
218 "expression": "foo.[0]",
219 "error": "syntax"
220 },
221 {
222 "expression": "foo.[*]",
223 "result": null
224 },
225 {
226 "comment": "Multi-select of a list with trailing comma",
227 "expression": "foo[0, ]",
228 "error": "syntax"
229 },
230 {
231 "comment": "Multi-select of a list with trailing comma and no close",
232 "expression": "foo[0,",
233 "error": "syntax"
234 },
235 {
236 "comment": "Multi-select of a list with trailing comma and no close",
237 "expression": "foo.[a",
238 "error": "syntax"
239 },
240 {
241 "comment": "Multi-select of a list with extra comma",
242 "expression": "foo[0,, 1]",
243 "error": "syntax"
244 },
245 {
246 "comment": "Multi-select of a list using an identifier index",
247 "expression": "foo[abc]",
248 "error": "syntax"
249 },
250 {
251 "comment": "Multi-select of a list using identifier indices",
252 "expression": "foo[abc, def]",
253 "error": "syntax"
254 },
255 {
256 "comment": "Multi-select of a list using an identifier index",
257 "expression": "foo[abc, 1]",
258 "error": "syntax"
259 },
260 {
261 "comment": "Multi-select of a list using an identifier index with trailing comma",
262 "expression": "foo[abc, ]",
263 "error": "syntax"
264 },
265 {
266 "comment": "Valid multi-select of a hash using an identifier index",
267 "expression": "foo.[abc]",
268 "result": null
269 },
270 {
271 "comment": "Valid multi-select of a hash",
272 "expression": "foo.[abc, def]",
273 "result": null
274 },
275 {
276 "comment": "Multi-select of a hash using a numeric index",
277 "expression": "foo.[abc, 1]",
278 "error": "syntax"
279 },
280 {
281 "comment": "Multi-select of a hash with a trailing comma",
282 "expression": "foo.[abc, ]",
283 "error": "syntax"
284 },
285 {
286 "comment": "Multi-select of a hash with extra commas",
287 "expression": "foo.[abc,, def]",
288 "error": "syntax"
289 },
290 {
291 "comment": "Multi-select of a hash using number indices",
292 "expression": "foo.[0, 1]",
293 "error": "syntax"
294 }
295 ]
296 },
297 {
298 "comment": "Multi-select hash syntax",
299 "given": {"type": "object"},
300 "cases": [
301 {
302 "comment": "No key or value",
303 "expression": "a{}",
304 "error": "syntax"
305 },
306 {
307 "comment": "No closing token",
308 "expression": "a{",
309 "error": "syntax"
310 },
311 {
312 "comment": "Not a key value pair",
313 "expression": "a{foo}",
314 "error": "syntax"
315 },
316 {
317 "comment": "Missing value and closing character",
318 "expression": "a{foo:",
319 "error": "syntax"
320 },
321 {
322 "comment": "Missing closing character",
323 "expression": "a{foo: 0",
324 "error": "syntax"
325 },
326 {
327 "comment": "Missing value",
328 "expression": "a{foo:}",
329 "error": "syntax"
330 },
331 {
332 "comment": "Trailing comma and no closing character",
333 "expression": "a{foo: 0, ",
334 "error": "syntax"
335 },
336 {
337 "comment": "Missing value with trailing comma",
338 "expression": "a{foo: ,}",
339 "error": "syntax"
340 },
341 {
342 "comment": "Accessing Array using an identifier",
343 "expression": "a{foo: bar}",
344 "error": "syntax"
345 },
346 {
347 "expression": "a{foo: 0}",
348 "error": "syntax"
349 },
350 {
351 "comment": "Missing key-value pair",
352 "expression": "a.{}",
353 "error": "syntax"
354 },
355 {
356 "comment": "Not a key-value pair",
357 "expression": "a.{foo}",
358 "error": "syntax"
359 },
360 {
361 "comment": "Missing value",
362 "expression": "a.{foo:}",
363 "error": "syntax"
364 },
365 {
366 "comment": "Missing value with trailing comma",
367 "expression": "a.{foo: ,}",
368 "error": "syntax"
369 },
370 {
371 "comment": "Valid multi-select hash extraction",
372 "expression": "a.{foo: bar}",
373 "result": null
374 },
375 {
376 "comment": "Valid multi-select hash extraction",
377 "expression": "a.{foo: bar, baz: bam}",
378 "result": null
379 },
380 {
381 "comment": "Trailing comma",
382 "expression": "a.{foo: bar, }",
383 "error": "syntax"
384 },
385 {
386 "comment": "Missing key in second key-value pair",
387 "expression": "a.{foo: bar, baz}",
388 "error": "syntax"
389 },
390 {
391 "comment": "Missing value in second key-value pair",
392 "expression": "a.{foo: bar, baz:}",
393 "error": "syntax"
394 },
395 {
396 "comment": "Trailing comma",
397 "expression": "a.{foo: bar, baz: bam, }",
398 "error": "syntax"
399 },
400 {
401 "comment": "Nested multi select",
402 "expression": "{\"\\\\\":{\" \":*}}",
403 "result": {"\\": {" ": ["object"]}}
404 }
405 ]
406 },
407 {
408 "comment": "Or expressions",
409 "given": {"type": "object"},
410 "cases": [
411 {
412 "expression": "foo || bar",
413 "result": null
414 },
415 {
416 "expression": "foo ||",
417 "error": "syntax"
418 },
419 {
420 "expression": "foo.|| bar",
421 "error": "syntax"
422 },
423 {
424 "expression": " || foo",
425 "error": "syntax"
426 },
427 {
428 "expression": "foo || || foo",
429 "error": "syntax"
430 },
431 {
432 "expression": "foo.[a || b]",
433 "result": null
434 },
435 {
436 "expression": "foo.[a ||]",
437 "error": "syntax"
438 },
439 {
440 "expression": "\"foo",
441 "error": "syntax"
442 }
443 ]
444 },
445 {
446 "comment": "Filter expressions",
447 "given": {"type": "object"},
448 "cases": [
449 {
450 "expression": "foo[?bar==`\"baz\"`]",
451 "result": null
452 },
453 {
454 "expression": "foo[? bar == `\"baz\"` ]",
455 "result": null
456 },
457 {
458 "expression": "foo[ ?bar==`\"baz\"`]",
459 "error": "syntax"
460 },
461 {
462 "expression": "foo[?bar==]",
463 "error": "syntax"
464 },
465 {
466 "expression": "foo[?==]",
467 "error": "syntax"
468 },
469 {
470 "expression": "foo[?==bar]",
471 "error": "syntax"
472 },
473 {
474 "expression": "foo[?bar==baz?]",
475 "error": "syntax"
476 },
477 {
478 "expression": "foo[?a.b.c==d.e.f]",
479 "result": null
480 },
481 {
482 "expression": "foo[?bar==`[0, 1, 2]`]",
483 "result": null
484 },
485 {
486 "expression": "foo[?bar==`[\"a\", \"b\", \"c\"]`]",
487 "result": null
488 },
489 {
490 "comment": "Literal char not escaped",
491 "expression": "foo[?bar==`[\"foo`bar\"]`]",
492 "error": "syntax"
493 },
494 {
495 "comment": "Literal char escaped",
496 "expression": "foo[?bar==`[\"foo\\`bar\"]`]",
497 "result": null
498 },
499 {
500 "comment": "Unknown comparator",
501 "expression": "foo[?bar<>baz]",
502 "error": "syntax"
503 },
504 {
505 "comment": "Unknown comparator",
506 "expression": "foo[?bar^baz]",
507 "error": "syntax"
508 },
509 {
510 "expression": "foo[bar==baz]",
511 "error": "syntax"
512 },
513 {
514 "comment": "Quoted identifier in filter expression no spaces",
515 "expression": "[?\"\\\\\">`\"foo\"`]",
516 "result": null
517 },
518 {
519 "comment": "Quoted identifier in filter expression with spaces",
520 "expression": "[?\"\\\\\" > `\"foo\"`]",
521 "result": null
522 }
523 ]
524 },
525 {
526 "comment": "Filter expression errors",
527 "given": {"type": "object"},
528 "cases": [
529 {
530 "expression": "bar.`\"anything\"`",
531 "error": "syntax"
532 },
533 {
534 "expression": "bar.baz.noexists.`\"literal\"`",
535 "error": "syntax"
536 },
537 {
538 "comment": "Literal wildcard projection",
539 "expression": "foo[*].`\"literal\"`",
540 "error": "syntax"
541 },
542 {
543 "expression": "foo[*].name.`\"literal\"`",
544 "error": "syntax"
545 },
546 {
547 "expression": "foo[].name.`\"literal\"`",
548 "error": "syntax"
549 },
550 {
551 "expression": "foo[].name.`\"literal\"`.`\"subliteral\"`",
552 "error": "syntax"
553 },
554 {
555 "comment": "Projecting a literal onto an empty list",
556 "expression": "foo[*].name.noexist.`\"literal\"`",
557 "error": "syntax"
558 },
559 {
560 "expression": "foo[].name.noexist.`\"literal\"`",
561 "error": "syntax"
562 },
563 {
564 "expression": "twolen[*].`\"foo\"`",
565 "error": "syntax"
566 },
567 {
568 "comment": "Two level projection of a literal",
569 "expression": "twolen[*].threelen[*].`\"bar\"`",
570 "error": "syntax"
571 },
572 {
573 "comment": "Two level flattened projection of a literal",
574 "expression": "twolen[].threelen[].`\"bar\"`",
575 "error": "syntax"
576 }
577 ]
578 },
579 {
580 "comment": "Identifiers",
581 "given": {"type": "object"},
582 "cases": [
583 {
584 "expression": "foo",
585 "result": null
586 },
587 {
588 "expression": "\"foo\"",
589 "result": null
590 },
591 {
592 "expression": "\"\\\\\"",
593 "result": null
594 }
595 ]
596 },
597 {
598 "comment": "Combined syntax",
599 "given": [],
600 "cases": [
601 {
602 "expression": "*||*|*|*",
603 "result": null
604 },
605 {
606 "expression": "*[]||[*]",
607 "result": []
608 },
609 {
610 "expression": "[*.*]",
611 "result": [null]
612 }
613 ]
614 }
615 ]
0 [
1 {
2 "given": {"foo": [{"✓": "✓"}, {"✓": "✗"}]},
3 "cases": [
4 {
5 "expression": "foo[].\"✓\"",
6 "result": ["✓", "✗"]
7 }
8 ]
9 },
10 {
11 "given": {"☯": true},
12 "cases": [
13 {
14 "expression": "\"☯\"",
15 "result": true
16 }
17 ]
18 },
19 {
20 "given": {"♪♫•*¨*•.¸¸❤¸¸.•*¨*•♫♪": true},
21 "cases": [
22 {
23 "expression": "\"♪♫•*¨*•.¸¸❤¸¸.•*¨*•♫♪\"",
24 "result": true
25 }
26 ]
27 },
28 {
29 "given": {"☃": true},
30 "cases": [
31 {
32 "expression": "\"☃\"",
33 "result": true
34 }
35 ]
36 }
37 ]
0 [{
1 "given": {
2 "foo": {
3 "bar": {
4 "baz": "val"
5 },
6 "other": {
7 "baz": "val"
8 },
9 "other2": {
10 "baz": "val"
11 },
12 "other3": {
13 "notbaz": ["a", "b", "c"]
14 },
15 "other4": {
16 "notbaz": ["a", "b", "c"]
17 },
18 "other5": {
19 "other": {
20 "a": 1,
21 "b": 1,
22 "c": 1
23 }
24 }
25 }
26 },
27 "cases": [
28 {
29 "expression": "foo.*.baz",
30 "result": ["val", "val", "val"]
31 },
32 {
33 "expression": "foo.bar.*",
34 "result": ["val"]
35 },
36 {
37 "expression": "foo.*.notbaz",
38 "result": [["a", "b", "c"], ["a", "b", "c"]]
39 },
40 {
41 "expression": "foo.*.notbaz[0]",
42 "result": ["a", "a"]
43 },
44 {
45 "expression": "foo.*.notbaz[-1]",
46 "result": ["c", "c"]
47 }
48 ]
49 }, {
50 "given": {
51 "foo": {
52 "first-1": {
53 "second-1": "val"
54 },
55 "first-2": {
56 "second-1": "val"
57 },
58 "first-3": {
59 "second-1": "val"
60 }
61 }
62 },
63 "cases": [
64 {
65 "expression": "foo.*",
66 "result": [{"second-1": "val"}, {"second-1": "val"},
67 {"second-1": "val"}]
68 },
69 {
70 "expression": "foo.*.*",
71 "result": [["val"], ["val"], ["val"]]
72 },
73 {
74 "expression": "foo.*.*.*",
75 "result": [[], [], []]
76 },
77 {
78 "expression": "foo.*.*.*.*",
79 "result": [[], [], []]
80 }
81 ]
82 }, {
83 "given": {
84 "foo": {
85 "bar": "one"
86 },
87 "other": {
88 "bar": "one"
89 },
90 "nomatch": {
91 "notbar": "three"
92 }
93 },
94 "cases": [
95 {
96 "expression": "*.bar",
97 "result": ["one", "one"]
98 }
99 ]
100 }, {
101 "given": {
102 "top1": {
103 "sub1": {"foo": "one"}
104 },
105 "top2": {
106 "sub1": {"foo": "one"}
107 }
108 },
109 "cases": [
110 {
111 "expression": "*",
112 "result": [{"sub1": {"foo": "one"}},
113 {"sub1": {"foo": "one"}}]
114 },
115 {
116 "expression": "*.sub1",
117 "result": [{"foo": "one"},
118 {"foo": "one"}]
119 },
120 {
121 "expression": "*.*",
122 "result": [[{"foo": "one"}],
123 [{"foo": "one"}]]
124 },
125 {
126 "expression": "*.*.foo[]",
127 "result": ["one", "one"]
128 },
129 {
130 "expression": "*.sub1.foo",
131 "result": ["one", "one"]
132 }
133 ]
134 },
135 {
136 "given":
137 {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]},
138 "cases": [
139 {
140 "expression": "foo[*].bar",
141 "result": ["one", "two", "three"]
142 },
143 {
144 "expression": "foo[*].notbar",
145 "result": ["four"]
146 }
147 ]
148 },
149 {
150 "given":
151 [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}],
152 "cases": [
153 {
154 "expression": "[*]",
155 "result": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]
156 },
157 {
158 "expression": "[*].bar",
159 "result": ["one", "two", "three"]
160 },
161 {
162 "expression": "[*].notbar",
163 "result": ["four"]
164 }
165 ]
166 },
167 {
168 "given": {
169 "foo": {
170 "bar": [
171 {"baz": ["one", "two", "three"]},
172 {"baz": ["four", "five", "six"]},
173 {"baz": ["seven", "eight", "nine"]}
174 ]
175 }
176 },
177 "cases": [
178 {
179 "expression": "foo.bar[*].baz",
180 "result": [["one", "two", "three"], ["four", "five", "six"], ["seven", "eight", "nine"]]
181 },
182 {
183 "expression": "foo.bar[*].baz[0]",
184 "result": ["one", "four", "seven"]
185 },
186 {
187 "expression": "foo.bar[*].baz[1]",
188 "result": ["two", "five", "eight"]
189 },
190 {
191 "expression": "foo.bar[*].baz[2]",
192 "result": ["three", "six", "nine"]
193 },
194 {
195 "expression": "foo.bar[*].baz[3]",
196 "result": []
197 }
198 ]
199 },
200 {
201 "given": {
202 "foo": {
203 "bar": [["one", "two"], ["three", "four"]]
204 }
205 },
206 "cases": [
207 {
208 "expression": "foo.bar[*]",
209 "result": [["one", "two"], ["three", "four"]]
210 },
211 {
212 "expression": "foo.bar[0]",
213 "result": ["one", "two"]
214 },
215 {
216 "expression": "foo.bar[0][0]",
217 "result": "one"
218 },
219 {
220 "expression": "foo.bar[0][0][0]",
221 "result": null
222 },
223 {
224 "expression": "foo.bar[0][0][0][0]",
225 "result": null
226 },
227 {
228 "expression": "foo[0][0]",
229 "result": null
230 }
231 ]
232 },
233 {
234 "given": {
235 "foo": [
236 {"bar": [{"kind": "basic"}, {"kind": "intermediate"}]},
237 {"bar": [{"kind": "advanced"}, {"kind": "expert"}]},
238 {"bar": "string"}
239 ]
240
241 },
242 "cases": [
243 {
244 "expression": "foo[*].bar[*].kind",
245 "result": [["basic", "intermediate"], ["advanced", "expert"]]
246 },
247 {
248 "expression": "foo[*].bar[0].kind",
249 "result": ["basic", "advanced"]
250 }
251 ]
252 },
253 {
254 "given": {
255 "foo": [
256 {"bar": {"kind": "basic"}},
257 {"bar": {"kind": "intermediate"}},
258 {"bar": {"kind": "advanced"}},
259 {"bar": {"kind": "expert"}},
260 {"bar": "string"}
261 ]
262 },
263 "cases": [
264 {
265 "expression": "foo[*].bar.kind",
266 "result": ["basic", "intermediate", "advanced", "expert"]
267 }
268 ]
269 },
270 {
271 "given": {
272 "foo": [{"bar": ["one", "two"]}, {"bar": ["three", "four"]}, {"bar": ["five"]}]
273 },
274 "cases": [
275 {
276 "expression": "foo[*].bar[0]",
277 "result": ["one", "three", "five"]
278 },
279 {
280 "expression": "foo[*].bar[1]",
281 "result": ["two", "four"]
282 },
283 {
284 "expression": "foo[*].bar[2]",
285 "result": []
286 }
287 ]
288 },
289 {
290 "given": {
291 "foo": [{"bar": []}, {"bar": []}, {"bar": []}]
292 },
293 "cases": [
294 {
295 "expression": "foo[*].bar[0]",
296 "result": []
297 }
298 ]
299 },
300 {
301 "given": {
302 "foo": [["one", "two"], ["three", "four"], ["five"]]
303 },
304 "cases": [
305 {
306 "expression": "foo[*][0]",
307 "result": ["one", "three", "five"]
308 },
309 {
310 "expression": "foo[*][1]",
311 "result": ["two", "four"]
312 }
313 ]
314 },
315 {
316 "given": {
317 "foo": [
318 [
319 ["one", "two"], ["three", "four"]
320 ], [
321 ["five", "six"], ["seven", "eight"]
322 ], [
323 ["nine"], ["ten"]
324 ]
325 ]
326 },
327 "cases": [
328 {
329 "expression": "foo[*][0]",
330 "result": [["one", "two"], ["five", "six"], ["nine"]]
331 },
332 {
333 "expression": "foo[*][1]",
334 "result": [["three", "four"], ["seven", "eight"], ["ten"]]
335 },
336 {
337 "expression": "foo[*][0][0]",
338 "result": ["one", "five", "nine"]
339 },
340 {
341 "expression": "foo[*][1][0]",
342 "result": ["three", "seven", "ten"]
343 },
344 {
345 "expression": "foo[*][0][1]",
346 "result": ["two", "six"]
347 },
348 {
349 "expression": "foo[*][1][1]",
350 "result": ["four", "eight"]
351 },
352 {
353 "expression": "foo[*][2]",
354 "result": []
355 },
356 {
357 "expression": "foo[*][2][2]",
358 "result": []
359 },
360 {
361 "expression": "bar[*]",
362 "result": null
363 },
364 {
365 "expression": "bar[*].baz[*]",
366 "result": null
367 }
368 ]
369 },
370 {
371 "given": {
372 "string": "string",
373 "hash": {"foo": "bar", "bar": "baz"},
374 "number": 23,
375 "nullvalue": null
376 },
377 "cases": [
378 {
379 "expression": "string[*]",
380 "result": null
381 },
382 {
383 "expression": "hash[*]",
384 "result": null
385 },
386 {
387 "expression": "number[*]",
388 "result": null
389 },
390 {
391 "expression": "nullvalue[*]",
392 "result": null
393 },
394 {
395 "expression": "string[*].foo",
396 "result": null
397 },
398 {
399 "expression": "hash[*].foo",
400 "result": null
401 },
402 {
403 "expression": "number[*].foo",
404 "result": null
405 },
406 {
407 "expression": "nullvalue[*].foo",
408 "result": null
409 },
410 {
411 "expression": "nullvalue[*].foo[*].bar",
412 "result": null
413 }
414 ]
415 },
416 {
417 "given": {
418 "string": "string",
419 "hash": {"foo": "val", "bar": "val"},
420 "number": 23,
421 "array": [1, 2, 3],
422 "nullvalue": null
423 },
424 "cases": [
425 {
426 "expression": "string.*",
427 "result": null
428 },
429 {
430 "expression": "hash.*",
431 "result": ["val", "val"]
432 },
433 {
434 "expression": "number.*",
435 "result": null
436 },
437 {
438 "expression": "array.*",
439 "result": null
440 },
441 {
442 "expression": "nullvalue.*",
443 "result": null
444 }
445 ]
446 },
447 {
448 "given": {
449 "a": [0, 1, 2],
450 "b": [0, 1, 2]
451 },
452 "cases": [
453 {
454 "expression": "*[0]",
455 "result": [0, 0]
456 }
457 ]
458 }
459 ]
0 #!/usr/bin/env python
1 """JMESPath compliance test runner.
2
3 This is a test runner that will run the JMESPath compliance tests against a
4 JMESPath executable.
5
6 Compliance tests are broken down into three components:
7
8 * The filename that contains the test. These are grouped by feature. The
9 * test group within the file. A test group can have multiple tests. The
10 * test case number. This is an individual test.
11
12 If "-t/--tests" is not provided then all compliance tests are run.
13 You can specify which tests to run using the "-t/--tests" argument.
14 Each test is specified as a comma separated list consisting of
15 "category,group_number,test_number". The group number and test number
16 are optional. If no test number if provided all tests within the group
17 are run. If no group number is given, all tests in that category
18 are run. To see a list of categories use the "-l/--list" option.
19 Multiple comma separates values are space separated.
20
21 When a test failure occurs, the category, group number, and test number are
22 displayed in the failure message. This allows you to quickly rerun a specific
23 test.
24
25 Examples
26 ========
27
28 These examples show how to run the compliance tests against the "jp"
29 executable.
30
31 Run all the basic tests::
32
33 jp-compliance -e jp -t basic
34
35 Run all the basic tests in group 1::
36
37 jp-compliance -e jp -t basic,1
38
39 Run the filter and function tests::
40
41 jp-compliance -e jp -t filters functions
42
43 Run the filter and function tests in group 1::
44
45 jp-compliance -e jp -t filters,1 functions,1
46
47 """
48
49 import sys
50 import argparse
51 import os
52 import subprocess
53 import json
54 import shlex
55
56
57 if sys.version_info[:2] == (2, 6):
58 import simplejson as json
59 from ordereddict import OrderedDict
60 else:
61 import json
62 from collections import OrderedDict
63
64
65 _abs = os.path.abspath
66 _dname = os.path.dirname
67 _pjoin = os.path.join
68 _splitext = os.path.splitext
69 _bname = os.path.basename
70
71
72 class ComplianceTestRunner(object):
73 TEST_DIR = _pjoin(_dname(_dname(_abs(__file__))), 'tests')
74
75 def __init__(self, exe=None, tests=None, test_dir=None):
76 if test_dir is None:
77 test_dir = self.TEST_DIR
78 self.test_dir = test_dir
79 self.tests = tests
80 self.jp_executable = exe
81 self.had_failures = False
82
83 def run_tests(self, stop_first_fail):
84 for test_case in self._test_cases():
85 if self._should_run(test_case):
86 test_passed = self._run_test(test_case)
87 if not test_passed:
88 self.had_failures = True
89 if stop_first_fail:
90 return
91
92 def _should_run(self, test_case):
93 if not self.tests:
94 return True
95 # Specific tests were called out so we need
96 # at least one thing in self.tests to match
97 # in order to run the test.
98 for allowed_test in self.tests:
99 if self._is_subset(allowed_test, test_case):
100 return True
101 return False
102
103 def _is_subset(self, subset, fullset):
104 for key in subset:
105 if subset[key] != fullset.get(key):
106 return False
107 return True
108
109 def _load_test_file(self, test_json_file):
110 with open(test_json_file) as f:
111 loaded_test = json.loads(f.read(), object_pairs_hook=OrderedDict)
112 return loaded_test
113
114 def _load_test_cases(self, filename, group_number, test_group):
115 given = test_group['given']
116 for i, case in enumerate(test_group['cases']):
117 current = {"given": given, "group_number": group_number,
118 "test_number": i,
119 'category': _splitext(_bname(filename))[0]}
120 current.update(case)
121 yield current
122
123 def _test_cases(self):
124 for test_json_file in self.get_compliance_test_files():
125 test_groups = self._load_test_file(test_json_file)
126 for i, test_group in enumerate(test_groups):
127 test_cases = self._load_test_cases(test_json_file,
128 i, test_group)
129 for test_case in test_cases:
130 yield test_case
131
132 def _run_test(self, test_case):
133 command = shlex.split(self.jp_executable)
134 command.append(test_case['expression'])
135 try:
136 process = subprocess.Popen(command, stdout=subprocess.PIPE,
137 stderr=subprocess.PIPE,
138 stdin=subprocess.PIPE)
139 except Exception, e:
140 raise RuntimeError('Could not execute test executable "%s": '
141 '%s' % (' '.join(command), e))
142 process.stdin.write(json.dumps(test_case['given']))
143 process.stdin.flush()
144 stdout, stderr = process.communicate()
145 if 'result' in test_case:
146 try:
147 actual = json.loads(stdout)
148 except:
149 actual = stdout
150 expected = test_case['result']
151 if not actual == expected:
152 self._show_failure(actual, test_case)
153 return False
154 else:
155 sys.stdout.write('.')
156 sys.stdout.flush()
157 return True
158 else:
159 # This is a test case for errors.
160 if process.returncode == 0:
161 self._show_failure_for_zero_rc(stderr, process.returncode,
162 test_case)
163 # For errors, we expect the error type on stderr.
164 if not self._passes_error_test(test_case['error'], stderr):
165 self._show_failure_for_error(stderr, test_case)
166 return False
167 else:
168 sys.stdout.write('.')
169 sys.stdout.flush()
170 return True
171
172 def _passes_error_test(self, error_type, stderr):
173 # Each tool will have different error messages, so we don't
174 # want to be super strict about what's allowed.
175 #
176 # Simplest case, the error_type appears in stderr, case insensitive.
177 if error_type not in stderr.lower():
178 return True
179 # Second case, all the parts of the error appear in stderr.
180 # Also case insensitive.
181 # An error_type will be '-' separated: invalid-type
182 # So a test can pass as long as "invalid" and "type" appear
183 # in stderr (case insensitive).
184 error_parts = error_type.split('-')
185 if all(p in stderr.lower() for p in error_parts):
186 return True
187 return False
188
189 def _show_failure(self, actual, test_case):
190 test_case['actual'] = json.dumps(actual)
191 test_case['result'] = json.dumps(test_case['result'])
192 test_case['given_js'] = json.dumps(test_case['given'])
193 failure_message = (
194 "\nFAIL {category},{group_number},{test_number}\n"
195 "The expression: {expression}\n"
196 "was suppose to give: {result}\n"
197 "for the JSON: {given_js}\n"
198 "but instead gave: {actual}\n"
199 ).format(**test_case)
200 sys.stdout.write(failure_message)
201
202 def _show_failure_for_zero_rc(self, stderr, return_code, test_case):
203 test_case['stderr'] = stderr
204 test_case['returncode'] = return_code
205 failure_message = (
206 "\nFAIL {category},{group_number},{test_number}\n"
207 "The expression: {expression}\n"
208 "was suppose to have non zero for error error: {error}\n"
209 "but instead gave rc of: {returncode}, stderr: \n{stderr}\n"
210 ).format(**test_case)
211 sys.stdout.write(failure_message)
212
213 def _show_failure_for_error(self, stderr, test_case):
214 test_case['stderr'] = stderr
215 failure_message = (
216 "\nFAIL {category},{group_number},{test_number}\n"
217 "The expression: {expression}\n"
218 "was suppose to emit the error: {error}\n"
219 "but instead gave: \n{stderr}\n"
220 ).format(**test_case)
221 sys.stdout.write(failure_message)
222
223 def get_compliance_test_files(self):
224 for root, _, filenames in os.walk(self.test_dir):
225 for filename in filenames:
226 if filename.endswith('.json'):
227 full_path = _pjoin(root, filename)
228 yield full_path
229
230
231 def display_available_tests(test_files):
232 print("Available test types:\n")
233 for filename in test_files:
234 no_extension = os.path.splitext(os.path.basename(filename))[0]
235 print(no_extension)
236
237
238 def test_spec(value):
239 parts = value.split(',')
240 if not parts:
241 raise ValueError("%s should be a comma separated list." % value)
242 spec = {'category': parts[0]}
243 if len(parts) == 2:
244 spec['group_number'] = int(parts[1])
245 elif len(parts) == 3:
246 spec['group_number'] = int(parts[1])
247 spec['test_number'] = int(parts[2])
248 return spec
249
250
251 def main():
252 parser = argparse.ArgumentParser(usage=__doc__)
253 parser.add_argument('-e', '--exe', help='The JMESPath executable to use.')
254 parser.add_argument('-t', '--tests', help=(
255 'The compliance tests to run. If this value is not provided, '
256 'then all compliance tests are run.'), type=test_spec, nargs='+')
257 parser.add_argument('-d', '--test-dir',
258 help='The directory containing compliance tests.')
259 parser.add_argument('-l', '--list', action="store_true",
260 help=('List the available compliance tests to run. '
261 'These values can then be used with the '
262 '"-t/--tests" argument. If this argument is '
263 'specified, no tests will actually be run.'))
264 parser.add_argument('-s', '--stop-first-fail', action='store_true',
265 help='Stop running tests after a single test fails.')
266 args = parser.parse_args()
267 runner = ComplianceTestRunner(args.exe, args.tests, args.test_dir)
268 if args.list:
269 display_available_tests(runner.get_compliance_test_files())
270 else:
271 try:
272 runner.run_tests(args.stop_first_fail)
273 except Exception, e:
274 sys.stderr.write(str(e))
275 sys.stderr.write("\n")
276 return 1
277 sys.stdout.write('\n')
278 if runner.had_failures:
279 sys.stdout.write('FAIL\n')
280 return 1
281 sys.stdout.write('OK\n')
282 return 0
283
284
285 if __name__ == '__main__':
286 sys.exit(main())