Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EvalMode to evaluation test assertion #29

Merged
merged 2 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 80 additions & 15 deletions docs/partiql-tests-schema-proposal.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ As of this proposal, we want to test these categories:
---

The following is an abstraction to describe current and future tests we will have in the `partiql-tests` suite
```
```ion
{
name: <string>,
statement: <string>,
Expand All @@ -36,7 +36,7 @@ Tests whether a given PartiQL statement is syntactically valid. For now, compose
- PartiQL statement (string)
- assert (struct or list of structs) with a syntax assertion

```
```ion
// syntax 'success' test with one assertion
{
name: <string>,
Expand All @@ -58,7 +58,7 @@ Tests whether a given PartiQL statement is syntactically valid. For now, compose

The `assert` field could also be a list of structs if more test assertions are added in the future.

```
```ion
// syntax 'success' test with multiple assertions
{
...
Expand Down Expand Up @@ -100,7 +100,7 @@ between parsing and evaluation. It's up to the implementation to decide at what
For now, composed of the same properties as the `syntax` `fail` tests. The only difference is the `assert`'s error
(i.e. `StaticAnalysisFail`).

```
```ion
{
name: <string>,
statement: <string>,
Expand All @@ -121,22 +121,23 @@ Tests whether a given PartiQL statement evaluates to the expected result. For no
- [optional] input evaluation environment, `env` - struct
- defaults to using environments specified in file (i.e. `envs`). If `envs` is unspecified, defaults to an empty
environment with no bindings
- [optional] evaluation `options` other than the defaults - struct
- e.g. typing mode - strict vs permissive (permissive may be most useful)
Comment on lines -124 to -125
Copy link
Member Author

@alancai98 alancai98 Aug 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was originally a struct to follow all the different compile options specified in the Kotlin reference implementation (see CompileOptions.kt). After looking at the Kotlin implementation's options and the specification, determined that "typing mode" (renamed to evaluation mode) was important to define at this time. Future options (if needed) can be added later.

- `assert` - struct or list of structs
- `result` key maps to symbol
- `EvaluatorSuccess` if evaluation succeeds for statement
- `EvaluatorFail` if evaluation fails for statement
- `evalMode` - evaluation mode to run the tests (symbol or list of symbols)
- `EvalModeCoerce` - dynamic type mismatch returns `MISSING`
- `EvalModeError` - dynamic type mismatch errors
- expected `output` of evaluation (only for `EvaluatorSuccess` `result`s)

```
```ion
// eval 'success' test
{
name: <string>,
statement: <string>,
env: <struct>, // optional
options: <struct>, // optional
assert: {
evalMode: <symbol> | <list<symbol>>,
result: EvaluationSuccess,
output: <ion>
},
Expand All @@ -147,8 +148,8 @@ Tests whether a given PartiQL statement evaluates to the expected result. For no
name: <string>,
statement: <string>,
env: <struct>, // optional
options: <struct>, // optional
assert: {
evalMode: <symbol> | <list<symbol>>,
result: EvaluationFail
}
}
Expand All @@ -158,7 +159,7 @@ For ease of writing evaluation tests, it’s necessary to provide a way to speci
outside a given test.

Specifying environments available for a given file:
```
```ion
envs::{
'table1': [{a:1}, {a:2}, {a:3}],
'table2': ...
Expand Down Expand Up @@ -189,7 +190,7 @@ few approaches we could take to model PartiQL data:
Of the above options, we've decided to go with the annotation approach. Values of these additional types will be denoted
using a `$<partiql_type>` annotation. This will be used for the output result and environments.

```
```ion
// bag -- list annotated with $bag
$bag::[1, 2, 3]

Expand All @@ -205,12 +206,76 @@ $time::'02:30:59'

---

#### PartiQL Evaluation Modes
The PartiQL specification defines two dynamic type mismatching evaluation modes (i.e. when a PartiQL statement is run
without schema). As defined in the PartiQL specification, these modes are:
- Permissive mode -- dynamic typing mismatches are neglected and PartiQL returns `MISSING`
- Type checking mode -- dynamic type mismatches result in evaluation errors

The naming of these modes can be somewhat confusing especially "type checking mode", which is sometimes referred to as
`STRICT` mode in the specification and Kotlin reference implementation. For the purposes of this document and the
conformance tests, we will refer to permissive mode as `EvalModeCoerce` and type checking mode as `EvalModeError`.
These names can be changed in the future once we improve the terminology in the specification.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it makes sense to have a TODO issue in partiql-docs for any required spec. change (like a spike) and linking it here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it makes sense to add. Created https://github.com/partiql/partiql-docs/issues/24 to track and added a link.


```ion
// Test case using `EvalModeCoerce`
{
name: "coerce eval mode tuple navigation missing attribute dot notation",
statement: "{'a':1, 'b':2}.noSuchAttribute",
assert: {
evalMode: EvalModeCoerce,
result: EvaluationSuccess,
output: $missing::null
}
}

// Test case using `EvalModeError`
{
name: "error eval mode tuple navigation missing attribute dot notation",
statement: "{'a':1, 'b':2}.noSuchAttribute",
assert: {
evalMode: EvalModeError,
result: EvaluationFail
}
}

// Test case using both eval modes in assertions
{
name: "tuple navigation missing attribute dot notation",
statement: "{'a':1, 'b':2}.noSuchAttribute",
assert: [
{
evalMode: EvalModeError,
result: EvaluationFail
},
{
evalMode: EvalModeCoerce,
result: EvaluationSuccess,
output: $missing::null
},
]
}

// Test case using both eval modes in assertions with same result
{
name: "tuple navigation for attribute dot notation",
statement: "{'a':1, 'b':2}.a",
assert: {
evalMode: [EvalModeError, EvalModeCoerce],
result: EvaluationSuccess,
output: 1
}
}
```

---

### Equivalence
The PartiQL specification mentions some PartiQL statements that could be rewritten using a different PartiQL syntax
(e.g. wildcard expressions). A common use case could be to assert that such PartiQL statements evaluate to the same
result or have the same plan. Users can specify an equivalence class as follows.

```
```ion
equiv_class::{
id: <symbol>, // identifier that can be referred to in tests (e.g. evaluation)
statements: <list<string>> // list of equivalent PartiQL statements as strings
Expand All @@ -220,7 +285,7 @@ equiv_class::{
Evaluation tests can check that an equivalence class defined in the file/namespace have statements that evaluate to the
same result by referencing the equivalence class' symbol identifier in the `statement` field.

```
```ion
// evaluation equivalence test
{
name: <string>,
Expand All @@ -234,7 +299,7 @@ same result by referencing the equivalence class' symbol identifier in the `stat
```

As a simple example, the following would be how to write an evaluation equivalence test:
```
```ion
// equivalence class definition
equiv_class::{
id: ten,
Expand Down Expand Up @@ -263,7 +328,7 @@ The concept of namespacing tests can help categorize groups of tests and can red
be used by the test runner to prepend additional text to a test name. E.g. namespace of "literals" can be prepended to test names of "int" and "null" to get "literals - int" and
"literals - null"

```
```ion
// namespacing/grouping (using a symbol annotation)
<namespace_symbol>::[
{
Expand Down
3 changes: 2 additions & 1 deletion partiql-tests-data/fail/eval/query/select/select.ion
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name: "select star from non existent binding",
statement: "SELECT * FROM non_existent_binding",
assert: {
result: EvaluationFail // example fails in type-checking mode TBD on default evaluation mode (https://github.com/partiql/partiql-tests/issues/25)
evalMode: EvalModeError,
result: EvaluationFail
}
}
23 changes: 20 additions & 3 deletions partiql-tests-data/partiql-tests-schema.isl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ type::{
occurs: required
},
env: { type: struct, occurs: optional },
options: { type: struct, occurs: optional },
assert: {
type: {
one_of: [
Expand All @@ -75,6 +74,22 @@ type::{
}
}

type::{
name: EvaluationMode,
type: symbol,
valid_values: [EvalModeError, EvalModeCoerce]
}

type::{
name: EvaluationModeSymbolOrList,
type: {
one_of: [
EvaluationMode,
{ type: list, element: EvaluationMode }
]
}
}

type::{
name: SyntaxSuccessAssertion,
type: struct,
Expand Down Expand Up @@ -107,7 +122,8 @@ type::{
type: struct,
fields: {
result: { type: symbol, valid_values: [EvaluationSuccess], occurs: required },
output: { occurs: required }
output: { occurs: required },
Copy link
Member Author

@alancai98 alancai98 Aug 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered modeling the evaluation output's value as the result for EvaluationSuccess cases but this made the schema validation a lot more complicated. The result field has been a symbol and allowing it to take output's value (i.e. any Ion value) can result in a duplicate matching type with EvaluationFail. So I chose to keep the output field separate.

evalMode: { type: EvaluationModeSymbolOrList, occurs: required }
},
content: closed
}
Expand All @@ -116,7 +132,8 @@ type::{
name: EvaluationFailAssertion,
type: struct,
fields: {
result: { type: symbol, valid_values: [EvaluationFail], occurs: required }
result: { type: symbol, valid_values: [EvaluationFail], occurs: required },
evalMode: { type: EvaluationModeSymbolOrList, occurs: required }
},
content: closed
}
Expand Down
2 changes: 2 additions & 0 deletions partiql-tests-data/success/eval-equiv/path.ion
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ equiv_class::{
name: "equiv wildcard steps collection",
statement: wildcard_steps_collection,
assert: {
evalMode: [EvalModeCoerce, EvalModeError],
result: EvaluationSuccess,
output: $bag::[1, 2, 3]
}
Expand All @@ -35,6 +36,7 @@ equiv_class::{
name: "equiv wildcard steps struct",
statement: wildcard_steps_struct,
assert: {
evalMode: [EvalModeCoerce, EvalModeError],
result: EvaluationSuccess,
output: $bag::[1, 2]
}
Expand Down
2 changes: 2 additions & 0 deletions partiql-tests-data/success/eval/query/select/select.ion
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ envs::{
name: "SELECT path",
statement: "SELECT r.v FROM sensors AS s, s.readings AS r",
assert: {
evalMode: [EvalModeCoerce, EvalModeError],
result: EvaluationSuccess,
output: $bag::[
{
Expand Down Expand Up @@ -39,6 +40,7 @@ envs::{
]
},
assert: {
evalMode: [EvalModeCoerce, EvalModeError],
result: EvaluationSuccess,
output: $bag::[
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ class PartiQLTestDataValidator {
name: "some name",
statement: "some statement",
assert: {
evalMode: EvalModeCoerce,
result: EvaluationSuccess,
output: some_output
}
Expand All @@ -323,6 +324,7 @@ class PartiQLTestDataValidator {
name: "some name",
statement: "some statement",
assert: {
evalMode: EvalModeError,
result: EvaluationFail
}
}
Expand Down Expand Up @@ -356,6 +358,7 @@ class PartiQLTestDataValidator {
name: "some name",
statement: some_equivalence_class,
assert: {
evalMode: [EvalModeCoerce, EvalModeError],
result: EvaluationSuccess,
output: some_output,
}
Expand All @@ -373,8 +376,8 @@ class PartiQLTestDataValidator {
name: "some name",
statement: "some statement",
env: { some_key: some_value },
options: { some_option_key: some_option_value },
assert: {
evalMode: [EvalModeCoerce, EvalModeError],
result: EvaluationSuccess,
output: some_output
}
Expand Down Expand Up @@ -589,6 +592,7 @@ class PartiQLTestDataValidator {
name: "some name",
statement: "some statement",
assert: {
evalMode: [EvalModeCoerce, EvalModeError],
result: EvaluationSuccess
}
}
Expand All @@ -597,6 +601,39 @@ class PartiQLTestDataValidator {
assertViolationsOccurred(dataInIon)
}

@Test
fun testNoEvalModeInEvaluationSuccessSchema() {
val testData =
"""
{
name: "some name",
statement: "some statement",
assert: {
result: EvaluationSuccess,
output: 'some_output'
}
}
"""
val dataInIon = ion.loader.load(testData)
assertViolationsOccurred(dataInIon)
}

@Test
fun testNoEvalModeInEvaluationFailSchema() {
val testData =
"""
{
name: "some name",
statement: "some statement",
assert: {
result: EvaluationFail,
}
}
"""
val dataInIon = ion.loader.load(testData)
assertViolationsOccurred(dataInIon)
}

@Test
fun testNoOutputInEvaluationSuccessSchemaWithEquivalenceClass() {
val testData =
Expand Down