From b89d9309a6f268d0447b5e3b5259ab3c7c44bfe6 Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Fri, 5 Jul 2024 16:58:46 -0400 Subject: [PATCH] PICS grammar: Adjust for tooling Changes the way the PICS conformances are rendered to match with the expected grammar in the tooling. - added new macros for PICS used in conformance statements - removed otherwise conformances - degrade P, D conformances to X - fix grammar on not - omit choice conformances with + / - / N - change optional conformance to use PICS: O style - changed macros to use the squished together styles Still to do: - command PICS - split PICS into separate file so it can be generated - check grammar with Agustin For later: I like screaming snake case better and not just beccause the name is awesome. Let's use that, but we'd need to search/replace the whole test plan. --- testplan/attributes.go | 5 ++- testplan/conformance.go | 76 ++++++++++++++++++++++++++--------------- testplan/events.go | 7 ++-- testplan/features.go | 7 +++- testplan/header.go | 1 + testplan/identifiers.go | 9 +++-- 6 files changed, 71 insertions(+), 34 deletions(-) diff --git a/testplan/attributes.go b/testplan/attributes.go index ec20030d..7ba050cd 100644 --- a/testplan/attributes.go +++ b/testplan/attributes.go @@ -39,13 +39,16 @@ func renderAttributes(doc *spec.Doc, cluster *matter.Cluster, b *strings.Builder for i, name := range names { b.WriteString(fmt.Sprintf(":PICS_S%-*s : {PICS_S}.A%04x({%s})\n", longest, name, i, name)) } + b.WriteRune('\n') + for i, name := range names { + b.WriteString(fmt.Sprintf(":PICS_S%-*s_CONFORMANCE : {PICS_S}.A%04x\n", longest, name, i)) + } b.WriteString("\n\n|===\n") b.WriteString("| *Variable* | *Description* | *Mandatory/Optional* | *Notes/Additional Constraints*\n") for i, a := range cluster.Attributes { name := names[i] b.WriteString(fmt.Sprintf("| {PICS_S%s} | {devimp} the _{%s}_ attribute?| ", name, name)) if len(a.Conformance) > 0 { - b.WriteString("{PICS_S}: ") renderPicsConformance(b, doc, cluster, a.Conformance) } b.WriteString(" |\n") diff --git a/testplan/conformance.go b/testplan/conformance.go index 534164ec..b3e71769 100644 --- a/testplan/conformance.go +++ b/testplan/conformance.go @@ -16,45 +16,65 @@ func renderPicsConformance(b *strings.Builder, doc *spec.Doc, cluster *matter.Cl if len(cs) == 0 { return } - renderConformance(cs, b, doc, cluster, entityPICS) + renderConformance(cs, b, doc, cluster, entityPICSConformance) } func renderFeatureConformance(b *strings.Builder, doc *spec.Doc, cluster *matter.Cluster, cs conformance.Set) { if len(cs) == 0 { return } - b.WriteString("{PICS_S}: ") renderConformance(cs, b, doc, cluster, entityVariable) } +func renderChoice(c *conformance.Optional, b *strings.Builder) { + // PICS tool does not support + style conformances, so unless this is a "pick one" choice, + //render as fully optional, we'll check the choice conformance properly in the tests. + o := conformance.ChoiceExactLimit{Limit: 1} + if c.Choice != nil && o.Equal(c.Choice.Limit) { + b.WriteRune('.') + b.WriteString(c.Choice.ASCIIDocString()) + } +} + func renderConformance(cs conformance.Set, b *strings.Builder, doc *spec.Doc, cluster *matter.Cluster, formatter conformanceEntityFormatter) { - for _, c := range cs { - switch c := c.(type) { - case *conformance.Mandatory: - if c.Expression == nil { - b.WriteString("M") - continue - } - renderExpression(b, doc, cluster, c.Expression, formatter) - case *conformance.Optional: - if c.Expression == nil { - b.WriteString("O") - if c.Choice != nil { - b.WriteRune('.') - b.WriteString(c.Choice.ASCIIDocString()) - } - continue - } - b.WriteRune('[') - renderExpression(b, doc, cluster, c.Expression, formatter) - b.WriteRune(']') - if c.Choice != nil { - b.WriteRune('.') - b.WriteString(c.Choice.ASCIIDocString()) - } + // PICS tool can't handle otherwise conformances, so render anything with an otherwise conformance as optional for the purposes of the + // test plan PICS. This can be fully evaluated in the tests. + // The only exception is if it is provisional, which should be rendered as X. + if len(cs) != 1 { + switch cs[0].(type) { + case *conformance.Provisional: + b.WriteRune('X') default: - b.WriteString(fmt.Sprintf("unknown conformance: %T", c)) + b.WriteString("{PICS_S}: O") + } + return + } + switch c := cs[0].(type) { + case *conformance.Mandatory: + if c.Expression == nil { + b.WriteString("{PICS_S}: M") + return } + renderExpression(b, doc, cluster, c.Expression, formatter) + case *conformance.Optional: + if c.Expression == nil { + b.WriteString("{PICS_S}: O") + renderChoice(c, b) + return + } + renderExpression(b, doc, cluster, c.Expression, formatter) + b.WriteString(": O") + renderChoice(c, b) + case *conformance.Provisional: + b.WriteRune('X') + case *conformance.Disallowed: + b.WriteRune('X') + case *conformance.Deprecated: + b.WriteRune('X') + case *conformance.Described: + b.WriteString("{PICS_S}: O") + default: + b.WriteString(fmt.Sprintf("unknown conformance: %T", c)) } } @@ -74,7 +94,7 @@ func renderExpression(b *strings.Builder, doc *spec.Doc, cluster *matter.Cluster b.WriteString(renderReference(doc, exp.Reference, formatter)) case *conformance.LogicalExpression: if exp.Not { - b.WriteRune('!') + b.WriteString("NOT") } b.WriteRune('(') renderExpression(b, doc, cluster, exp.Left, formatter) diff --git a/testplan/events.go b/testplan/events.go index 1da5cc5d..da7e8aa5 100644 --- a/testplan/events.go +++ b/testplan/events.go @@ -31,7 +31,11 @@ func renderEvents(doc *spec.Doc, cluster *matter.Cluster, b *strings.Builder) { } b.WriteRune('\n') for i, name := range names { - b.WriteString(fmt.Sprintf(":PICS_S%-*s : {PICS_S}.A%04x({%s})\n", longest, name, i, name)) + b.WriteString(fmt.Sprintf(":PICS_S%-*s : {PICS_S}.E%02x({%s})\n", longest, name, i, name)) + } + b.WriteRune('\n') + for i, name := range names { + b.WriteString(fmt.Sprintf(":PICS_S%-*s_CONFORMANCE : {PICS_S}.E%02x\n", longest, name, i)) } b.WriteString("\n\n|===\n") b.WriteString("| *Variable* | *Description* | *Mandatory/Optional* | *Notes/Additional Constraints*\n") @@ -39,7 +43,6 @@ func renderEvents(doc *spec.Doc, cluster *matter.Cluster, b *strings.Builder) { name := names[i] b.WriteString(fmt.Sprintf("| {PICS_S%s} | {devimp} sending the _{%s}_ event?| ", name, name)) if len(event.Conformance) > 0 { - b.WriteString("{PICS_S}: ") renderPicsConformance(b, doc, cluster, event.Conformance) } b.WriteString(" |\n") diff --git a/testplan/features.go b/testplan/features.go index 996b8799..49b22739 100644 --- a/testplan/features.go +++ b/testplan/features.go @@ -21,6 +21,11 @@ func renderFeatures(doc *spec.Doc, cluster *matter.Cluster, b *strings.Builder) b.WriteString(fmt.Sprintf(":PICS_SF_%s: {PICS_S}.F%02d({F_%s})\n", f.Code, i, f.Code)) } b.WriteRune('\n') + for i, bit := range cluster.Features.Bits { + f := bit.(*matter.Feature) + b.WriteString(fmt.Sprintf(":PICS_SF_%s_CONFORMANCE: {PICS_S}.F%02d\n", f.Code, i)) + } + b.WriteRune('\n') b.WriteString("|===\n") b.WriteString("| *Variable* | *Description* | *Mandatory/Optional* | *Notes/Additional Constraints*\n") for _, bit := range cluster.Features.Bits { @@ -30,7 +35,7 @@ func renderFeatures(doc *spec.Doc, cluster *matter.Cluster, b *strings.Builder) b.WriteString("} | {devsup} ") b.WriteString(f.Summary()) b.WriteString(" | ") - renderFeatureConformance(b, doc, cluster, f.Conformance()) + renderPicsConformance(b, doc, cluster, f.Conformance()) b.WriteString(" | \n") } b.WriteString("|===\n\n\n") diff --git a/testplan/header.go b/testplan/header.go index c10bb717..cd82fd9c 100644 --- a/testplan/header.go +++ b/testplan/header.go @@ -55,6 +55,7 @@ This section covers the {clustername} Cluster Test Plan related PICS items that |=== | *Variable* | *Description* | *Mandatory/Optional* | *Notes/Additional Constraints* | {PICS_S} | {devimp} the {clustername} cluster as a server? | O | +| {PICS_C} | {devimp} the {clustername} cluster as a client? | O | |=== ` diff --git a/testplan/identifiers.go b/testplan/identifiers.go index 312b028c..aeef1265 100644 --- a/testplan/identifiers.go +++ b/testplan/identifiers.go @@ -2,6 +2,7 @@ package testplan import ( "fmt" + "strings" "github.com/iancoleman/strcase" "github.com/project-chip/alchemy/matter" @@ -12,6 +13,10 @@ func entityPICS(entity types.Entity) string { return fmt.Sprintf("{PICS_S%s}", entityIdentifier(entity)) } +func entityPICSConformance(entity types.Entity) string { + return fmt.Sprintf("{PICS_S%s_CONFORMANCE}", entityIdentifier(entity)) +} + func entityVariable(entity types.Entity) string { return fmt.Sprintf("{%s}", entityIdentifier(entity)) } @@ -21,12 +26,12 @@ func entityIdentifier(entity types.Entity) string { case *matter.Field: switch entity.EntityType() { case types.EntityTypeAttribute: - return fmt.Sprintf("A_%s", strcase.ToScreamingSnake(entity.Name)) + return fmt.Sprintf("A_%s", strings.ToUpper(strcase.ToCamel(entity.Name))) } case *matter.Feature: return fmt.Sprintf("F_%s", entity.Code) case *matter.Event: - return fmt.Sprintf("E_%s", strcase.ToScreamingSnake(entity.Name)) + return fmt.Sprintf("E_%s", strings.ToUpper(strcase.ToCamel(entity.Name))) } return fmt.Sprintf("UNKNOWN_TYPE_%T", entity) }