Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion packages/engine-client/test/wails-adapter.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ test("Wails client executes every shared portable compile and cancellation vecto
let releaseCancellation;
const cancellationGate = new Promise((resolve) => { releaseCancellation = resolve; });
const selected = parityCorpus.cases;
assert.equal(selected.length, 10);
assert.equal(selected.length, 14);
const byRequest = new Map(selected.map((entry) => [entry.expected.response.request_id, entry]));
const bindings = generatedBindings({
EngineHandshake: async (exchange) => handshakeResponse(exchange),
Expand Down
4 changes: 4 additions & 0 deletions packages/engine-wasm/test/browser/engine-worker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const parityCases = [
"deterministic_rejection",
"resource_limit_rejection",
"representative_large_graph",
"query_where",
"query_relation_where",
"query_traverse",
"query_where_relation_where_traverse",
"cancellation",
];
const viewDataCases = [
Expand Down
2 changes: 1 addition & 1 deletion packages/engine-wasm/test/shared/real-engine.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ export async function handshakeAndCompileCorpus(
caseNames,
) {
if (corpus.schema_version !== 1 || corpus.engine_release_variable !== "$engine_release" ||
corpus.cases.length !== 10 || corpus.required_features.length !== 10 || corpus.normalization.length !== 3) {
corpus.cases.length !== 14 || corpus.required_features.length !== 13 || corpus.normalization.length !== 3) {
throw new Error("transport-neutral parity corpus is incompatible");
}
const handshake = await performRequest(transport, `${suffix}-handshake-exchange`, {
Expand Down
2 changes: 1 addition & 1 deletion tests/conformance/testdata/engine_compile_parity_v1.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tests/packaged/portable_corpus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestPackagedStdioExecutesPortableCompilerCorpus(t *testing.T) {
if err := json.Unmarshal(data, &corpus); err != nil {
t.Fatal(err)
}
if corpus.SchemaVersion != 1 || len(corpus.Cases) != 10 || len(corpus.RequiredFeatures) != 10 || len(corpus.Normalization) != 3 {
if corpus.SchemaVersion != 1 || len(corpus.Cases) != 14 || len(corpus.RequiredFeatures) != 13 || len(corpus.Normalization) != 3 {
t.Fatalf("portable corpus is incomplete: cases=%d features=%d normalization=%d", len(corpus.Cases), len(corpus.RequiredFeatures), len(corpus.Normalization))
}

Expand Down
166 changes: 160 additions & 6 deletions tools/wasmparity/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/dencyuinc/layerdraw/gen/go/engineprotocol"
"github.com/dencyuinc/layerdraw/gen/go/protocolcommon"
"github.com/dencyuinc/layerdraw/gen/go/semantic"
"github.com/dencyuinc/layerdraw/internal/engine/endpoint"
wasmtransport "github.com/dencyuinc/layerdraw/internal/transport/wasm"
)
Expand Down Expand Up @@ -183,7 +184,8 @@ func buildCorpus() (parityCorpus, error) {
EngineReleaseVariable: engineReleaseVariable,
RequiredFeatures: []string{
"asset", "cancellation", "deterministic_rejection", "installed_pack", "large_graph",
"multi_module", "project", "resource_limit", "root_pack", "all_declarations",
"multi_module", "project", "query_relation_where", "query_traverse", "query_where",
"resource_limit", "root_pack", "all_declarations",
},
Normalization: []string{
"engine_release is replaced by $engine_release because each artifact reports its linked release",
Expand Down Expand Up @@ -215,11 +217,19 @@ func buildCorpus() (parityCorpus, error) {
if err != nil {
return parityCorpus{}, fmt.Errorf("dispatch %s: %w", input.name, err)
}
if input.queryClauses != nil && response.Outcome != protocolcommon.OutcomeSuccess {
return parityCorpus{}, fmt.Errorf("%s query-clause compile outcome = %s", input.name, response.Outcome)
}
switch response.Outcome {
case protocolcommon.OutcomeSuccess:
if response.Payload == nil || len(response.Diagnostics) != 0 || response.Payload.DefinitionHash == "" || len(response.Payload.SubjectSemanticHashes) == 0 || len(sink.blobs) == 0 {
return parityCorpus{}, fmt.Errorf("%s did not produce a complete successful semantic response", input.name)
}
if input.queryClauses != nil {
if err := assertQueryClauseRecipe(input.name, sink.blobs, *input.queryClauses); err != nil {
return parityCorpus{}, err
}
}
case protocolcommon.OutcomeRejected:
if response.Payload != nil || len(response.Diagnostics) == 0 || len(sink.blobs) != 0 {
return parityCorpus{}, fmt.Errorf("%s did not produce a closed rejection", input.name)
Expand Down Expand Up @@ -295,11 +305,18 @@ func validateCoverage(corpus parityCorpus) error {
}

type compileCase struct {
name string
features []string
execution string
request engineprotocol.CompileRequestEnvelope
blobs []parityInputBlob
name string
features []string
execution string
request engineprotocol.CompileRequestEnvelope
blobs []parityInputBlob
queryClauses *queryClauseExpectation
}

type queryClauseExpectation struct {
where bool
relationWhere bool
traverse bool
}

func compileCases() ([]compileCase, error) {
Expand Down Expand Up @@ -330,6 +347,34 @@ func compileCases() ([]compileCase, error) {
"document.ldl": []byte(large),
}, []string{"project", "large_graph"}, nil, engineprotocol.ResourceLimits{}),
}
clauseMatrix := []queryClauseExpectation{
{where: true},
{relationWhere: true},
{traverse: true},
{where: true, relationWhere: true, traverse: true},
}
for _, expectation := range clauseMatrix {
parts := make([]string, 0, 3)
features := []string{"project"}
if expectation.where {
parts = append(parts, "where")
features = append(features, "query_where")
}
if expectation.relationWhere {
parts = append(parts, "relation_where")
features = append(features, "query_relation_where")
}
if expectation.traverse {
parts = append(parts, "traverse")
features = append(features, "query_traverse")
}
name := "query_" + strings.Join(parts, "_")
testCase := projectCase(name, "document.ldl", map[string][]byte{
"document.ldl": []byte(queryClauseSource(expectation)),
}, features, nil, engineprotocol.ResourceLimits{})
testCase.queryClauses = &expectation
cases = append(cases, testCase)
}
installed, root, err := packCases()
if err != nil {
return nil, err
Expand All @@ -343,6 +388,115 @@ func compileCases() ([]compileCase, error) {
return cases, nil
}

func queryClauseSource(expectation queryClauseExpectation) string {
clauses := make([]string, 0, 3)
if expectation.where {
clauses = append(clauses, " where all {\n field id == \"alpha\"\n }")
}
if expectation.relationWhere {
clauses = append(clauses, " relation_where all {\n field type == link\n }")
}
if expectation.traverse {
clauses = append(clauses, " traverse outgoing 0..2 visit_once relations [link]")
}
return fmt.Sprintf(`project p "Project" {}
layers {
app "Application" @1
}
entity_type service "Service" {
representation shape rect
}
relation_type link "Link" dependency {
from source types [service] layers [app]
to target types [service] layers [app]
label "links"
}
entities service @app {
alpha "Alpha"
beta "Beta"
}
relations link {
alpha_beta: alpha -> beta
}
query scope "Scope" {
select {
entity_types [service]
relation_types [link]
roots [alpha]
}
%s
result [seed_entities, traversed_entities, path_relations]
}
`, strings.Join(clauses, "\n"))
}

func assertQueryClauseRecipe(name string, blobs []endpoint.OutputBlob, want queryClauseExpectation) error {
var document semantic.CompiledQueryRecipeDocument
found := false
for _, blob := range blobs {
if blob.Ref.MediaType != string(engineprotocol.QueryRecipeBlobRefMediaTypeValue) {
continue
}
decoded, err := semantic.DecodeCompiledQueryRecipeDocument(blob.Bytes)
if err != nil {
return fmt.Errorf("%s decode query recipe: %w", name, err)
}
document = decoded
found = true
break
}
if !found {
return fmt.Errorf("%s did not publish a query recipe", name)
}
if err := assertFieldPredicate(document.Recipe.Where, want.where, "id", "scalar", "string", "alpha"); err != nil {
return fmt.Errorf("%s where recipe: %w", name, err)
}
if err := assertFieldPredicate(document.Recipe.RelationWhere, want.relationWhere, "type", "address", "relation_type", "ldl:project:p:relation-type:link"); err != nil {
return fmt.Errorf("%s relation_where recipe: %w", name, err)
}
if !want.traverse {
if document.Recipe.Traverse != nil {
return fmt.Errorf("%s unexpected traverse recipe: %+v", name, document.Recipe.Traverse)
}
return nil
}
traversal := document.Recipe.Traverse
if traversal == nil || traversal.Direction != "outgoing" || traversal.MinDepth != "0" || traversal.MaxDepth != "2" || traversal.CyclePolicy != "visit_once" ||
traversal.RelationTypeAddresses == nil || len(*traversal.RelationTypeAddresses) != 1 || (*traversal.RelationTypeAddresses)[0] != "ldl:project:p:relation-type:link" {
return fmt.Errorf("%s traverse recipe does not preserve the authored traversal: %+v", name, traversal)
}
return nil
}

func assertFieldPredicate(predicate semantic.RecipePredicate, present bool, field, operandKind, operandType, value string) error {
if !present {
if predicate.Kind != "all" || predicate.Children == nil || len(*predicate.Children) != 0 {
return fmt.Errorf("omitted predicate did not compile to empty all: %+v", predicate)
}
return nil
}
if predicate.Kind != "all" || predicate.Children == nil || len(*predicate.Children) != 1 {
return fmt.Errorf("predicate tree = %+v", predicate)
}
child := (*predicate.Children)[0]
if child.Kind != "field" || child.Field == nil || *child.Field != field || child.Operator == nil || *child.Operator != "eq" || child.OperandType == nil || child.OperandType.Kind != operandKind || child.Value == nil {
return fmt.Errorf("field predicate = %+v", child)
}
switch operandKind {
case "scalar":
if child.OperandType.ScalarType == nil || string(*child.OperandType.ScalarType) != operandType || child.Value.Kind != "scalar" || child.Value.ScalarValue == nil || child.Value.ScalarValue.Kind != operandType || child.Value.ScalarValue.StringValue == nil || *child.Value.ScalarValue.StringValue != value {
return fmt.Errorf("scalar predicate = %+v", child)
}
case "address":
if child.OperandType.AddressKind == nil || string(*child.OperandType.AddressKind) != operandType || child.Value.Kind != "address" || child.Value.AddressValue == nil || string(*child.Value.AddressValue) != value {
return fmt.Errorf("address predicate = %+v", child)
}
default:
return fmt.Errorf("unsupported expected operand kind %q", operandKind)
}
return nil
}

type assetFixtureValue struct {
locator string
mediaType string
Expand Down
Loading