jp-0.1.3/000077500000000000000000000000001315600161600121235ustar00rootroot00000000000000jp-0.1.3/.gitignore000066400000000000000000000000131315600161600141050ustar00rootroot00000000000000jp build/* jp-0.1.3/.travis.yml000066400000000000000000000001111315600161600142250ustar00rootroot00000000000000language: go go: - 1.5 install: - go build jp.go script: make test jp-0.1.3/LICENSE000066400000000000000000000261351315600161600131370ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. jp-0.1.3/Makefile000066400000000000000000000017371315600161600135730ustar00rootroot00000000000000JP_VERSION="" help: @echo "Please use \`make ' where is one of" @echo " test to run all the tests." test: # CLI specific test cases. test/vendor/bats/libexec/bats test/cases # JMESPath compliance tests, using the jp-compliance # runner from github.com/jmespath/jmespath.test test/jp-compliance -d test/compliance/ -e ./jp # This will create/tag a new release locally, but not push anything. # The workflow for a new release is: # # 1. $ make new-release JP_VERSION=1.0.0 # < you'll get prompted for a few things > # # 2. $ git push origin master --tags # # # 3. Go to github and create a release # Right now, the last step isn't automated. You still # have to manually upload the release assets from build/. new-release: scripts/bump-version $(JP_VERSION) git add jp.go && git commit -m "Bumping version to $(JP_VERSION)" git tag -s -m "Tagging $(JP_VERSION) release" $(JP_VERSION) scripts/build-all-platforms scripts/sign-all .PHONY: help test jp-0.1.3/NOTICE000066400000000000000000000001401315600161600130220ustar00rootroot00000000000000jp: The JMESPath Command Line Interface Copyright 2015 James Saryerwinnie. All Rights Reserved. jp-0.1.3/README.md000066400000000000000000000212041315600161600134010ustar00rootroot00000000000000jp == The ``jp`` command is a command line interface to [JMESPath](http://jmespath.org), an expression language for manipulating JSON: ``` $ echo '{"foo": {"bar": ["a", "b", "c"]}}' | jp foo.bar[1] "b" ``` # Installing If you're a Mac user, you can install via homebrew from the JMESPath Homebrew tap: ``` brew tap jmespath/jmespath brew install jp ``` You can download prebuilt binaries if you prefer. Check the [Release page](https://github.com/jmespath/jp/releases) to download the latest ``jp`` executable. There are binaries available for Windows, Linux, Mac, FreeBSD. For example, to install version 0.1.2 on a 64 bit Linux environment use: ``` sudo wget https://github.com/jmespath/jp/releases/download/0.1.2/jp-linux-amd64 -O /usr/local/bin/jp \ && sudo chmod +x /usr/local/bin/jp ``` ## Building from Source If you have a Go environment installed you can also run: ``go get -u github.com/jmespath/jp`` to get the latest version of jmespath. If you have the repo checked out locally you can also just ``go build`` the project: ``` git clone git://github.com/jmespath/jp cd jp go build ./jp --help ``` And finally, if you have a go environment setup, but don't have a workspace/GOPATH configured, you can just run ``scripts/build-self-contained`` and it will build the ``jp`` executable for you: ``` git clone git://github.com/jmespath/jp cd jp scripts/build-self-contained ./jp --help ``` ## Cross platform binaries If you have a go 1.5 environment setup, you can build cross platform binaries by running ``scripts/build-all-platforms``. This will put executables in the ``build/`` directory and each executable will be named ``jp-``, e.g ``jp-darwin-amd64``, ``jp-linux-386``, etc. # Usage The most basic usage of ``jp`` is to accept input JSON data through stdin, apply the JMESPath expression you've provided as an argument to ``jp``, and print the resulting JSON data to stdout. ``` $ echo '{"key": "value"}' | jp key "value" $ echo '{"foo": {"bar": ["a", "b", "c"]}}' | jp foo.bar[1] "b" ``` Note the argument after ``jp``. This is a JMESPath expression. If you have no idea what that is, there's a [JMESPath Tutorial](http://jmespath.org/tutorial.html) that will take you through the JMESPath language. ## Input From a File In addition to this basic usage, there's also other ways to use ``jp``. First, instead of reading from stdin, you can provide a JSON file as input using the ``-f/--filename`` option: ``` $ echo '{"foo": {"bar": "baz"}}' > /tmp/input.json $ jp -f /tmp/input.json foo.bar "baz" ``` ## Unquoted Output [[Notice]] the output of the above command is ``"baz"``, that is, a double quote ``"``, followed by baz, followed by another a final double quote. This can be problematic if you're trying to use this with other commands that just want the string and *not* the quoted string. For example: ``` $ curl -s https://api.github.com/repos/golang/go/events | jp [0].actor.url "https://api.github.com/users/robpike" ``` Now let's suppose we want to then curl the above URL. Our first attempt might look something like this: ``` $ curl $(curl -s https://api.github.com/repos/golang/go/events | ./jp [0].actor.url) ``` And it would fail with: ``` curl: (1) Protocol "https not supported or disabled in libcurl ``` To fix this, we can use the ``-u/--unquoted`` option to specify that any result that is a string will be printed without quotes. Note that the result is not surrounded by double quotes: ``` $ curl -s https://api.github.com/repos/golang/go/events | jp --unquoted [0].actor.url https://api.github.com/users/robpike ``` If this is a common enough occurance for you, you can set the ``JP_UNQUOTED`` environment variable to make this the default behavior: ``` $ export JP_UNQUOTED=true $ curl -s https://api.github.com/repos/golang/go/events | jp --unquoted [0].actor.url https://api.github.com/users/robpike ``` Also keep in mind that this behavior only applies if the result of evaluating the JMESPath expression is a string: ``` $ echo '{"foo": ["bar", "baz"]}' | jp -u foo[0] bar # But -u does nothing here because the result is an array, not a string: $ echo '{"foo": ["bar", "baz"]}' | jp -u foo [ "bar", "baz" ] ``` You can also use the ``-u/--unquoted`` option along with the [join](http://jmespath.org/specification.html#join) function to create a list of strings that can be piped into other POSIX text tools. For example: ``` $ echo '{"foo": {"bar": ["a", "b", "c"]}}' | jp foo.bar [ "a", "b", "c" ] ``` Suppose we want to iterate over the 3 values in the list and run some bash code for each value. We can do this by running: ``` $ for name in $(echo '{"foo": {"bar": ["a", "b", "c"]}}' | \ jp -u 'join(`"\n"`, foo.bar)'); do echo "Processing: $name"; done Processing: a Processing: b Processing: c ``` ## Examples If you're new to the JMESPath language, or just want to see what the language is capable of, you can check out the [JMESPath tutorial](http://jmespath.org/tutorial.html) as well as the [JMESPath examples](http://jmespath.org/examples.html), which contains a curated set of JMESPath examples. But for now, here's a real world example. Let's say you wanted to see what the latest activity was with regard to the issue tracker for one of your github issues. Here's a simple way to do this: ``` $ curl -s https://api.github.com/repos/golang/go/events | jp \ "[?type=='IssuesEvent'].payload.\ {Title: issue.title, URL: issue.url, User: issue.user.login, Event: action}" [ { "Event": "opened", "Title": "release: cherry pick changes for 1.5 to release branch", "URL": "https://api.github.com/repos/golang/go/issues/12093", "User": "adg" }, { "Event": "closed", "Title": "fmt: x format verb for []byte fails in a recursive call to Fscanf from a scanln call in go1.5rc1", "URL": "https://api.github.com/repos/golang/go/issues/12090", "User": "hubslave" }, { "Event": "closed", "Title": "doc: release notes recommend wrong version of NaCl", "URL": "https://api.github.com/repos/golang/go/issues/12062", "User": "davecheney" }, { "Event": "opened", "Title": "cmd/godoc: show internal packages when explicitly requested", "URL": "https://api.github.com/repos/golang/go/issues/12092", "User": "jacobsa" } ] ``` Try it for your own repo, instead of ``/golang/go``, replace it with your own ``/owner/repo`` value. In words, this expression says: * For each element in the top level list, select only the elements where the ``type`` key is equal to the string ``IssueEvent`` * For each of those filtered elements select the ``payload`` hash. * Each each ``payload`` hash, we're going to create our own hash that has 4 keys: ``Title``, ``URL``, ``User``, ``Event``. The value for each of key is the result of evaluating these expressions in their respective order: ``issue.title``, ``issue.url``, ``issue.user.login``, ``action``. Ensure that if your expression has spaces you surround the expression in quotes, as shown in the example above. ## Testing The parsing and evaluation of JMESPath expression is done in the go-jmespath library, which is a dependency of this project. ``go-jmespath`` has extensive testing to ensure it is parsing and evaluating JMESPath expressions correctly. To ensure that there are no regressions between `go-imespath` and `jp`, the entire suite of [JMESPath compliance tests](https://github.com/jmespath/jmespath.test) are run against the `jp` executable. This repo also include CLI specific test that verify the command line options and output work as intended. You can run all of these tests for `jp` by running `make test`: ``` $ make test # CLI specific test cases. test/vendor/bats/libexec/bats test/cases ✓ Has valid help output ✓ Can display version ✓ Can search basic expression ✓ Can search subexpr expression ✓ Can read input from file ✓ Can print result unquoted ✓ Bad JMESPath expression has non zero rc - Large numbers are not printed with scientific notation (skipped) ✓ Can accept expression from file ✓ Can pretty print expr AST ✓ Can sort int array X tests, 0 failures, 1 skipped # JMESPath compliance tests, using the jp-compliance # runner from github.com/jmespath/jmespath.test test/jp-compliance -d test/compliance/ -e ./jp ............................................................ ............................................................ ............................................................ ............................................................ ............................................................ ............................................................ ............................................................ ............................................................ OK ``` jp-0.1.3/jp.go000066400000000000000000000056251315600161600130730ustar00rootroot00000000000000package main import ( "encoding/json" "fmt" "io/ioutil" "os" "github.com/jmespath/jp/Godeps/_workspace/src/github.com/codegangsta/cli" "github.com/jmespath/jp/Godeps/_workspace/src/github.com/jmespath/go-jmespath" ) const version = "0.1.3" func main() { app := cli.NewApp() app.Name = "jp" app.Version = version app.Usage = "jp [] " app.Author = "" app.Email = "" app.Flags = []cli.Flag{ cli.StringFlag{ Name: "filename, f", Usage: "Read input JSON from a file instead of stdin.", }, cli.StringFlag{ Name: "expr-file, e", Usage: "Read JMESPath expression from the specified file.", }, cli.BoolFlag{ Name: "unquoted, u", Usage: "If the final result is a string, it will be printed without quotes.", EnvVar: "JP_UNQUOTED", }, cli.BoolFlag{ Name: "ast", Usage: "Only print the AST of the parsed expression. Do not rely on this output, only useful for debugging purposes.", }, } app.Action = runMainAndExit app.Run(os.Args) } func runMainAndExit(c *cli.Context) { os.Exit(runMain(c)) } func errMsg(msg string, a ...interface{}) int { fmt.Fprintf(os.Stderr, msg, a...) fmt.Fprintln(os.Stderr) return 1 } func runMain(c *cli.Context) int { var expression string if c.String("expr-file") != "" { byteExpr, err := ioutil.ReadFile(c.String("expr-file")) expression = string(byteExpr) if err != nil { return errMsg("Error opening expression file: %s", err) } } else { if len(c.Args()) == 0 { return errMsg("Must provide at least one argument.") } expression = c.Args()[0] } if c.Bool("ast") { parser := jmespath.NewParser() parsed, err := parser.Parse(expression) if err != nil { if syntaxError, ok := err.(jmespath.SyntaxError); ok { return errMsg("%s\n%s\n", syntaxError, syntaxError.HighlightLocation()) } return errMsg("%s", err) } fmt.Println("") fmt.Printf("%s\n", parsed) return 0 } var input interface{} var jsonParser *json.Decoder if c.String("filename") != "" { f, err := os.Open(c.String("filename")) if err != nil { return errMsg("Error opening input file: %s", err) } jsonParser = json.NewDecoder(f) } else { jsonParser = json.NewDecoder(os.Stdin) } if err := jsonParser.Decode(&input); err != nil { errMsg("Error parsing input json: %s\n", err) return 2 } result, err := jmespath.Search(expression, input) if err != nil { if syntaxError, ok := err.(jmespath.SyntaxError); ok { return errMsg("%s\n%s\n", syntaxError, syntaxError.HighlightLocation()) } return errMsg("Error evaluating JMESPath expression: %s", err) } converted, isString := result.(string) if c.Bool("unquoted") && isString { os.Stdout.WriteString(converted) } else { toJSON, err := json.MarshalIndent(result, "", " ") if err != nil { errMsg("Error marshalling result to JSON: %s\n", err) return 3 } os.Stdout.Write(toJSON) } os.Stdout.WriteString("\n") return 0 } jp-0.1.3/scripts/000077500000000000000000000000001315600161600136125ustar00rootroot00000000000000jp-0.1.3/scripts/build-all-platforms000077500000000000000000000006761315600161600174230ustar00rootroot00000000000000#!/bin/bash go get ./... rm -rf ./build/jp-* for goos in darwin linux windows freebsd; do export GOOS="$goos" for goarch in 386 amd64; do export GOARCH="$goarch" echo "Building for $GOOS/$GOARCH" go build -v -o build/jp-$GOOS-$GOARCH 2>/dev/null done done # Also build for ARM7/linux export GOOS=linux export GOARCH=arm export GOARM=7 echo "Building for $GOOS/$GOARCH/$GOARM" go build -v -o build/jp-$GOOS-$GOARCH-arm$GOARM 2> /dev/null jp-0.1.3/scripts/build-self-contained000077500000000000000000000005451315600161600175340ustar00rootroot00000000000000#!/bin/bash # This is a self contained shell script for building jp without having to set # up GOPATH. You just need go installed. tempdir="$(mktemp -d -t jpbuild)" tempgopath="$tempdir/go" jppath="${tempgopath}/src/github.com/jmespath" fakerepo="$jppath/jp" mkdir -p $jppath ln -s "$(pwd)" "$jppath/jp" export GOPATH="$tempgopath" cd "$fakerepo" go build jp-0.1.3/scripts/bump-version000077500000000000000000000015011315600161600161630ustar00rootroot00000000000000#!/usr/bin/env python import sys import re import os version_line_re = re.compile(r'^const version = "\d+\.\d+\.\d+"') if len(sys.argv) == 1 or sys.argv[1] == '': print "Must provide jp version to bump to. (make new-release JP_VERSION=1.0.0)" sys.exit(1) new_version = sys.argv[1] source_file = os.path.join( os.path.dirname( os.path.dirname(os.path.abspath(__file__))), 'jp.go') assert os.path.isfile(source_file) with open(source_file, 'r') as f: contents = f.readlines() for i, line in enumerate(contents): if version_line_re.search(line) is not None: contents[i] = 'const version = "%s"\n' % new_version break else: sys.stderr.write("Could not find version string in %s\n" % source_file) with open(source_file, 'w') as f: for line in contents: f.write(line) jp-0.1.3/scripts/docker-build000077500000000000000000000005411315600161600161040ustar00rootroot00000000000000#!/bin/bash # Helper script to build "jp" on all supported platforms. # This script uses docker so you don't have to have the cross # platform golang environment setup. Just make sure you have docker # installed. The built executables will be in build/ docker run --rm -v "$PWD:/go/src/jp" -w /go/src/jp golang:1.4.2-cross scripts/build-all-platforms jp-0.1.3/scripts/sign-all000077500000000000000000000002451315600161600152470ustar00rootroot00000000000000#!/bin/bash set -e cd build shasum -a 256 ./jp-* > jp-checksums.sha256 gpg --clearsign --output jp-checksums.sha256.asc jp-checksums.sha256 rm jp-checksums.sha256 jp-0.1.3/test/000077500000000000000000000000001315600161600131025ustar00rootroot00000000000000jp-0.1.3/test/cases/000077500000000000000000000000001315600161600142005ustar00rootroot00000000000000jp-0.1.3/test/cases/search.bats000066400000000000000000000034101315600161600163160ustar00rootroot00000000000000@test "Has valid help output" { run ./jp --help [ "$status" -eq 0 ] echo $output | grep "\-\-filename" } @test "Can display version" { run ./jp --version [ "$status" -eq 0 ] } @test "Can search basic expression" { output=$(echo '{"foo": "bar"}' | ./jp foo) [ "$output" == "\"bar\"" ] } @test "Can search subexpr expression" { output=$(echo '{"foo": {"bar": "baz"}}' | ./jp foo.bar) [ "$output" == "\"baz\"" ] } @test "Can read input from file" { echo '{"foo": "bar"}' > "$BATS_TMPDIR/input.json" run ./jp -f "$BATS_TMPDIR/input.json" foo [ "$output" == "\"bar\"" ] } @test "Can print result unquoted" { output=$(echo '{"foo": "bar"}' | ./jp -u foo) [ "$output" == "bar" ] } @test "Bad JMESPath expression has non zero rc" { echo '{"foo": "bar"}' > "$BATS_TMPDIR/input.json" run ./jp -f "$BATS_TMPDIR/input.json" "bax[expre]ssion" [ "$status" -eq 1 ] } @test "Large numbers are not printed with scientific notation" { skip echo '{"foo": 47268765}' > "$BATS_TMPDIR/input.json" run ./jp -f "$BATS_TMPDIR/input.json" "foo" [ "$status" -eq 0 ] [ "$output" == "47268765" ] } @test "Can accept expression from file" { echo 'foo.bar' > "$BATS_TMPDIR/expr" echo '{"foo": {"bar": "baz"}}' > "$BATS_TMPDIR/input.json" run ./jp -u -f "$BATS_TMPDIR/input.json" -e "$BATS_TMPDIR/expr" [ "$output" == "baz" ] } @test "Can pretty print expr AST" { run ./jp --ast "foo" expected=' ASTField { value: "foo" }' echo "$output" echo "$expected" [ "$output" == "$expected" ] } @test "Can sort int array" { echo '[2,1,3,5,4]' > "$BATS_TMPDIR/input.json" echo "sort(@) | map(&to_string(@), @) | join('', @)" > "$BATS_TMPDIR/expr" run ./jp -u -f "$BATS_TMPDIR/input.json" -e "$BATS_TMPDIR/expr" [ "$status" -eq 0 ] [ "$output" == "12345" ] } jp-0.1.3/test/compliance/000077500000000000000000000000001315600161600152145ustar00rootroot00000000000000jp-0.1.3/test/compliance/basic.json000066400000000000000000000037671315600161600172050ustar00rootroot00000000000000[{ "given": {"foo": {"bar": {"baz": "correct"}}}, "cases": [ { "expression": "foo", "result": {"bar": {"baz": "correct"}} }, { "expression": "foo.bar", "result": {"baz": "correct"} }, { "expression": "foo.bar.baz", "result": "correct" }, { "expression": "foo\n.\nbar\n.baz", "result": "correct" }, { "expression": "foo.bar.baz.bad", "result": null }, { "expression": "foo.bar.bad", "result": null }, { "expression": "foo.bad", "result": null }, { "expression": "bad", "result": null }, { "expression": "bad.morebad.morebad", "result": null } ] }, { "given": {"foo": {"bar": ["one", "two", "three"]}}, "cases": [ { "expression": "foo", "result": {"bar": ["one", "two", "three"]} }, { "expression": "foo.bar", "result": ["one", "two", "three"] } ] }, { "given": ["one", "two", "three"], "cases": [ { "expression": "one", "result": null }, { "expression": "two", "result": null }, { "expression": "three", "result": null }, { "expression": "one.two", "result": null } ] }, { "given": {"foo": {"1": ["one", "two", "three"], "-1": "bar"}}, "cases": [ { "expression": "foo.\"1\"", "result": ["one", "two", "three"] }, { "expression": "foo.\"1\"[0]", "result": "one" }, { "expression": "foo.\"-1\"", "result": "bar" } ] } ] jp-0.1.3/test/compliance/boolean.json000066400000000000000000000115761315600161600175400ustar00rootroot00000000000000[ { "given": { "outer": { "foo": "foo", "bar": "bar", "baz": "baz" } }, "cases": [ { "expression": "outer.foo || outer.bar", "result": "foo" }, { "expression": "outer.foo||outer.bar", "result": "foo" }, { "expression": "outer.bar || outer.baz", "result": "bar" }, { "expression": "outer.bar||outer.baz", "result": "bar" }, { "expression": "outer.bad || outer.foo", "result": "foo" }, { "expression": "outer.bad||outer.foo", "result": "foo" }, { "expression": "outer.foo || outer.bad", "result": "foo" }, { "expression": "outer.foo||outer.bad", "result": "foo" }, { "expression": "outer.bad || outer.alsobad", "result": null }, { "expression": "outer.bad||outer.alsobad", "result": null } ] }, { "given": { "outer": { "foo": "foo", "bool": false, "empty_list": [], "empty_string": "" } }, "cases": [ { "expression": "outer.empty_string || outer.foo", "result": "foo" }, { "expression": "outer.nokey || outer.bool || outer.empty_list || outer.empty_string || outer.foo", "result": "foo" } ] }, { "given": { "True": true, "False": false, "Number": 5, "EmptyList": [], "Zero": 0 }, "cases": [ { "expression": "True && False", "result": false }, { "expression": "False && True", "result": false }, { "expression": "True && True", "result": true }, { "expression": "False && False", "result": false }, { "expression": "True && Number", "result": 5 }, { "expression": "Number && True", "result": true }, { "expression": "Number && False", "result": false }, { "expression": "Number && EmptyList", "result": [] }, { "expression": "Number && True", "result": true }, { "expression": "EmptyList && True", "result": [] }, { "expression": "EmptyList && False", "result": [] }, { "expression": "True || False", "result": true }, { "expression": "True || True", "result": true }, { "expression": "False || True", "result": true }, { "expression": "False || False", "result": false }, { "expression": "Number || EmptyList", "result": 5 }, { "expression": "Number || True", "result": 5 }, { "expression": "Number || True && False", "result": 5 }, { "expression": "(Number || True) && False", "result": false }, { "expression": "Number || (True && False)", "result": 5 }, { "expression": "!True", "result": false }, { "expression": "!False", "result": true }, { "expression": "!Number", "result": false }, { "expression": "!EmptyList", "result": true }, { "expression": "True && !False", "result": true }, { "expression": "True && !EmptyList", "result": true }, { "expression": "!False && !EmptyList", "result": true }, { "expression": "!(True && False)", "result": true }, { "expression": "!Zero", "result": false }, { "expression": "!!Zero", "result": true } ] }, { "given": { "one": 1, "two": 2, "three": 3 }, "cases": [ { "expression": "one < two", "result": true }, { "expression": "one <= two", "result": true }, { "expression": "one == one", "result": true }, { "expression": "one == two", "result": false }, { "expression": "one > two", "result": false }, { "expression": "one >= two", "result": false }, { "expression": "one != two", "result": true }, { "expression": "one < two && three > one", "result": true }, { "expression": "one < two || three > one", "result": true }, { "expression": "one < two || three < one", "result": true }, { "expression": "two < one || three < one", "result": false } ] } ] jp-0.1.3/test/compliance/current.json000066400000000000000000000011161315600161600175700ustar00rootroot00000000000000[ { "given": { "foo": [{"name": "a"}, {"name": "b"}], "bar": {"baz": "qux"} }, "cases": [ { "expression": "@", "result": { "foo": [{"name": "a"}, {"name": "b"}], "bar": {"baz": "qux"} } }, { "expression": "@.bar", "result": {"baz": "qux"} }, { "expression": "@.foo[0]", "result": {"name": "a"} } ] } ] jp-0.1.3/test/compliance/escape.json000066400000000000000000000021201315600161600173420ustar00rootroot00000000000000[{ "given": { "foo.bar": "dot", "foo bar": "space", "foo\nbar": "newline", "foo\"bar": "doublequote", "c:\\\\windows\\path": "windows", "/unix/path": "unix", "\"\"\"": "threequotes", "bar": {"baz": "qux"} }, "cases": [ { "expression": "\"foo.bar\"", "result": "dot" }, { "expression": "\"foo bar\"", "result": "space" }, { "expression": "\"foo\\nbar\"", "result": "newline" }, { "expression": "\"foo\\\"bar\"", "result": "doublequote" }, { "expression": "\"c:\\\\\\\\windows\\\\path\"", "result": "windows" }, { "expression": "\"/unix/path\"", "result": "unix" }, { "expression": "\"\\\"\\\"\\\"\"", "result": "threequotes" }, { "expression": "\"bar\".\"baz\"", "result": "qux" } ] }] jp-0.1.3/test/compliance/filters.json000066400000000000000000000313221315600161600175600ustar00rootroot00000000000000[ { "given": {"foo": [{"name": "a"}, {"name": "b"}]}, "cases": [ { "comment": "Matching a literal", "expression": "foo[?name == 'a']", "result": [{"name": "a"}] } ] }, { "given": {"foo": [0, 1], "bar": [2, 3]}, "cases": [ { "comment": "Matching a literal", "expression": "*[?[0] == `0`]", "result": [[], []] } ] }, { "given": {"foo": [{"first": "foo", "last": "bar"}, {"first": "foo", "last": "foo"}, {"first": "foo", "last": "baz"}]}, "cases": [ { "comment": "Matching an expression", "expression": "foo[?first == last]", "result": [{"first": "foo", "last": "foo"}] }, { "comment": "Verify projection created from filter", "expression": "foo[?first == last].first", "result": ["foo"] } ] }, { "given": {"foo": [{"age": 20}, {"age": 25}, {"age": 30}]}, "cases": [ { "comment": "Greater than with a number", "expression": "foo[?age > `25`]", "result": [{"age": 30}] }, { "expression": "foo[?age >= `25`]", "result": [{"age": 25}, {"age": 30}] }, { "comment": "Greater than with a number", "expression": "foo[?age > `30`]", "result": [] }, { "comment": "Greater than with a number", "expression": "foo[?age < `25`]", "result": [{"age": 20}] }, { "comment": "Greater than with a number", "expression": "foo[?age <= `25`]", "result": [{"age": 20}, {"age": 25}] }, { "comment": "Greater than with a number", "expression": "foo[?age < `20`]", "result": [] }, { "expression": "foo[?age == `20`]", "result": [{"age": 20}] }, { "expression": "foo[?age != `20`]", "result": [{"age": 25}, {"age": 30}] } ] }, { "given": {"foo": [{"top": {"name": "a"}}, {"top": {"name": "b"}}]}, "cases": [ { "comment": "Filter with subexpression", "expression": "foo[?top.name == 'a']", "result": [{"top": {"name": "a"}}] } ] }, { "given": {"foo": [{"top": {"first": "foo", "last": "bar"}}, {"top": {"first": "foo", "last": "foo"}}, {"top": {"first": "foo", "last": "baz"}}]}, "cases": [ { "comment": "Matching an expression", "expression": "foo[?top.first == top.last]", "result": [{"top": {"first": "foo", "last": "foo"}}] }, { "comment": "Matching a JSON array", "expression": "foo[?top == `{\"first\": \"foo\", \"last\": \"bar\"}`]", "result": [{"top": {"first": "foo", "last": "bar"}}] } ] }, { "given": {"foo": [ {"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}} ]}, "cases": [ { "expression": "foo[?key == `true`]", "result": [{"key": true}] }, { "expression": "foo[?key == `false`]", "result": [{"key": false}] }, { "expression": "foo[?key == `0`]", "result": [{"key": 0}] }, { "expression": "foo[?key == `1`]", "result": [{"key": 1}] }, { "expression": "foo[?key == `[0]`]", "result": [{"key": [0]}] }, { "expression": "foo[?key == `{\"bar\": [0]}`]", "result": [{"key": {"bar": [0]}}] }, { "expression": "foo[?key == `null`]", "result": [{"key": null}] }, { "expression": "foo[?key == `[1]`]", "result": [{"key": [1]}] }, { "expression": "foo[?key == `{\"a\":2}`]", "result": [{"key": {"a":2}}] }, { "expression": "foo[?`true` == key]", "result": [{"key": true}] }, { "expression": "foo[?`false` == key]", "result": [{"key": false}] }, { "expression": "foo[?`0` == key]", "result": [{"key": 0}] }, { "expression": "foo[?`1` == key]", "result": [{"key": 1}] }, { "expression": "foo[?`[0]` == key]", "result": [{"key": [0]}] }, { "expression": "foo[?`{\"bar\": [0]}` == key]", "result": [{"key": {"bar": [0]}}] }, { "expression": "foo[?`null` == key]", "result": [{"key": null}] }, { "expression": "foo[?`[1]` == key]", "result": [{"key": [1]}] }, { "expression": "foo[?`{\"a\":2}` == key]", "result": [{"key": {"a":2}}] }, { "expression": "foo[?key != `true`]", "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `false`]", "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `0`]", "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `1`]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `null`]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?key != `[1]`]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}] }, { "expression": "foo[?key != `{\"a\":2}`]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}] }, { "expression": "foo[?`true` != key]", "result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`false` != key]", "result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`0` != key]", "result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`1` != key]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`null` != key]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}] }, { "expression": "foo[?`[1]` != key]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}] }, { "expression": "foo[?`{\"a\":2}` != key]", "result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]}, {"key": {"bar": [0]}}, {"key": null}, {"key": [1]}] } ] }, { "given": {"reservations": [ {"instances": [ {"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 1, "bar": 2}, {"foo": 2, "bar": 1}]}]}, "cases": [ { "expression": "reservations[].instances[?bar==`1`]", "result": [[{"foo": 2, "bar": 1}]] }, { "expression": "reservations[*].instances[?bar==`1`]", "result": [[{"foo": 2, "bar": 1}]] }, { "expression": "reservations[].instances[?bar==`1`][]", "result": [{"foo": 2, "bar": 1}] } ] }, { "given": { "baz": "other", "foo": [ {"bar": 1}, {"bar": 2}, {"bar": 3}, {"bar": 4}, {"bar": 1, "baz": 2} ] }, "cases": [ { "expression": "foo[?bar==`1`].bar[0]", "result": [] } ] }, { "given": { "foo": [ {"a": 1, "b": {"c": "x"}}, {"a": 1, "b": {"c": "y"}}, {"a": 1, "b": {"c": "z"}}, {"a": 2, "b": {"c": "z"}}, {"a": 1, "baz": 2} ] }, "cases": [ { "expression": "foo[?a==`1`].b.c", "result": ["x", "y", "z"] } ] }, { "given": {"foo": [{"name": "a"}, {"name": "b"}, {"name": "c"}]}, "cases": [ { "comment": "Filter with or expression", "expression": "foo[?name == 'a' || name == 'b']", "result": [{"name": "a"}, {"name": "b"}] }, { "expression": "foo[?name == 'a' || name == 'e']", "result": [{"name": "a"}] }, { "expression": "foo[?name == 'a' || name == 'b' || name == 'c']", "result": [{"name": "a"}, {"name": "b"}, {"name": "c"}] } ] }, { "given": {"foo": [{"a": 1, "b": 2}, {"a": 1, "b": 3}]}, "cases": [ { "comment": "Filter with and expression", "expression": "foo[?a == `1` && b == `2`]", "result": [{"a": 1, "b": 2}] }, { "expression": "foo[?a == `1` && b == `4`]", "result": [] } ] }, { "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]}, "cases": [ { "comment": "Filter with Or and And expressions", "expression": "foo[?c == `3` || a == `1` && b == `4`]", "result": [{"a": 1, "b": 2, "c": 3}] }, { "expression": "foo[?b == `2` || a == `3` && b == `4`]", "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] }, { "expression": "foo[?a == `3` && b == `4` || b == `2`]", "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] }, { "expression": "foo[?(a == `3` && b == `4`) || b == `2`]", "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] }, { "expression": "foo[?((a == `3` && b == `4`)) || b == `2`]", "result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}] }, { "expression": "foo[?a == `3` && (b == `4` || b == `2`)]", "result": [{"a": 3, "b": 4}] }, { "expression": "foo[?a == `3` && ((b == `4` || b == `2`))]", "result": [{"a": 3, "b": 4}] } ] }, { "given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]}, "cases": [ { "comment": "Verify precedence of or/and expressions", "expression": "foo[?a == `1` || b ==`2` && c == `5`]", "result": [{"a": 1, "b": 2, "c": 3}] }, { "comment": "Parentheses can alter precedence", "expression": "foo[?(a == `1` || b ==`2`) && c == `5`]", "result": [] }, { "comment": "Not expressions combined with and/or", "expression": "foo[?!(a == `1` || b ==`2`)]", "result": [{"a": 3, "b": 4}] } ] }, { "given": { "foo": [ {"key": true}, {"key": false}, {"key": []}, {"key": {}}, {"key": [0]}, {"key": {"a": "b"}}, {"key": 0}, {"key": 1}, {"key": null}, {"notkey": true} ] }, "cases": [ { "comment": "Unary filter expression", "expression": "foo[?key]", "result": [ {"key": true}, {"key": [0]}, {"key": {"a": "b"}}, {"key": 0}, {"key": 1} ] }, { "comment": "Unary not filter expression", "expression": "foo[?!key]", "result": [ {"key": false}, {"key": []}, {"key": {}}, {"key": null}, {"notkey": true} ] }, { "comment": "Equality with null RHS", "expression": "foo[?key == `null`]", "result": [ {"key": null}, {"notkey": true} ] } ] }, { "given": { "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, "cases": [ { "comment": "Using @ in a filter expression", "expression": "foo[?@ < `5`]", "result": [0, 1, 2, 3, 4] }, { "comment": "Using @ in a filter expression", "expression": "foo[?`5` > @]", "result": [0, 1, 2, 3, 4] }, { "comment": "Using @ in a filter expression", "expression": "foo[?@ == @]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] } ] } ] jp-0.1.3/test/compliance/functions.json000066400000000000000000000432141315600161600201230ustar00rootroot00000000000000[{ "given": { "foo": -1, "zero": 0, "numbers": [-1, 3, 4, 5], "array": [-1, 3, 4, 5, "a", "100"], "strings": ["a", "b", "c"], "decimals": [1.01, 1.2, -1.5], "str": "Str", "false": false, "empty_list": [], "empty_hash": {}, "objects": {"foo": "bar", "bar": "baz"}, "null_key": null }, "cases": [ { "expression": "abs(foo)", "result": 1 }, { "expression": "abs(foo)", "result": 1 }, { "expression": "abs(str)", "error": "invalid-type" }, { "expression": "abs(array[1])", "result": 3 }, { "expression": "abs(array[1])", "result": 3 }, { "expression": "abs(`false`)", "error": "invalid-type" }, { "expression": "abs(`-24`)", "result": 24 }, { "expression": "abs(`-24`)", "result": 24 }, { "expression": "abs(`1`, `2`)", "error": "invalid-arity" }, { "expression": "abs()", "error": "invalid-arity" }, { "expression": "unknown_function(`1`, `2`)", "error": "unknown-function" }, { "expression": "avg(numbers)", "result": 2.75 }, { "expression": "avg(array)", "error": "invalid-type" }, { "expression": "avg('abc')", "error": "invalid-type" }, { "expression": "avg(foo)", "error": "invalid-type" }, { "expression": "avg(@)", "error": "invalid-type" }, { "expression": "avg(strings)", "error": "invalid-type" }, { "expression": "ceil(`1.2`)", "result": 2 }, { "expression": "ceil(decimals[0])", "result": 2 }, { "expression": "ceil(decimals[1])", "result": 2 }, { "expression": "ceil(decimals[2])", "result": -1 }, { "expression": "ceil('string')", "error": "invalid-type" }, { "expression": "contains('abc', 'a')", "result": true }, { "expression": "contains('abc', 'd')", "result": false }, { "expression": "contains(`false`, 'd')", "error": "invalid-type" }, { "expression": "contains(strings, 'a')", "result": true }, { "expression": "contains(decimals, `1.2`)", "result": true }, { "expression": "contains(decimals, `false`)", "result": false }, { "expression": "ends_with(str, 'r')", "result": true }, { "expression": "ends_with(str, 'tr')", "result": true }, { "expression": "ends_with(str, 'Str')", "result": true }, { "expression": "ends_with(str, 'SStr')", "result": false }, { "expression": "ends_with(str, 'foo')", "result": false }, { "expression": "ends_with(str, `0`)", "error": "invalid-type" }, { "expression": "floor(`1.2`)", "result": 1 }, { "expression": "floor('string')", "error": "invalid-type" }, { "expression": "floor(decimals[0])", "result": 1 }, { "expression": "floor(foo)", "result": -1 }, { "expression": "floor(str)", "error": "invalid-type" }, { "expression": "length('abc')", "result": 3 }, { "expression": "length('✓foo')", "result": 4 }, { "expression": "length('')", "result": 0 }, { "expression": "length(@)", "result": 12 }, { "expression": "length(strings[0])", "result": 1 }, { "expression": "length(str)", "result": 3 }, { "expression": "length(array)", "result": 6 }, { "expression": "length(objects)", "result": 2 }, { "expression": "length(`false`)", "error": "invalid-type" }, { "expression": "length(foo)", "error": "invalid-type" }, { "expression": "length(strings[0])", "result": 1 }, { "expression": "max(numbers)", "result": 5 }, { "expression": "max(decimals)", "result": 1.2 }, { "expression": "max(strings)", "result": "c" }, { "expression": "max(abc)", "error": "invalid-type" }, { "expression": "max(array)", "error": "invalid-type" }, { "expression": "max(decimals)", "result": 1.2 }, { "expression": "max(empty_list)", "result": null }, { "expression": "merge(`{}`)", "result": {} }, { "expression": "merge(`{}`, `{}`)", "result": {} }, { "expression": "merge(`{\"a\": 1}`, `{\"b\": 2}`)", "result": {"a": 1, "b": 2} }, { "expression": "merge(`{\"a\": 1}`, `{\"a\": 2}`)", "result": {"a": 2} }, { "expression": "merge(`{\"a\": 1, \"b\": 2}`, `{\"a\": 2, \"c\": 3}`, `{\"d\": 4}`)", "result": {"a": 2, "b": 2, "c": 3, "d": 4} }, { "expression": "min(numbers)", "result": -1 }, { "expression": "min(decimals)", "result": -1.5 }, { "expression": "min(abc)", "error": "invalid-type" }, { "expression": "min(array)", "error": "invalid-type" }, { "expression": "min(empty_list)", "result": null }, { "expression": "min(decimals)", "result": -1.5 }, { "expression": "min(strings)", "result": "a" }, { "expression": "type('abc')", "result": "string" }, { "expression": "type(`1.0`)", "result": "number" }, { "expression": "type(`2`)", "result": "number" }, { "expression": "type(`true`)", "result": "boolean" }, { "expression": "type(`false`)", "result": "boolean" }, { "expression": "type(`null`)", "result": "null" }, { "expression": "type(`[0]`)", "result": "array" }, { "expression": "type(`{\"a\": \"b\"}`)", "result": "object" }, { "expression": "type(@)", "result": "object" }, { "expression": "sort(keys(objects))", "result": ["bar", "foo"] }, { "expression": "keys(foo)", "error": "invalid-type" }, { "expression": "keys(strings)", "error": "invalid-type" }, { "expression": "keys(`false`)", "error": "invalid-type" }, { "expression": "sort(values(objects))", "result": ["bar", "baz"] }, { "expression": "keys(empty_hash)", "result": [] }, { "expression": "values(foo)", "error": "invalid-type" }, { "expression": "join(', ', strings)", "result": "a, b, c" }, { "expression": "join(', ', strings)", "result": "a, b, c" }, { "expression": "join(',', `[\"a\", \"b\"]`)", "result": "a,b" }, { "expression": "join(',', `[\"a\", 0]`)", "error": "invalid-type" }, { "expression": "join(', ', str)", "error": "invalid-type" }, { "expression": "join('|', strings)", "result": "a|b|c" }, { "expression": "join(`2`, strings)", "error": "invalid-type" }, { "expression": "join('|', decimals)", "error": "invalid-type" }, { "expression": "join('|', decimals[].to_string(@))", "result": "1.01|1.2|-1.5" }, { "expression": "join('|', empty_list)", "result": "" }, { "expression": "reverse(numbers)", "result": [5, 4, 3, -1] }, { "expression": "reverse(array)", "result": ["100", "a", 5, 4, 3, -1] }, { "expression": "reverse(`[]`)", "result": [] }, { "expression": "reverse('')", "result": "" }, { "expression": "reverse('hello world')", "result": "dlrow olleh" }, { "expression": "starts_with(str, 'S')", "result": true }, { "expression": "starts_with(str, 'St')", "result": true }, { "expression": "starts_with(str, 'Str')", "result": true }, { "expression": "starts_with(str, 'String')", "result": false }, { "expression": "starts_with(str, `0`)", "error": "invalid-type" }, { "expression": "sum(numbers)", "result": 11 }, { "expression": "sum(decimals)", "result": 0.71 }, { "expression": "sum(array)", "error": "invalid-type" }, { "expression": "sum(array[].to_number(@))", "result": 111 }, { "expression": "sum(`[]`)", "result": 0 }, { "expression": "to_array('foo')", "result": ["foo"] }, { "expression": "to_array(`0`)", "result": [0] }, { "expression": "to_array(objects)", "result": [{"foo": "bar", "bar": "baz"}] }, { "expression": "to_array(`[1, 2, 3]`)", "result": [1, 2, 3] }, { "expression": "to_array(false)", "result": [false] }, { "expression": "to_string('foo')", "result": "foo" }, { "expression": "to_string(`1.2`)", "result": "1.2" }, { "expression": "to_string(`[0, 1]`)", "result": "[0,1]" }, { "expression": "to_number('1.0')", "result": 1.0 }, { "expression": "to_number('1.1')", "result": 1.1 }, { "expression": "to_number('4')", "result": 4 }, { "expression": "to_number('notanumber')", "result": null }, { "expression": "to_number(`false`)", "result": null }, { "expression": "to_number(`null`)", "result": null }, { "expression": "to_number(`[0]`)", "result": null }, { "expression": "to_number(`{\"foo\": 0}`)", "result": null }, { "expression": "\"to_string\"(`1.0`)", "error": "syntax" }, { "expression": "sort(numbers)", "result": [-1, 3, 4, 5] }, { "expression": "sort(strings)", "result": ["a", "b", "c"] }, { "expression": "sort(decimals)", "result": [-1.5, 1.01, 1.2] }, { "expression": "sort(array)", "error": "invalid-type" }, { "expression": "sort(abc)", "error": "invalid-type" }, { "expression": "sort(empty_list)", "result": [] }, { "expression": "sort(@)", "error": "invalid-type" }, { "expression": "not_null(unknown_key, str)", "result": "Str" }, { "expression": "not_null(unknown_key, foo.bar, empty_list, str)", "result": [] }, { "expression": "not_null(unknown_key, null_key, empty_list, str)", "result": [] }, { "expression": "not_null(all, expressions, are_null)", "result": null }, { "expression": "not_null()", "error": "invalid-arity" }, { "description": "function projection on single arg function", "expression": "numbers[].to_string(@)", "result": ["-1", "3", "4", "5"] }, { "description": "function projection on single arg function", "expression": "array[].to_number(@)", "result": [-1, 3, 4, 5, 100] } ] }, { "given": { "foo": [ {"b": "b", "a": "a"}, {"c": "c", "b": "b"}, {"d": "d", "c": "c"}, {"e": "e", "d": "d"}, {"f": "f", "e": "e"} ] }, "cases": [ { "description": "function projection on variadic function", "expression": "foo[].not_null(f, e, d, c, b, a)", "result": ["b", "c", "d", "e", "f"] } ] }, { "given": { "people": [ {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, {"age": 30, "age_str": "30", "bool": true, "name": "c"}, {"age": 50, "age_str": "50", "bool": false, "name": "d"}, {"age": 10, "age_str": "10", "bool": true, "name": 3} ] }, "cases": [ { "description": "sort by field expression", "expression": "sort_by(people, &age)", "result": [ {"age": 10, "age_str": "10", "bool": true, "name": 3}, {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, {"age": 30, "age_str": "30", "bool": true, "name": "c"}, {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, {"age": 50, "age_str": "50", "bool": false, "name": "d"} ] }, { "expression": "sort_by(people, &age_str)", "result": [ {"age": 10, "age_str": "10", "bool": true, "name": 3}, {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, {"age": 30, "age_str": "30", "bool": true, "name": "c"}, {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, {"age": 50, "age_str": "50", "bool": false, "name": "d"} ] }, { "description": "sort by function expression", "expression": "sort_by(people, &to_number(age_str))", "result": [ {"age": 10, "age_str": "10", "bool": true, "name": 3}, {"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"}, {"age": 30, "age_str": "30", "bool": true, "name": "c"}, {"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"}, {"age": 50, "age_str": "50", "bool": false, "name": "d"} ] }, { "description": "function projection on sort_by function", "expression": "sort_by(people, &age)[].name", "result": [3, "a", "c", "b", "d"] }, { "expression": "sort_by(people, &extra)", "error": "invalid-type" }, { "expression": "sort_by(people, &bool)", "error": "invalid-type" }, { "expression": "sort_by(people, &name)", "error": "invalid-type" }, { "expression": "sort_by(people, name)", "error": "invalid-type" }, { "expression": "sort_by(people, &age)[].extra", "result": ["foo", "bar"] }, { "expression": "sort_by(`[]`, &age)", "result": [] }, { "expression": "max_by(people, &age)", "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} }, { "expression": "max_by(people, &age_str)", "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} }, { "expression": "max_by(people, &bool)", "error": "invalid-type" }, { "expression": "max_by(people, &extra)", "error": "invalid-type" }, { "expression": "max_by(people, &to_number(age_str))", "result": {"age": 50, "age_str": "50", "bool": false, "name": "d"} }, { "expression": "min_by(people, &age)", "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} }, { "expression": "min_by(people, &age_str)", "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} }, { "expression": "min_by(people, &bool)", "error": "invalid-type" }, { "expression": "min_by(people, &extra)", "error": "invalid-type" }, { "expression": "min_by(people, &to_number(age_str))", "result": {"age": 10, "age_str": "10", "bool": true, "name": 3} } ] }, { "given": { "people": [ {"age": 10, "order": "1"}, {"age": 10, "order": "2"}, {"age": 10, "order": "3"}, {"age": 10, "order": "4"}, {"age": 10, "order": "5"}, {"age": 10, "order": "6"}, {"age": 10, "order": "7"}, {"age": 10, "order": "8"}, {"age": 10, "order": "9"}, {"age": 10, "order": "10"}, {"age": 10, "order": "11"} ] }, "cases": [ { "description": "stable sort order", "expression": "sort_by(people, &age)", "result": [ {"age": 10, "order": "1"}, {"age": 10, "order": "2"}, {"age": 10, "order": "3"}, {"age": 10, "order": "4"}, {"age": 10, "order": "5"}, {"age": 10, "order": "6"}, {"age": 10, "order": "7"}, {"age": 10, "order": "8"}, {"age": 10, "order": "9"}, {"age": 10, "order": "10"}, {"age": 10, "order": "11"} ] } ] }, { "given": { "people": [ {"a": 10, "b": 1, "c": "z"}, {"a": 10, "b": 2, "c": null}, {"a": 10, "b": 3}, {"a": 10, "b": 4, "c": "z"}, {"a": 10, "b": 5, "c": null}, {"a": 10, "b": 6}, {"a": 10, "b": 7, "c": "z"}, {"a": 10, "b": 8, "c": null}, {"a": 10, "b": 9} ], "empty": [] }, "cases": [ { "expression": "map(&a, people)", "result": [10, 10, 10, 10, 10, 10, 10, 10, 10] }, { "expression": "map(&c, people)", "result": ["z", null, null, "z", null, null, "z", null, null] }, { "expression": "map(&a, badkey)", "error": "invalid-type" }, { "expression": "map(&foo, empty)", "result": [] } ] }, { "given": { "array": [ { "foo": {"bar": "yes1"} }, { "foo": {"bar": "yes2"} }, { "foo1": {"bar": "no"} } ]}, "cases": [ { "expression": "map(&foo.bar, array)", "result": ["yes1", "yes2", null] }, { "expression": "map(&foo1.bar, array)", "result": [null, null, "no"] }, { "expression": "map(&foo.bar.baz, array)", "result": [null, null, null] } ] }, { "given": { "array": [[1, 2, 3, [4]], [5, 6, 7, [8, 9]]] }, "cases": [ { "expression": "map(&[], array)", "result": [[1, 2, 3, 4], [5, 6, 7, 8, 9]] } ] } ] jp-0.1.3/test/compliance/identifiers.json000066400000000000000000000602321315600161600204170ustar00rootroot00000000000000[ { "given": { "__L": true }, "cases": [ { "expression": "__L", "result": true } ] }, { "given": { "!\r": true }, "cases": [ { "expression": "\"!\\r\"", "result": true } ] }, { "given": { "Y_1623": true }, "cases": [ { "expression": "Y_1623", "result": true } ] }, { "given": { "x": true }, "cases": [ { "expression": "x", "result": true } ] }, { "given": { "\tF\uCebb": true }, "cases": [ { "expression": "\"\\tF\\uCebb\"", "result": true } ] }, { "given": { " \t": true }, "cases": [ { "expression": "\" \\t\"", "result": true } ] }, { "given": { " ": true }, "cases": [ { "expression": "\" \"", "result": true } ] }, { "given": { "v2": true }, "cases": [ { "expression": "v2", "result": true } ] }, { "given": { "\t": true }, "cases": [ { "expression": "\"\\t\"", "result": true } ] }, { "given": { "_X": true }, "cases": [ { "expression": "_X", "result": true } ] }, { "given": { "\t4\ud9da\udd15": true }, "cases": [ { "expression": "\"\\t4\\ud9da\\udd15\"", "result": true } ] }, { "given": { "v24_W": true }, "cases": [ { "expression": "v24_W", "result": true } ] }, { "given": { "H": true }, "cases": [ { "expression": "\"H\"", "result": true } ] }, { "given": { "\f": true }, "cases": [ { "expression": "\"\\f\"", "result": true } ] }, { "given": { "E4": true }, "cases": [ { "expression": "\"E4\"", "result": true } ] }, { "given": { "!": true }, "cases": [ { "expression": "\"!\"", "result": true } ] }, { "given": { "tM": true }, "cases": [ { "expression": "tM", "result": true } ] }, { "given": { " [": true }, "cases": [ { "expression": "\" [\"", "result": true } ] }, { "given": { "R!": true }, "cases": [ { "expression": "\"R!\"", "result": true } ] }, { "given": { "_6W": true }, "cases": [ { "expression": "_6W", "result": true } ] }, { "given": { "\uaBA1\r": true }, "cases": [ { "expression": "\"\\uaBA1\\r\"", "result": true } ] }, { "given": { "tL7": true }, "cases": [ { "expression": "tL7", "result": true } ] }, { "given": { "<": true }, "cases": [ { "expression": "\">\"", "result": true } ] }, { "given": { "hvu": true }, "cases": [ { "expression": "hvu", "result": true } ] }, { "given": { "; !": true }, "cases": [ { "expression": "\"; !\"", "result": true } ] }, { "given": { "hU": true }, "cases": [ { "expression": "hU", "result": true } ] }, { "given": { "!I\n\/": true }, "cases": [ { "expression": "\"!I\\n\\/\"", "result": true } ] }, { "given": { "\uEEbF": true }, "cases": [ { "expression": "\"\\uEEbF\"", "result": true } ] }, { "given": { "U)\t": true }, "cases": [ { "expression": "\"U)\\t\"", "result": true } ] }, { "given": { "fa0_9": true }, "cases": [ { "expression": "fa0_9", "result": true } ] }, { "given": { "/": true }, "cases": [ { "expression": "\"/\"", "result": true } ] }, { "given": { "Gy": true }, "cases": [ { "expression": "Gy", "result": true } ] }, { "given": { "\b": true }, "cases": [ { "expression": "\"\\b\"", "result": true } ] }, { "given": { "<": true }, "cases": [ { "expression": "\"<\"", "result": true } ] }, { "given": { "\t": true }, "cases": [ { "expression": "\"\\t\"", "result": true } ] }, { "given": { "\t&\\\r": true }, "cases": [ { "expression": "\"\\t&\\\\\\r\"", "result": true } ] }, { "given": { "#": true }, "cases": [ { "expression": "\"#\"", "result": true } ] }, { "given": { "B__": true }, "cases": [ { "expression": "B__", "result": true } ] }, { "given": { "\nS \n": true }, "cases": [ { "expression": "\"\\nS \\n\"", "result": true } ] }, { "given": { "Bp": true }, "cases": [ { "expression": "Bp", "result": true } ] }, { "given": { ",\t;": true }, "cases": [ { "expression": "\",\\t;\"", "result": true } ] }, { "given": { "B_q": true }, "cases": [ { "expression": "B_q", "result": true } ] }, { "given": { "\/+\t\n\b!Z": true }, "cases": [ { "expression": "\"\\/+\\t\\n\\b!Z\"", "result": true } ] }, { "given": { "\udadd\udfc7\\ueFAc": true }, "cases": [ { "expression": "\"\udadd\udfc7\\\\ueFAc\"", "result": true } ] }, { "given": { ":\f": true }, "cases": [ { "expression": "\":\\f\"", "result": true } ] }, { "given": { "\/": true }, "cases": [ { "expression": "\"\\/\"", "result": true } ] }, { "given": { "_BW_6Hg_Gl": true }, "cases": [ { "expression": "_BW_6Hg_Gl", "result": true } ] }, { "given": { "\udbcf\udc02": true }, "cases": [ { "expression": "\"\udbcf\udc02\"", "result": true } ] }, { "given": { "zs1DC": true }, "cases": [ { "expression": "zs1DC", "result": true } ] }, { "given": { "__434": true }, "cases": [ { "expression": "__434", "result": true } ] }, { "given": { "\udb94\udd41": true }, "cases": [ { "expression": "\"\udb94\udd41\"", "result": true } ] }, { "given": { "Z_5": true }, "cases": [ { "expression": "Z_5", "result": true } ] }, { "given": { "z_M_": true }, "cases": [ { "expression": "z_M_", "result": true } ] }, { "given": { "YU_2": true }, "cases": [ { "expression": "YU_2", "result": true } ] }, { "given": { "_0": true }, "cases": [ { "expression": "_0", "result": true } ] }, { "given": { "\b+": true }, "cases": [ { "expression": "\"\\b+\"", "result": true } ] }, { "given": { "\"": true }, "cases": [ { "expression": "\"\\\"\"", "result": true } ] }, { "given": { "D7": true }, "cases": [ { "expression": "D7", "result": true } ] }, { "given": { "_62L": true }, "cases": [ { "expression": "_62L", "result": true } ] }, { "given": { "\tK\t": true }, "cases": [ { "expression": "\"\\tK\\t\"", "result": true } ] }, { "given": { "\n\\\f": true }, "cases": [ { "expression": "\"\\n\\\\\\f\"", "result": true } ] }, { "given": { "I_": true }, "cases": [ { "expression": "I_", "result": true } ] }, { "given": { "W_a0_": true }, "cases": [ { "expression": "W_a0_", "result": true } ] }, { "given": { "BQ": true }, "cases": [ { "expression": "BQ", "result": true } ] }, { "given": { "\tX$\uABBb": true }, "cases": [ { "expression": "\"\\tX$\\uABBb\"", "result": true } ] }, { "given": { "Z9": true }, "cases": [ { "expression": "Z9", "result": true } ] }, { "given": { "\b%\"\uda38\udd0f": true }, "cases": [ { "expression": "\"\\b%\\\"\uda38\udd0f\"", "result": true } ] }, { "given": { "_F": true }, "cases": [ { "expression": "_F", "result": true } ] }, { "given": { "!,": true }, "cases": [ { "expression": "\"!,\"", "result": true } ] }, { "given": { "\"!": true }, "cases": [ { "expression": "\"\\\"!\"", "result": true } ] }, { "given": { "Hh": true }, "cases": [ { "expression": "Hh", "result": true } ] }, { "given": { "&": true }, "cases": [ { "expression": "\"&\"", "result": true } ] }, { "given": { "9\r\\R": true }, "cases": [ { "expression": "\"9\\r\\\\R\"", "result": true } ] }, { "given": { "M_k": true }, "cases": [ { "expression": "M_k", "result": true } ] }, { "given": { "!\b\n\udb06\ude52\"\"": true }, "cases": [ { "expression": "\"!\\b\\n\udb06\ude52\\\"\\\"\"", "result": true } ] }, { "given": { "6": true }, "cases": [ { "expression": "\"6\"", "result": true } ] }, { "given": { "_7": true }, "cases": [ { "expression": "_7", "result": true } ] }, { "given": { "0": true }, "cases": [ { "expression": "\"0\"", "result": true } ] }, { "given": { "\\8\\": true }, "cases": [ { "expression": "\"\\\\8\\\\\"", "result": true } ] }, { "given": { "b7eo": true }, "cases": [ { "expression": "b7eo", "result": true } ] }, { "given": { "xIUo9": true }, "cases": [ { "expression": "xIUo9", "result": true } ] }, { "given": { "5": true }, "cases": [ { "expression": "\"5\"", "result": true } ] }, { "given": { "?": true }, "cases": [ { "expression": "\"?\"", "result": true } ] }, { "given": { "sU": true }, "cases": [ { "expression": "sU", "result": true } ] }, { "given": { "VH2&H\\\/": true }, "cases": [ { "expression": "\"VH2&H\\\\\\/\"", "result": true } ] }, { "given": { "_C": true }, "cases": [ { "expression": "_C", "result": true } ] }, { "given": { "_": true }, "cases": [ { "expression": "_", "result": true } ] }, { "given": { "<\t": true }, "cases": [ { "expression": "\"<\\t\"", "result": true } ] }, { "given": { "\uD834\uDD1E": true }, "cases": [ { "expression": "\"\\uD834\\uDD1E\"", "result": true } ] } ] jp-0.1.3/test/compliance/indices.json000066400000000000000000000211371315600161600175310ustar00rootroot00000000000000[{ "given": {"foo": {"bar": ["zero", "one", "two"]}}, "cases": [ { "expression": "foo.bar[0]", "result": "zero" }, { "expression": "foo.bar[1]", "result": "one" }, { "expression": "foo.bar[2]", "result": "two" }, { "expression": "foo.bar[3]", "result": null }, { "expression": "foo.bar[-1]", "result": "two" }, { "expression": "foo.bar[-2]", "result": "one" }, { "expression": "foo.bar[-3]", "result": "zero" }, { "expression": "foo.bar[-4]", "result": null } ] }, { "given": {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]}, "cases": [ { "expression": "foo.bar", "result": null }, { "expression": "foo[0].bar", "result": "one" }, { "expression": "foo[1].bar", "result": "two" }, { "expression": "foo[2].bar", "result": "three" }, { "expression": "foo[3].notbar", "result": "four" }, { "expression": "foo[3].bar", "result": null }, { "expression": "foo[0]", "result": {"bar": "one"} }, { "expression": "foo[1]", "result": {"bar": "two"} }, { "expression": "foo[2]", "result": {"bar": "three"} }, { "expression": "foo[3]", "result": {"notbar": "four"} }, { "expression": "foo[4]", "result": null } ] }, { "given": [ "one", "two", "three" ], "cases": [ { "expression": "[0]", "result": "one" }, { "expression": "[1]", "result": "two" }, { "expression": "[2]", "result": "three" }, { "expression": "[-1]", "result": "three" }, { "expression": "[-2]", "result": "two" }, { "expression": "[-3]", "result": "one" } ] }, { "given": {"reservations": [ {"instances": [{"foo": 1}, {"foo": 2}]} ]}, "cases": [ { "expression": "reservations[].instances[].foo", "result": [1, 2] }, { "expression": "reservations[].instances[].bar", "result": [] }, { "expression": "reservations[].notinstances[].foo", "result": [] }, { "expression": "reservations[].notinstances[].foo", "result": [] } ] }, { "given": {"reservations": [{ "instances": [ {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}, {"foo": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]}, {"foo": "bar"}, {"notfoo": [{"bar": 20}, {"bar": 21}, {"notbar": [7]}, {"bar": 22}]}, {"bar": [{"baz": [1]}, {"baz": [2]}, {"baz": [3]}, {"baz": [4]}]}, {"baz": [{"baz": [1, 2]}, {"baz": []}, {"baz": []}, {"baz": [3, 4]}]}, {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]} ], "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]} }, { "instances": [ {"a": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}, {"b": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]}, {"c": "bar"}, {"notfoo": [{"bar": 23}, {"bar": 24}, {"notbar": [7]}, {"bar": 25}]}, {"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]} ], "otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]} } ]}, "cases": [ { "expression": "reservations[].instances[].foo[].bar", "result": [1, 2, 4, 5, 6, 8] }, { "expression": "reservations[].instances[].foo[].baz", "result": [] }, { "expression": "reservations[].instances[].notfoo[].bar", "result": [20, 21, 22, 23, 24, 25] }, { "expression": "reservations[].instances[].notfoo[].notbar", "result": [[7], [7]] }, { "expression": "reservations[].notinstances[].foo", "result": [] }, { "expression": "reservations[].instances[].foo[].notbar", "result": [3, [7]] }, { "expression": "reservations[].instances[].bar[].baz", "result": [[1], [2], [3], [4]] }, { "expression": "reservations[].instances[].baz[].baz", "result": [[1, 2], [], [], [3, 4]] }, { "expression": "reservations[].instances[].qux[].baz", "result": [[], [1, 2, 3], [4], [], [], [1, 2, 3], [4], []] }, { "expression": "reservations[].instances[].qux[].baz[]", "result": [1, 2, 3, 4, 1, 2, 3, 4] } ] }, { "given": { "foo": [ [["one", "two"], ["three", "four"]], [["five", "six"], ["seven", "eight"]], [["nine"], ["ten"]] ] }, "cases": [ { "expression": "foo[]", "result": [["one", "two"], ["three", "four"], ["five", "six"], ["seven", "eight"], ["nine"], ["ten"]] }, { "expression": "foo[][0]", "result": ["one", "three", "five", "seven", "nine", "ten"] }, { "expression": "foo[][1]", "result": ["two", "four", "six", "eight"] }, { "expression": "foo[][0][0]", "result": [] }, { "expression": "foo[][2][2]", "result": [] }, { "expression": "foo[][0][0][100]", "result": [] } ] }, { "given": { "foo": [{ "bar": [ { "qux": 2, "baz": 1 }, { "qux": 4, "baz": 3 } ] }, { "bar": [ { "qux": 6, "baz": 5 }, { "qux": 8, "baz": 7 } ] } ] }, "cases": [ { "expression": "foo", "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] }, { "expression": "foo[]", "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] }, { "expression": "foo[].bar", "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}], [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]] }, { "expression": "foo[].bar[]", "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}, {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}] }, { "expression": "foo[].bar[].baz", "result": [1, 3, 5, 7] } ] }, { "given": { "string": "string", "hash": {"foo": "bar", "bar": "baz"}, "number": 23, "nullvalue": null }, "cases": [ { "expression": "string[]", "result": null }, { "expression": "hash[]", "result": null }, { "expression": "number[]", "result": null }, { "expression": "nullvalue[]", "result": null }, { "expression": "string[].foo", "result": null }, { "expression": "hash[].foo", "result": null }, { "expression": "number[].foo", "result": null }, { "expression": "nullvalue[].foo", "result": null }, { "expression": "nullvalue[].foo[].bar", "result": null } ] } ] jp-0.1.3/test/compliance/literal.json000066400000000000000000000113631315600161600175470ustar00rootroot00000000000000[ { "given": { "foo": [{"name": "a"}, {"name": "b"}], "bar": {"baz": "qux"} }, "cases": [ { "expression": "`\"foo\"`", "result": "foo" }, { "comment": "Interpret escaped unicode.", "expression": "`\"\\u03a6\"`", "result": "Φ" }, { "expression": "`\"✓\"`", "result": "✓" }, { "expression": "`[1, 2, 3]`", "result": [1, 2, 3] }, { "expression": "`{\"a\": \"b\"}`", "result": {"a": "b"} }, { "expression": "`true`", "result": true }, { "expression": "`false`", "result": false }, { "expression": "`null`", "result": null }, { "expression": "`0`", "result": 0 }, { "expression": "`1`", "result": 1 }, { "expression": "`2`", "result": 2 }, { "expression": "`3`", "result": 3 }, { "expression": "`4`", "result": 4 }, { "expression": "`5`", "result": 5 }, { "expression": "`6`", "result": 6 }, { "expression": "`7`", "result": 7 }, { "expression": "`8`", "result": 8 }, { "expression": "`9`", "result": 9 }, { "comment": "Escaping a backtick in quotes", "expression": "`\"foo\\`bar\"`", "result": "foo`bar" }, { "comment": "Double quote in literal", "expression": "`\"foo\\\"bar\"`", "result": "foo\"bar" }, { "expression": "`\"1\\`\"`", "result": "1`" }, { "comment": "Multiple literal expressions with escapes", "expression": "`\"\\\\\"`.{a:`\"b\"`}", "result": {"a": "b"} }, { "comment": "literal . identifier", "expression": "`{\"a\": \"b\"}`.a", "result": "b" }, { "comment": "literal . identifier . identifier", "expression": "`{\"a\": {\"b\": \"c\"}}`.a.b", "result": "c" }, { "comment": "literal . identifier bracket-expr", "expression": "`[0, 1, 2]`[1]", "result": 1 } ] }, { "comment": "Literals", "given": {"type": "object"}, "cases": [ { "comment": "Literal with leading whitespace", "expression": "` {\"foo\": true}`", "result": {"foo": true} }, { "comment": "Literal with trailing whitespace", "expression": "`{\"foo\": true} `", "result": {"foo": true} }, { "comment": "Literal on RHS of subexpr not allowed", "expression": "foo.`\"bar\"`", "error": "syntax" } ] }, { "comment": "Raw String Literals", "given": {}, "cases": [ { "expression": "'foo'", "result": "foo" }, { "expression": "' foo '", "result": " foo " }, { "expression": "'0'", "result": "0" }, { "expression": "'newline\n'", "result": "newline\n" }, { "expression": "'\n'", "result": "\n" }, { "expression": "'✓'", "result": "✓" }, { "expression": "'𝄞'", "result": "𝄞" }, { "expression": "' [foo] '", "result": " [foo] " }, { "expression": "'[foo]'", "result": "[foo]" }, { "comment": "Do not interpret escaped unicode.", "expression": "'\\u03a6'", "result": "\\u03a6" }, { "comment": "Can escape the single quote", "expression": "'foo\\'bar'", "result": "foo'bar" } ] } ] jp-0.1.3/test/compliance/multiselect.json000066400000000000000000000240331315600161600204430ustar00rootroot00000000000000[{ "given": { "foo": { "bar": "bar", "baz": "baz", "qux": "qux", "nested": { "one": { "a": "first", "b": "second", "c": "third" }, "two": { "a": "first", "b": "second", "c": "third" }, "three": { "a": "first", "b": "second", "c": {"inner": "third"} } } }, "bar": 1, "baz": 2, "qux\"": 3 }, "cases": [ { "expression": "foo.{bar: bar}", "result": {"bar": "bar"} }, { "expression": "foo.{\"bar\": bar}", "result": {"bar": "bar"} }, { "expression": "foo.{\"foo.bar\": bar}", "result": {"foo.bar": "bar"} }, { "expression": "foo.{bar: bar, baz: baz}", "result": {"bar": "bar", "baz": "baz"} }, { "expression": "foo.{\"bar\": bar, \"baz\": baz}", "result": {"bar": "bar", "baz": "baz"} }, { "expression": "{\"baz\": baz, \"qux\\\"\": \"qux\\\"\"}", "result": {"baz": 2, "qux\"": 3} }, { "expression": "foo.{bar:bar,baz:baz}", "result": {"bar": "bar", "baz": "baz"} }, { "expression": "foo.{bar: bar,qux: qux}", "result": {"bar": "bar", "qux": "qux"} }, { "expression": "foo.{bar: bar, noexist: noexist}", "result": {"bar": "bar", "noexist": null} }, { "expression": "foo.{noexist: noexist, alsonoexist: alsonoexist}", "result": {"noexist": null, "alsonoexist": null} }, { "expression": "foo.badkey.{nokey: nokey, alsonokey: alsonokey}", "result": null }, { "expression": "foo.nested.*.{a: a,b: b}", "result": [{"a": "first", "b": "second"}, {"a": "first", "b": "second"}, {"a": "first", "b": "second"}] }, { "expression": "foo.nested.three.{a: a, cinner: c.inner}", "result": {"a": "first", "cinner": "third"} }, { "expression": "foo.nested.three.{a: a, c: c.inner.bad.key}", "result": {"a": "first", "c": null} }, { "expression": "foo.{a: nested.one.a, b: nested.two.b}", "result": {"a": "first", "b": "second"} }, { "expression": "{bar: bar, baz: baz}", "result": {"bar": 1, "baz": 2} }, { "expression": "{bar: bar}", "result": {"bar": 1} }, { "expression": "{otherkey: bar}", "result": {"otherkey": 1} }, { "expression": "{no: no, exist: exist}", "result": {"no": null, "exist": null} }, { "expression": "foo.[bar]", "result": ["bar"] }, { "expression": "foo.[bar,baz]", "result": ["bar", "baz"] }, { "expression": "foo.[bar,qux]", "result": ["bar", "qux"] }, { "expression": "foo.[bar,noexist]", "result": ["bar", null] }, { "expression": "foo.[noexist,alsonoexist]", "result": [null, null] } ] }, { "given": { "foo": {"bar": 1, "baz": [2, 3, 4]} }, "cases": [ { "expression": "foo.{bar:bar,baz:baz}", "result": {"bar": 1, "baz": [2, 3, 4]} }, { "expression": "foo.[bar,baz[0]]", "result": [1, 2] }, { "expression": "foo.[bar,baz[1]]", "result": [1, 3] }, { "expression": "foo.[bar,baz[2]]", "result": [1, 4] }, { "expression": "foo.[bar,baz[3]]", "result": [1, null] }, { "expression": "foo.[bar[0],baz[3]]", "result": [null, null] } ] }, { "given": { "foo": {"bar": 1, "baz": 2} }, "cases": [ { "expression": "foo.{bar: bar, baz: baz}", "result": {"bar": 1, "baz": 2} }, { "expression": "foo.[bar,baz]", "result": [1, 2] } ] }, { "given": { "foo": { "bar": {"baz": [{"common": "first", "one": 1}, {"common": "second", "two": 2}]}, "ignoreme": 1, "includeme": true } }, "cases": [ { "expression": "foo.{bar: bar.baz[1],includeme: includeme}", "result": {"bar": {"common": "second", "two": 2}, "includeme": true} }, { "expression": "foo.{\"bar.baz.two\": bar.baz[1].two, includeme: includeme}", "result": {"bar.baz.two": 2, "includeme": true} }, { "expression": "foo.[includeme, bar.baz[*].common]", "result": [true, ["first", "second"]] }, { "expression": "foo.[includeme, bar.baz[*].none]", "result": [true, []] }, { "expression": "foo.[includeme, bar.baz[].common]", "result": [true, ["first", "second"]] } ] }, { "given": { "reservations": [{ "instances": [ {"id": "id1", "name": "first"}, {"id": "id2", "name": "second"} ]}, { "instances": [ {"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"} ]} ]}, "cases": [ { "expression": "reservations[*].instances[*].{id: id, name: name}", "result": [[{"id": "id1", "name": "first"}, {"id": "id2", "name": "second"}], [{"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"}]] }, { "expression": "reservations[].instances[].{id: id, name: name}", "result": [{"id": "id1", "name": "first"}, {"id": "id2", "name": "second"}, {"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"}] }, { "expression": "reservations[].instances[].[id, name]", "result": [["id1", "first"], ["id2", "second"], ["id3", "third"], ["id4", "fourth"]] } ] }, { "given": { "foo": [{ "bar": [ { "qux": 2, "baz": 1 }, { "qux": 4, "baz": 3 } ] }, { "bar": [ { "qux": 6, "baz": 5 }, { "qux": 8, "baz": 7 } ] } ] }, "cases": [ { "expression": "foo", "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] }, { "expression": "foo[]", "result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]}, {"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}] }, { "expression": "foo[].bar", "result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}], [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]] }, { "expression": "foo[].bar[]", "result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}, {"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}] }, { "expression": "foo[].bar[].[baz, qux]", "result": [[1, 2], [3, 4], [5, 6], [7, 8]] }, { "expression": "foo[].bar[].[baz]", "result": [[1], [3], [5], [7]] }, { "expression": "foo[].bar[].[baz, qux][]", "result": [1, 2, 3, 4, 5, 6, 7, 8] } ] }, { "given": { "foo": { "baz": [ { "bar": "abc" }, { "bar": "def" } ], "qux": ["zero"] } }, "cases": [ { "expression": "foo.[baz[*].bar, qux[0]]", "result": [["abc", "def"], "zero"] } ] }, { "given": { "foo": { "baz": [ { "bar": "a", "bam": "b", "boo": "c" }, { "bar": "d", "bam": "e", "boo": "f" } ], "qux": ["zero"] } }, "cases": [ { "expression": "foo.[baz[*].[bar, boo], qux[0]]", "result": [[["a", "c" ], ["d", "f" ]], "zero"] } ] }, { "given": { "foo": { "baz": [ { "bar": "a", "bam": "b", "boo": "c" }, { "bar": "d", "bam": "e", "boo": "f" } ], "qux": ["zero"] } }, "cases": [ { "expression": "foo.[baz[*].not_there || baz[*].bar, qux[0]]", "result": [["a", "d"], "zero"] } ] }, { "given": {"type": "object"}, "cases": [ { "comment": "Nested multiselect", "expression": "[[*],*]", "result": [null, ["object"]] } ] }, { "given": [], "cases": [ { "comment": "Nested multiselect", "expression": "[[*]]", "result": [[]] } ] } ] jp-0.1.3/test/compliance/pipe.json000066400000000000000000000044511315600161600170500ustar00rootroot00000000000000[{ "given": { "foo": { "bar": { "baz": "subkey" }, "other": { "baz": "subkey" }, "other2": { "baz": "subkey" }, "other3": { "notbaz": ["a", "b", "c"] }, "other4": { "notbaz": ["a", "b", "c"] } } }, "cases": [ { "expression": "foo.*.baz | [0]", "result": "subkey" }, { "expression": "foo.*.baz | [1]", "result": "subkey" }, { "expression": "foo.*.baz | [2]", "result": "subkey" }, { "expression": "foo.bar.* | [0]", "result": "subkey" }, { "expression": "foo.*.notbaz | [*]", "result": [["a", "b", "c"], ["a", "b", "c"]] }, { "expression": "{\"a\": foo.bar, \"b\": foo.other} | *.baz", "result": ["subkey", "subkey"] } ] }, { "given": { "foo": { "bar": { "baz": "one" }, "other": { "baz": "two" }, "other2": { "baz": "three" }, "other3": { "notbaz": ["a", "b", "c"] }, "other4": { "notbaz": ["d", "e", "f"] } } }, "cases": [ { "expression": "foo | bar", "result": {"baz": "one"} }, { "expression": "foo | bar | baz", "result": "one" }, { "expression": "foo|bar| baz", "result": "one" }, { "expression": "not_there | [0]", "result": null }, { "expression": "not_there | [0]", "result": null }, { "expression": "[foo.bar, foo.other] | [0]", "result": {"baz": "one"} }, { "expression": "{\"a\": foo.bar, \"b\": foo.other} | a", "result": {"baz": "one"} }, { "expression": "{\"a\": foo.bar, \"b\": foo.other} | b", "result": {"baz": "two"} }, { "expression": "foo.bam || foo.bar | baz", "result": "one" }, { "expression": "foo | not_there || bar", "result": {"baz": "one"} } ] }, { "given": { "foo": [{ "bar": [{ "baz": "one" }, { "baz": "two" }] }, { "bar": [{ "baz": "three" }, { "baz": "four" }] }] }, "cases": [ { "expression": "foo[*].bar[*] | [0][0]", "result": {"baz": "one"} } ] }] jp-0.1.3/test/compliance/slice.json000066400000000000000000000070671315600161600172200ustar00rootroot00000000000000[{ "given": { "foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "bar": { "baz": 1 } }, "cases": [ { "expression": "bar[0:10]", "result": null }, { "expression": "foo[0:10:1]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0:10]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0:10:]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0::1]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0::]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[0:]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[:10:1]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[::1]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[:10:]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[::]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[:]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[1:9]", "result": [1, 2, 3, 4, 5, 6, 7, 8] }, { "expression": "foo[0:10:2]", "result": [0, 2, 4, 6, 8] }, { "expression": "foo[5:]", "result": [5, 6, 7, 8, 9] }, { "expression": "foo[5::2]", "result": [5, 7, 9] }, { "expression": "foo[::2]", "result": [0, 2, 4, 6, 8] }, { "expression": "foo[::-1]", "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] }, { "expression": "foo[1::2]", "result": [1, 3, 5, 7, 9] }, { "expression": "foo[10:0:-1]", "result": [9, 8, 7, 6, 5, 4, 3, 2, 1] }, { "expression": "foo[10:5:-1]", "result": [9, 8, 7, 6] }, { "expression": "foo[8:2:-2]", "result": [8, 6, 4] }, { "expression": "foo[0:20]", "result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }, { "expression": "foo[10:-20:-1]", "result": [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] }, { "expression": "foo[10:-20]", "result": [] }, { "expression": "foo[-4:-1]", "result": [6, 7, 8] }, { "expression": "foo[:-5:-1]", "result": [9, 8, 7, 6] }, { "expression": "foo[8:2:0]", "error": "invalid-value" }, { "expression": "foo[8:2:0:1]", "error": "syntax" }, { "expression": "foo[8:2&]", "error": "syntax" }, { "expression": "foo[2:a:3]", "error": "syntax" } ] }, { "given": { "foo": [{"a": 1}, {"a": 2}, {"a": 3}], "bar": [{"a": {"b": 1}}, {"a": {"b": 2}}, {"a": {"b": 3}}], "baz": 50 }, "cases": [ { "expression": "foo[:2].a", "result": [1, 2] }, { "expression": "foo[:2].b", "result": [] }, { "expression": "foo[:2].a.b", "result": [] }, { "expression": "bar[::-1].a.b", "result": [3, 2, 1] }, { "expression": "bar[:2].a.b", "result": [1, 2] }, { "expression": "baz[:2].a", "result": null } ] }, { "given": [{"a": 1}, {"a": 2}, {"a": 3}], "cases": [ { "expression": "[:]", "result": [{"a": 1}, {"a": 2}, {"a": 3}] }, { "expression": "[:2].a", "result": [1, 2] }, { "expression": "[::-1].a", "result": [3, 2, 1] }, { "expression": "[:2].b", "result": [] } ] }] jp-0.1.3/test/compliance/syntax.json000066400000000000000000000322101315600161600174330ustar00rootroot00000000000000[{ "comment": "Dot syntax", "given": {"type": "object"}, "cases": [ { "expression": "foo.bar", "result": null }, { "expression": "foo.1", "error": "syntax" }, { "expression": "foo.-11", "error": "syntax" }, { "expression": "foo", "result": null }, { "expression": "foo.", "error": "syntax" }, { "expression": "foo.", "error": "syntax" }, { "expression": ".foo", "error": "syntax" }, { "expression": "foo..bar", "error": "syntax" }, { "expression": "foo.bar.", "error": "syntax" }, { "expression": "foo[.]", "error": "syntax" } ] }, { "comment": "Simple token errors", "given": {"type": "object"}, "cases": [ { "expression": ".", "error": "syntax" }, { "expression": ":", "error": "syntax" }, { "expression": ",", "error": "syntax" }, { "expression": "]", "error": "syntax" }, { "expression": "[", "error": "syntax" }, { "expression": "}", "error": "syntax" }, { "expression": "{", "error": "syntax" }, { "expression": ")", "error": "syntax" }, { "expression": "(", "error": "syntax" }, { "expression": "((&", "error": "syntax" }, { "expression": "a[", "error": "syntax" }, { "expression": "a]", "error": "syntax" }, { "expression": "a][", "error": "syntax" }, { "expression": "!", "error": "syntax" } ] }, { "comment": "Boolean syntax errors", "given": {"type": "object"}, "cases": [ { "expression": "![!(!", "error": "syntax" } ] }, { "comment": "Wildcard syntax", "given": {"type": "object"}, "cases": [ { "expression": "*", "result": ["object"] }, { "expression": "*.*", "result": [] }, { "expression": "*.foo", "result": [] }, { "expression": "*[0]", "result": [] }, { "expression": ".*", "error": "syntax" }, { "expression": "*foo", "error": "syntax" }, { "expression": "*0", "error": "syntax" }, { "expression": "foo[*]bar", "error": "syntax" }, { "expression": "foo[*]*", "error": "syntax" } ] }, { "comment": "Flatten syntax", "given": {"type": "object"}, "cases": [ { "expression": "[]", "result": null } ] }, { "comment": "Simple bracket syntax", "given": {"type": "object"}, "cases": [ { "expression": "[0]", "result": null }, { "expression": "[*]", "result": null }, { "expression": "*.[0]", "error": "syntax" }, { "expression": "*.[\"0\"]", "result": [[null]] }, { "expression": "[*].bar", "result": null }, { "expression": "[*][0]", "result": null }, { "expression": "foo[#]", "error": "syntax" } ] }, { "comment": "Multi-select list syntax", "given": {"type": "object"}, "cases": [ { "expression": "foo[0]", "result": null }, { "comment": "Valid multi-select of a list", "expression": "foo[0, 1]", "error": "syntax" }, { "expression": "foo.[0]", "error": "syntax" }, { "expression": "foo.[*]", "result": null }, { "comment": "Multi-select of a list with trailing comma", "expression": "foo[0, ]", "error": "syntax" }, { "comment": "Multi-select of a list with trailing comma and no close", "expression": "foo[0,", "error": "syntax" }, { "comment": "Multi-select of a list with trailing comma and no close", "expression": "foo.[a", "error": "syntax" }, { "comment": "Multi-select of a list with extra comma", "expression": "foo[0,, 1]", "error": "syntax" }, { "comment": "Multi-select of a list using an identifier index", "expression": "foo[abc]", "error": "syntax" }, { "comment": "Multi-select of a list using identifier indices", "expression": "foo[abc, def]", "error": "syntax" }, { "comment": "Multi-select of a list using an identifier index", "expression": "foo[abc, 1]", "error": "syntax" }, { "comment": "Multi-select of a list using an identifier index with trailing comma", "expression": "foo[abc, ]", "error": "syntax" }, { "comment": "Valid multi-select of a hash using an identifier index", "expression": "foo.[abc]", "result": null }, { "comment": "Valid multi-select of a hash", "expression": "foo.[abc, def]", "result": null }, { "comment": "Multi-select of a hash using a numeric index", "expression": "foo.[abc, 1]", "error": "syntax" }, { "comment": "Multi-select of a hash with a trailing comma", "expression": "foo.[abc, ]", "error": "syntax" }, { "comment": "Multi-select of a hash with extra commas", "expression": "foo.[abc,, def]", "error": "syntax" }, { "comment": "Multi-select of a hash using number indices", "expression": "foo.[0, 1]", "error": "syntax" } ] }, { "comment": "Multi-select hash syntax", "given": {"type": "object"}, "cases": [ { "comment": "No key or value", "expression": "a{}", "error": "syntax" }, { "comment": "No closing token", "expression": "a{", "error": "syntax" }, { "comment": "Not a key value pair", "expression": "a{foo}", "error": "syntax" }, { "comment": "Missing value and closing character", "expression": "a{foo:", "error": "syntax" }, { "comment": "Missing closing character", "expression": "a{foo: 0", "error": "syntax" }, { "comment": "Missing value", "expression": "a{foo:}", "error": "syntax" }, { "comment": "Trailing comma and no closing character", "expression": "a{foo: 0, ", "error": "syntax" }, { "comment": "Missing value with trailing comma", "expression": "a{foo: ,}", "error": "syntax" }, { "comment": "Accessing Array using an identifier", "expression": "a{foo: bar}", "error": "syntax" }, { "expression": "a{foo: 0}", "error": "syntax" }, { "comment": "Missing key-value pair", "expression": "a.{}", "error": "syntax" }, { "comment": "Not a key-value pair", "expression": "a.{foo}", "error": "syntax" }, { "comment": "Missing value", "expression": "a.{foo:}", "error": "syntax" }, { "comment": "Missing value with trailing comma", "expression": "a.{foo: ,}", "error": "syntax" }, { "comment": "Valid multi-select hash extraction", "expression": "a.{foo: bar}", "result": null }, { "comment": "Valid multi-select hash extraction", "expression": "a.{foo: bar, baz: bam}", "result": null }, { "comment": "Trailing comma", "expression": "a.{foo: bar, }", "error": "syntax" }, { "comment": "Missing key in second key-value pair", "expression": "a.{foo: bar, baz}", "error": "syntax" }, { "comment": "Missing value in second key-value pair", "expression": "a.{foo: bar, baz:}", "error": "syntax" }, { "comment": "Trailing comma", "expression": "a.{foo: bar, baz: bam, }", "error": "syntax" }, { "comment": "Nested multi select", "expression": "{\"\\\\\":{\" \":*}}", "result": {"\\": {" ": ["object"]}} } ] }, { "comment": "Or expressions", "given": {"type": "object"}, "cases": [ { "expression": "foo || bar", "result": null }, { "expression": "foo ||", "error": "syntax" }, { "expression": "foo.|| bar", "error": "syntax" }, { "expression": " || foo", "error": "syntax" }, { "expression": "foo || || foo", "error": "syntax" }, { "expression": "foo.[a || b]", "result": null }, { "expression": "foo.[a ||]", "error": "syntax" }, { "expression": "\"foo", "error": "syntax" } ] }, { "comment": "Filter expressions", "given": {"type": "object"}, "cases": [ { "expression": "foo[?bar==`\"baz\"`]", "result": null }, { "expression": "foo[? bar == `\"baz\"` ]", "result": null }, { "expression": "foo[ ?bar==`\"baz\"`]", "error": "syntax" }, { "expression": "foo[?bar==]", "error": "syntax" }, { "expression": "foo[?==]", "error": "syntax" }, { "expression": "foo[?==bar]", "error": "syntax" }, { "expression": "foo[?bar==baz?]", "error": "syntax" }, { "expression": "foo[?a.b.c==d.e.f]", "result": null }, { "expression": "foo[?bar==`[0, 1, 2]`]", "result": null }, { "expression": "foo[?bar==`[\"a\", \"b\", \"c\"]`]", "result": null }, { "comment": "Literal char not escaped", "expression": "foo[?bar==`[\"foo`bar\"]`]", "error": "syntax" }, { "comment": "Literal char escaped", "expression": "foo[?bar==`[\"foo\\`bar\"]`]", "result": null }, { "comment": "Unknown comparator", "expression": "foo[?bar<>baz]", "error": "syntax" }, { "comment": "Unknown comparator", "expression": "foo[?bar^baz]", "error": "syntax" }, { "expression": "foo[bar==baz]", "error": "syntax" }, { "comment": "Quoted identifier in filter expression no spaces", "expression": "[?\"\\\\\">`\"foo\"`]", "result": null }, { "comment": "Quoted identifier in filter expression with spaces", "expression": "[?\"\\\\\" > `\"foo\"`]", "result": null } ] }, { "comment": "Filter expression errors", "given": {"type": "object"}, "cases": [ { "expression": "bar.`\"anything\"`", "error": "syntax" }, { "expression": "bar.baz.noexists.`\"literal\"`", "error": "syntax" }, { "comment": "Literal wildcard projection", "expression": "foo[*].`\"literal\"`", "error": "syntax" }, { "expression": "foo[*].name.`\"literal\"`", "error": "syntax" }, { "expression": "foo[].name.`\"literal\"`", "error": "syntax" }, { "expression": "foo[].name.`\"literal\"`.`\"subliteral\"`", "error": "syntax" }, { "comment": "Projecting a literal onto an empty list", "expression": "foo[*].name.noexist.`\"literal\"`", "error": "syntax" }, { "expression": "foo[].name.noexist.`\"literal\"`", "error": "syntax" }, { "expression": "twolen[*].`\"foo\"`", "error": "syntax" }, { "comment": "Two level projection of a literal", "expression": "twolen[*].threelen[*].`\"bar\"`", "error": "syntax" }, { "comment": "Two level flattened projection of a literal", "expression": "twolen[].threelen[].`\"bar\"`", "error": "syntax" } ] }, { "comment": "Identifiers", "given": {"type": "object"}, "cases": [ { "expression": "foo", "result": null }, { "expression": "\"foo\"", "result": null }, { "expression": "\"\\\\\"", "result": null } ] }, { "comment": "Combined syntax", "given": [], "cases": [ { "expression": "*||*|*|*", "result": null }, { "expression": "*[]||[*]", "result": [] }, { "expression": "[*.*]", "result": [null] } ] } ] jp-0.1.3/test/compliance/unicode.json000066400000000000000000000014731315600161600175420ustar00rootroot00000000000000[ { "given": {"foo": [{"✓": "✓"}, {"✓": "✗"}]}, "cases": [ { "expression": "foo[].\"✓\"", "result": ["✓", "✗"] } ] }, { "given": {"☯": true}, "cases": [ { "expression": "\"☯\"", "result": true } ] }, { "given": {"♪♫•*¨*•.¸¸❤¸¸.•*¨*•♫♪": true}, "cases": [ { "expression": "\"♪♫•*¨*•.¸¸❤¸¸.•*¨*•♫♪\"", "result": true } ] }, { "given": {"☃": true}, "cases": [ { "expression": "\"☃\"", "result": true } ] } ] jp-0.1.3/test/compliance/wildcard.json000066400000000000000000000243311315600161600177030ustar00rootroot00000000000000[{ "given": { "foo": { "bar": { "baz": "val" }, "other": { "baz": "val" }, "other2": { "baz": "val" }, "other3": { "notbaz": ["a", "b", "c"] }, "other4": { "notbaz": ["a", "b", "c"] }, "other5": { "other": { "a": 1, "b": 1, "c": 1 } } } }, "cases": [ { "expression": "foo.*.baz", "result": ["val", "val", "val"] }, { "expression": "foo.bar.*", "result": ["val"] }, { "expression": "foo.*.notbaz", "result": [["a", "b", "c"], ["a", "b", "c"]] }, { "expression": "foo.*.notbaz[0]", "result": ["a", "a"] }, { "expression": "foo.*.notbaz[-1]", "result": ["c", "c"] } ] }, { "given": { "foo": { "first-1": { "second-1": "val" }, "first-2": { "second-1": "val" }, "first-3": { "second-1": "val" } } }, "cases": [ { "expression": "foo.*", "result": [{"second-1": "val"}, {"second-1": "val"}, {"second-1": "val"}] }, { "expression": "foo.*.*", "result": [["val"], ["val"], ["val"]] }, { "expression": "foo.*.*.*", "result": [[], [], []] }, { "expression": "foo.*.*.*.*", "result": [[], [], []] } ] }, { "given": { "foo": { "bar": "one" }, "other": { "bar": "one" }, "nomatch": { "notbar": "three" } }, "cases": [ { "expression": "*.bar", "result": ["one", "one"] } ] }, { "given": { "top1": { "sub1": {"foo": "one"} }, "top2": { "sub1": {"foo": "one"} } }, "cases": [ { "expression": "*", "result": [{"sub1": {"foo": "one"}}, {"sub1": {"foo": "one"}}] }, { "expression": "*.sub1", "result": [{"foo": "one"}, {"foo": "one"}] }, { "expression": "*.*", "result": [[{"foo": "one"}], [{"foo": "one"}]] }, { "expression": "*.*.foo[]", "result": ["one", "one"] }, { "expression": "*.sub1.foo", "result": ["one", "one"] } ] }, { "given": {"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]}, "cases": [ { "expression": "foo[*].bar", "result": ["one", "two", "three"] }, { "expression": "foo[*].notbar", "result": ["four"] } ] }, { "given": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}], "cases": [ { "expression": "[*]", "result": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}] }, { "expression": "[*].bar", "result": ["one", "two", "three"] }, { "expression": "[*].notbar", "result": ["four"] } ] }, { "given": { "foo": { "bar": [ {"baz": ["one", "two", "three"]}, {"baz": ["four", "five", "six"]}, {"baz": ["seven", "eight", "nine"]} ] } }, "cases": [ { "expression": "foo.bar[*].baz", "result": [["one", "two", "three"], ["four", "five", "six"], ["seven", "eight", "nine"]] }, { "expression": "foo.bar[*].baz[0]", "result": ["one", "four", "seven"] }, { "expression": "foo.bar[*].baz[1]", "result": ["two", "five", "eight"] }, { "expression": "foo.bar[*].baz[2]", "result": ["three", "six", "nine"] }, { "expression": "foo.bar[*].baz[3]", "result": [] } ] }, { "given": { "foo": { "bar": [["one", "two"], ["three", "four"]] } }, "cases": [ { "expression": "foo.bar[*]", "result": [["one", "two"], ["three", "four"]] }, { "expression": "foo.bar[0]", "result": ["one", "two"] }, { "expression": "foo.bar[0][0]", "result": "one" }, { "expression": "foo.bar[0][0][0]", "result": null }, { "expression": "foo.bar[0][0][0][0]", "result": null }, { "expression": "foo[0][0]", "result": null } ] }, { "given": { "foo": [ {"bar": [{"kind": "basic"}, {"kind": "intermediate"}]}, {"bar": [{"kind": "advanced"}, {"kind": "expert"}]}, {"bar": "string"} ] }, "cases": [ { "expression": "foo[*].bar[*].kind", "result": [["basic", "intermediate"], ["advanced", "expert"]] }, { "expression": "foo[*].bar[0].kind", "result": ["basic", "advanced"] } ] }, { "given": { "foo": [ {"bar": {"kind": "basic"}}, {"bar": {"kind": "intermediate"}}, {"bar": {"kind": "advanced"}}, {"bar": {"kind": "expert"}}, {"bar": "string"} ] }, "cases": [ { "expression": "foo[*].bar.kind", "result": ["basic", "intermediate", "advanced", "expert"] } ] }, { "given": { "foo": [{"bar": ["one", "two"]}, {"bar": ["three", "four"]}, {"bar": ["five"]}] }, "cases": [ { "expression": "foo[*].bar[0]", "result": ["one", "three", "five"] }, { "expression": "foo[*].bar[1]", "result": ["two", "four"] }, { "expression": "foo[*].bar[2]", "result": [] } ] }, { "given": { "foo": [{"bar": []}, {"bar": []}, {"bar": []}] }, "cases": [ { "expression": "foo[*].bar[0]", "result": [] } ] }, { "given": { "foo": [["one", "two"], ["three", "four"], ["five"]] }, "cases": [ { "expression": "foo[*][0]", "result": ["one", "three", "five"] }, { "expression": "foo[*][1]", "result": ["two", "four"] } ] }, { "given": { "foo": [ [ ["one", "two"], ["three", "four"] ], [ ["five", "six"], ["seven", "eight"] ], [ ["nine"], ["ten"] ] ] }, "cases": [ { "expression": "foo[*][0]", "result": [["one", "two"], ["five", "six"], ["nine"]] }, { "expression": "foo[*][1]", "result": [["three", "four"], ["seven", "eight"], ["ten"]] }, { "expression": "foo[*][0][0]", "result": ["one", "five", "nine"] }, { "expression": "foo[*][1][0]", "result": ["three", "seven", "ten"] }, { "expression": "foo[*][0][1]", "result": ["two", "six"] }, { "expression": "foo[*][1][1]", "result": ["four", "eight"] }, { "expression": "foo[*][2]", "result": [] }, { "expression": "foo[*][2][2]", "result": [] }, { "expression": "bar[*]", "result": null }, { "expression": "bar[*].baz[*]", "result": null } ] }, { "given": { "string": "string", "hash": {"foo": "bar", "bar": "baz"}, "number": 23, "nullvalue": null }, "cases": [ { "expression": "string[*]", "result": null }, { "expression": "hash[*]", "result": null }, { "expression": "number[*]", "result": null }, { "expression": "nullvalue[*]", "result": null }, { "expression": "string[*].foo", "result": null }, { "expression": "hash[*].foo", "result": null }, { "expression": "number[*].foo", "result": null }, { "expression": "nullvalue[*].foo", "result": null }, { "expression": "nullvalue[*].foo[*].bar", "result": null } ] }, { "given": { "string": "string", "hash": {"foo": "val", "bar": "val"}, "number": 23, "array": [1, 2, 3], "nullvalue": null }, "cases": [ { "expression": "string.*", "result": null }, { "expression": "hash.*", "result": ["val", "val"] }, { "expression": "number.*", "result": null }, { "expression": "array.*", "result": null }, { "expression": "nullvalue.*", "result": null } ] }, { "given": { "a": [0, 1, 2], "b": [0, 1, 2] }, "cases": [ { "expression": "*[0]", "result": [0, 0] } ] } ] jp-0.1.3/test/jp-compliance000077500000000000000000000243441315600161600155600ustar00rootroot00000000000000#!/usr/bin/env python """JMESPath compliance test runner. This is a test runner that will run the JMESPath compliance tests against a JMESPath executable. Compliance tests are broken down into three components: * The filename that contains the test. These are grouped by feature. The * test group within the file. A test group can have multiple tests. The * test case number. This is an individual test. If "-t/--tests" is not provided then all compliance tests are run. You can specify which tests to run using the "-t/--tests" argument. Each test is specified as a comma separated list consisting of "category,group_number,test_number". The group number and test number are optional. If no test number if provided all tests within the group are run. If no group number is given, all tests in that category are run. To see a list of categories use the "-l/--list" option. Multiple comma separates values are space separated. When a test failure occurs, the category, group number, and test number are displayed in the failure message. This allows you to quickly rerun a specific test. Examples ======== These examples show how to run the compliance tests against the "jp" executable. Run all the basic tests:: jp-compliance -e jp -t basic Run all the basic tests in group 1:: jp-compliance -e jp -t basic,1 Run the filter and function tests:: jp-compliance -e jp -t filters functions Run the filter and function tests in group 1:: jp-compliance -e jp -t filters,1 functions,1 """ import sys import argparse import os import subprocess import json import shlex if sys.version_info[:2] == (2, 6): import simplejson as json from ordereddict import OrderedDict else: import json from collections import OrderedDict _abs = os.path.abspath _dname = os.path.dirname _pjoin = os.path.join _splitext = os.path.splitext _bname = os.path.basename class ComplianceTestRunner(object): TEST_DIR = _pjoin(_dname(_dname(_abs(__file__))), 'tests') def __init__(self, exe=None, tests=None, test_dir=None): if test_dir is None: test_dir = self.TEST_DIR self.test_dir = test_dir self.tests = tests self.jp_executable = exe self.had_failures = False def run_tests(self, stop_first_fail): for test_case in self._test_cases(): if self._should_run(test_case): test_passed = self._run_test(test_case) if not test_passed: self.had_failures = True if stop_first_fail: return def _should_run(self, test_case): if not self.tests: return True # Specific tests were called out so we need # at least one thing in self.tests to match # in order to run the test. for allowed_test in self.tests: if self._is_subset(allowed_test, test_case): return True return False def _is_subset(self, subset, fullset): for key in subset: if subset[key] != fullset.get(key): return False return True def _load_test_file(self, test_json_file): with open(test_json_file) as f: loaded_test = json.loads(f.read(), object_pairs_hook=OrderedDict) return loaded_test def _load_test_cases(self, filename, group_number, test_group): given = test_group['given'] for i, case in enumerate(test_group['cases']): current = {"given": given, "group_number": group_number, "test_number": i, 'category': _splitext(_bname(filename))[0]} current.update(case) yield current def _test_cases(self): for test_json_file in self.get_compliance_test_files(): test_groups = self._load_test_file(test_json_file) for i, test_group in enumerate(test_groups): test_cases = self._load_test_cases(test_json_file, i, test_group) for test_case in test_cases: yield test_case def _run_test(self, test_case): command = shlex.split(self.jp_executable) command.append(test_case['expression']) try: process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) except Exception, e: raise RuntimeError('Could not execute test executable "%s": ' '%s' % (' '.join(command), e)) process.stdin.write(json.dumps(test_case['given'])) process.stdin.flush() stdout, stderr = process.communicate() if 'result' in test_case: try: actual = json.loads(stdout) except: actual = stdout expected = test_case['result'] if not actual == expected: self._show_failure(actual, test_case) return False else: sys.stdout.write('.') sys.stdout.flush() return True else: # This is a test case for errors. if process.returncode == 0: self._show_failure_for_zero_rc(stderr, process.returncode, test_case) # For errors, we expect the error type on stderr. if not self._passes_error_test(test_case['error'], stderr): self._show_failure_for_error(stderr, test_case) return False else: sys.stdout.write('.') sys.stdout.flush() return True def _passes_error_test(self, error_type, stderr): # Each tool will have different error messages, so we don't # want to be super strict about what's allowed. # # Simplest case, the error_type appears in stderr, case insensitive. if error_type not in stderr.lower(): return True # Second case, all the parts of the error appear in stderr. # Also case insensitive. # An error_type will be '-' separated: invalid-type # So a test can pass as long as "invalid" and "type" appear # in stderr (case insensitive). error_parts = error_type.split('-') if all(p in stderr.lower() for p in error_parts): return True return False def _show_failure(self, actual, test_case): test_case['actual'] = json.dumps(actual) test_case['result'] = json.dumps(test_case['result']) test_case['given_js'] = json.dumps(test_case['given']) failure_message = ( "\nFAIL {category},{group_number},{test_number}\n" "The expression: {expression}\n" "was suppose to give: {result}\n" "for the JSON: {given_js}\n" "but instead gave: {actual}\n" ).format(**test_case) sys.stdout.write(failure_message) def _show_failure_for_zero_rc(self, stderr, return_code, test_case): test_case['stderr'] = stderr test_case['returncode'] = return_code failure_message = ( "\nFAIL {category},{group_number},{test_number}\n" "The expression: {expression}\n" "was suppose to have non zero for error error: {error}\n" "but instead gave rc of: {returncode}, stderr: \n{stderr}\n" ).format(**test_case) sys.stdout.write(failure_message) def _show_failure_for_error(self, stderr, test_case): test_case['stderr'] = stderr failure_message = ( "\nFAIL {category},{group_number},{test_number}\n" "The expression: {expression}\n" "was suppose to emit the error: {error}\n" "but instead gave: \n{stderr}\n" ).format(**test_case) sys.stdout.write(failure_message) def get_compliance_test_files(self): for root, _, filenames in os.walk(self.test_dir): for filename in filenames: if filename.endswith('.json'): full_path = _pjoin(root, filename) yield full_path def display_available_tests(test_files): print("Available test types:\n") for filename in test_files: no_extension = os.path.splitext(os.path.basename(filename))[0] print(no_extension) def test_spec(value): parts = value.split(',') if not parts: raise ValueError("%s should be a comma separated list." % value) spec = {'category': parts[0]} if len(parts) == 2: spec['group_number'] = int(parts[1]) elif len(parts) == 3: spec['group_number'] = int(parts[1]) spec['test_number'] = int(parts[2]) return spec def main(): parser = argparse.ArgumentParser(usage=__doc__) parser.add_argument('-e', '--exe', help='The JMESPath executable to use.') parser.add_argument('-t', '--tests', help=( 'The compliance tests to run. If this value is not provided, ' 'then all compliance tests are run.'), type=test_spec, nargs='+') parser.add_argument('-d', '--test-dir', help='The directory containing compliance tests.') parser.add_argument('-l', '--list', action="store_true", help=('List the available compliance tests to run. ' 'These values can then be used with the ' '"-t/--tests" argument. If this argument is ' 'specified, no tests will actually be run.')) parser.add_argument('-s', '--stop-first-fail', action='store_true', help='Stop running tests after a single test fails.') args = parser.parse_args() runner = ComplianceTestRunner(args.exe, args.tests, args.test_dir) if args.list: display_available_tests(runner.get_compliance_test_files()) else: try: runner.run_tests(args.stop_first_fail) except Exception, e: sys.stderr.write(str(e)) sys.stderr.write("\n") return 1 sys.stdout.write('\n') if runner.had_failures: sys.stdout.write('FAIL\n') return 1 sys.stdout.write('OK\n') return 0 if __name__ == '__main__': sys.exit(main())