Skip to content

PICS grammar: Adjust for tooling #3

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 4 additions & 1 deletion testplan/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,16 @@ func renderAttributes(doc *spec.Doc, cut *clusterUnderTest, b *strings.Builder)
for i, name := range names {
b.WriteString(fmt.Sprintf(":PICS_S%-*s : {PICS_S}.A%04X({%s})\n", longest, name, cut.attributes[i].ID.Value(), 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 cut.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, cut.cluster, a.Conformance)
}
b.WriteString(" |\n")
Expand Down
76 changes: 48 additions & 28 deletions testplan/conformance.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}

Expand All @@ -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)
Expand Down
7 changes: 5 additions & 2 deletions testplan/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ func renderEvents(doc *spec.Doc, cut *clusterUnderTest, b *strings.Builder) {
}
b.WriteRune('\n')
for i, name := range names {
b.WriteString(fmt.Sprintf(":PICS_S%-*s : {PICS_S}.A%s({%s})\n", longest, name, cut.events[i].ID.HexString(), name))
b.WriteString(fmt.Sprintf(":PICS_S%-*s : {PICS_S}.E%s({%s})\n", longest, name, cut.events[i].ID.ShortHexString(), name))
}
b.WriteRune('\n')
for i, name := range names {
b.WriteString(fmt.Sprintf(":PICS_S%-*s_CONFORMANCE : {PICS_S}.E%s\n", longest, name, cut.events[i].ID.ShortHexString()))
}
b.WriteString("\n\n|===\n")
b.WriteString("| *Variable* | *Description* | *Mandatory/Optional* | *Notes/Additional Constraints*\n")
for i, event := range cut.events {
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, cut.cluster, event.Conformance)
}
b.WriteString(" |\n")
Expand Down
6 changes: 5 additions & 1 deletion testplan/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func renderFeatures(doc *spec.Doc, cut *clusterUnderTest, 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, f := range cut.features {
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 _, f := range cut.features {
Expand All @@ -29,7 +33,7 @@ func renderFeatures(doc *spec.Doc, cut *clusterUnderTest, b *strings.Builder) {
b.WriteString("} | {devsup} ")
b.WriteString(f.Summary())
b.WriteString(" | ")
renderFeatureConformance(b, doc, cut.cluster, f.Conformance())
renderPicsConformance(b, doc, cut.cluster, f.Conformance())
b.WriteString(" | \n")
}
b.WriteString("|===\n\n\n")
Expand Down
1 change: 1 addition & 0 deletions testplan/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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 |
|===

`
Expand Down
11 changes: 8 additions & 3 deletions testplan/identifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package testplan

import (
"fmt"
"strings"

"github.com/iancoleman/strcase"
"github.com/project-chip/alchemy/matter"
Expand All @@ -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))
}
Expand All @@ -21,14 +26,14 @@ 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)))
case *matter.Command:
return fmt.Sprintf("C_%s", strcase.ToScreamingSnake(entity.Name))
return fmt.Sprintf("C_%s", strings.ToUpper(strcase.ToCamel(entity.Name)))
}
return fmt.Sprintf("UNKNOWN_TYPE_%T", entity)
}