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

Validation Script for OC Path and RPC Coverage in READMEs #2865

Merged
merged 19 commits into from
Apr 8, 2024
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
Prev Previous commit
Next Next commit
Improve logic by searching from header rather than YAML block
  • Loading branch information
wenovus committed Apr 5, 2024
commit 5826fd989a3c003c5911b55505483072f992bc11
72 changes: 33 additions & 39 deletions tools/internal/mdocspec/md.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ const (
// OCSpecHeading is the MarkDown heading that MUST precede the yaml
// section containing the OC path and RPC listing.
OCSpecHeading = "OpenConfig Path and RPC Coverage"
// OCSpecHeadingSearchN is the number of previous siblings in the
// MarkDown AST to search for OCSpecHeading in order to establish
// whether the yaml block might be the OC path and RPC listing.
OCSpecHeadingSearchN = 5
)

type mdOCSpecs struct{}
Expand All @@ -41,53 +37,51 @@ func (r *yamlRenderer) Render(w io.Writer, source []byte, n ast.Node) error {

func (r *yamlRenderer) AddOptions(...renderer.Option) {}

func yamlCodeBlock(source []byte, n ast.Node) *ast.FencedCodeBlock {
if s, ok := n.(*ast.FencedCodeBlock); ok && s.Info != nil {
if lang := s.Info.Text(source); len(lang) > 0 && string(lang) == "yaml" {
return s
func ocSpecHeading(source []byte, n ast.Node) (heading *ast.Heading, ok bool) {
if h, ok := n.(*ast.Heading); ok {
headingSegment := h.Lines().At(0)
if string(headingSegment.Value(source)) == OCSpecHeading {
return h, true
}
}
return nil
return nil, false
}

func yamlCodeBlock(source []byte, n ast.Node) (block *ast.FencedCodeBlock, ok bool) {
if c, ok := n.(*ast.FencedCodeBlock); ok && c.Info != nil {
if lang := c.Info.Text(source); len(lang) > 0 && string(lang) == "yaml" {
return c, true
}
}
return nil, false
}

func renderYAML(w io.Writer, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
if s := yamlCodeBlock(source, n); s != nil {
// Check if within the last `OCSpecHeadingSearchN` siblings,
// the "OC Path and RPC Coverage" heading can be found, and
// that there is no intervening yaml block.
yamlBlockIsOCSpec := false
prev := s.PreviousSibling()
for i := 0; i != OCSpecHeadingSearchN && prev != nil; i++ {
if yamlCodeBlock(source, prev) != nil {
break
}
if h, ok := prev.(*ast.Heading); ok {
if h.Lines().Len() > 0 {
headingSegment := h.Lines().At(0)
if string(headingSegment.Value(source)) == OCSpecHeading {
yamlBlockIsOCSpec = true
break
}
}
}
prev = prev.PreviousSibling()
}
if !yamlBlockIsOCSpec {
heading, ok := ocSpecHeading(source, n)
if !ok {
return ast.WalkContinue, nil
}
// Check if prior to the next heading of the same level,
// a yaml code block can be found.
for next := heading.NextSibling(); next != nil; next = next.NextSibling() {
if h, ok := next.(*ast.Heading); ok && h.Level <= heading.Level {
// End of heading reached.
return ast.WalkContinue, nil
}

l := s.Lines().Len()
for i := 0; i != l; i++ {
line := s.Lines().At(i)
if _, err := w.Write(line.Value(source)); err != nil {
return ast.WalkStop, err
if c, ok := yamlCodeBlock(source, next); ok {
l := c.Lines().Len()
for i := 0; i != l; i++ {
line := c.Lines().At(i)
if _, err := w.Write(line.Value(source)); err != nil {
return ast.WalkStop, err
}
}
// Stop after finding the first such YAML block.
return ast.WalkStop, nil
dplore marked this conversation as resolved.
Show resolved Hide resolved
}
// Stop after finding the first.
return ast.WalkStop, nil
}
return ast.WalkContinue, nil
}
51 changes: 51 additions & 0 deletions tools/internal/mdocspec/md_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,57 @@ rpcs:
fooi.Subscribe:
on_change: true
`,
}, {
desc: "yaml-block-after-next-heading-ignored",
inSource: []byte(`---
name: New featureprofiles test requirement
about: Use this template to document the requirements for a new test to be implemented.
title: ''
labels: enhancement
assignees: ''
---

## Procedure

* Test environment setup
* Description of procedure to configure ATE and DUT with pre-requisites making it possible to cover the intended paths and RPC's.

* TestID-x.y.z - Name of subtest
* Step 1
* Step 2
* Validation and pass/fail criteria

* TestID-x.y.z - Name of subtest
* Step 1
* Step 2
* Validation and pass/fail criteria

## OpenConfig Path and RPC Coverage

This example yaml defines the OC paths intended to be covered by this test. OC paths used for test environment setup are not required to be listed here.

## Required DUT platform

` + "```" + `yaml
paths:
# interface configuration
/a/b/c:
/d/e/f:

rpcs:
fooi:
fooi.Set:
union_replace: true
fooi.Subscribe:
on_change: true
` + "```" + `

* Specify the minimum DUT-type:
* MFF - A modular form factor device containing LINECARDs, FABRIC and redundant CONTROLLER_CARD components
* FFF - fixed form factor
* vRX - virtual router device
`),
want: ``,
}, {
desc: "two-blocks-same-heading-first-language-not-specified-and-ignored",
inSource: []byte(`---
Expand Down
Loading