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
5 changes: 5 additions & 0 deletions .claude/skills/add-assertion/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
name: add-assertion
description: adding a new assertion to the library
---

# Adding a New Assertion

Step-by-step workflow for adding a new assertion function to testify.
Expand Down
5 changes: 5 additions & 0 deletions .claude/skills/codegen/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
name: regenerate-code-and-doc
description: How to regenerate code and documentation after a maintenance that changes the API
---

# Code Generation

How the testify code and documentation generator works.
Expand Down
5 changes: 5 additions & 0 deletions .claude/skills/doc-site/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
name: documentation-site
description: How to run and test the hugo doc site locally
---

# Documentation Site

Hugo-based documentation site for testify, auto-generated from source code.
Expand Down
5 changes: 5 additions & 0 deletions .claude/skills/testing-generic-functions/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
name: writing-test-for-generics
description: How to write tests that cover both generic and reflection-based assertions
---

# Testing Generic Functions with Table-Driven Tests

## The Challenge
Expand Down
2 changes: 2 additions & 0 deletions assert/assert_assertions.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion codegen/internal/generator/doc_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ func (d *DocGenerator) reorganizeByDomain() (iter.Seq2[string, model.Document],
}
weight++

// populate document context in all children
// populate document context in all children: at doc generation time,
// we need the full context to be available when iterating over functions.
doc.Package.Context = &doc
for i, fn := range doc.Package.Functions {
fn.Context = &doc
Expand Down
12 changes: 9 additions & 3 deletions codegen/internal/generator/domains/domains.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package domains

import (
"fmt"
"iter"
"path"
"slices"
Expand All @@ -15,25 +16,30 @@ import (
const (
nodomain = "common"
assertions = "assertions"
maxDepth = 10
)

// FlattenDocumentation flattens a nested documentation structure into a map of packages.
func FlattenDocumentation(documentation model.Documentation) map[string]model.Document {
index := make(map[string]model.Document, len(documentation.Documents))

flattenDocuments(index, documentation.Documents)
flattenDocuments(index, documentation.Documents, 0)

return index
}

func flattenDocuments(index map[string]model.Document, docs []model.Document) {
func flattenDocuments(index map[string]model.Document, docs []model.Document, depth int) {
if depth > maxDepth {
panic(fmt.Errorf("dev error: there is no sensible reason why we should recurse more than %d here", maxDepth))
}

for _, doc := range docs {
key := doc.Package.Package
if _, ok := index[key]; !ok {
index[key] = doc
}

flattenDocuments(index, doc.Documents)
flattenDocuments(index, doc.Documents, depth+1)
}
}

Expand Down
3 changes: 2 additions & 1 deletion codegen/internal/generator/funcmaps/funcmaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@ func relocate(values []model.TestValue, pkg string) string {
return strings.Join(parts, ", ")
}

// Relocate each value
// Relocate each value: the original "assertions" package moves to the
// target package where the value is generated.
relocated := make([]string, 0, len(values))
for _, tv := range values {
// If parse failed, use original (fallback)
Expand Down
5 changes: 3 additions & 2 deletions codegen/internal/generator/funcmaps/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ func markdownLinks(in string) (string, []string) {
// Pattern: [text]: url (at start of line or after whitespace)
refLinks := make(map[string]string)

// Extract all reference links
// Extract all reference links: godoc-style links need to be reworked as proper markdown.
// We extract the detected links (regexp match) before further reprocessing (below).
matches := refLinkPattern.FindAllStringSubmatch(in, -1)
const expectedGroups = 2
for _, match := range matches {
Expand All @@ -70,7 +71,7 @@ func markdownLinks(in string) (string, []string) {
// Remove reference link definitions from input
processed := refLinkPattern.ReplaceAllString(in, "")

// Convert reference-style links to inline links
// Convert reference-style links to inline links.
// Replace [text] with [text](url) where we have the reference
usedRefs := make(map[string]bool)
for refText, refURL := range refLinks {
Expand Down
7 changes: 6 additions & 1 deletion codegen/internal/scanner/comments-parser/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ func ParseTestExamples(text string) []model.Test {
continue
}

// skip until we find the Examples section
// skip until we find the Examples section.
//
// This assumes that the godoc comment is organized in sections ("# {section}"):
// we'll start processing only when we find an "# Example[s]" section.
//
// In this section, we expect to find example values that fit the documented assertion (both success and fail).
if !inExamplesSection {
continue
}
Expand Down
38 changes: 17 additions & 21 deletions codegen/internal/scanner/comments-parser/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ func ParseTestValues(input string) []model.TestValue {
}}
}

// Extract elements from the composite literal
// Extract elements from the composite literal.
// The godoc comment contains testable values as legit go literals.
//
// We parse this snippet and produce a documented version of it [model.TestValue]
// for proper rendering (either as code or as documentation).
compositeLit, ok := expr.(*ast.CompositeLit)
if !ok {
// Should never happen if parser succeeded
Expand All @@ -58,29 +62,11 @@ func ParseTestValues(input string) []model.TestValue {
}

// Convert each element to TestValue
result := make([]model.TestValue, 0, len(compositeLit.Elts))

// We need to extract the original source text for each element
// Since we don't have position info for the original input, we'll format the AST
fset := token.NewFileSet()
for _, elt := range compositeLit.Elts {
// Format the expression back to source code
var buf strings.Builder
if err := format.Node(&buf, fset, elt); err != nil {
result = append(result, model.TestValue{
Raw: "<formatting error>",
Expr: elt,
Error: fmt.Errorf("failed to format expression: %w", err),
})
continue
}

result = append(result, model.TestValue{
Raw: buf.String(),
Expr: elt,
Error: nil,
})
}
result := formatLiteralExpression(compositeLit, fset)

return result
}
Expand Down Expand Up @@ -120,6 +106,16 @@ func ParseExprWithFileSet(fset *token.FileSet, filename string, input string) ([
}

// Convert each element to TestValue
result := formatLiteralExpression(compositeLit, fset)

return result, nil
}

func formatLiteralExpression(compositeLit *ast.CompositeLit, fset *token.FileSet) []model.TestValue {
if compositeLit == nil {
return nil
}

result := make([]model.TestValue, 0, len(compositeLit.Elts))

for _, elt := range compositeLit.Elts {
Expand All @@ -141,5 +137,5 @@ func ParseExprWithFileSet(fset *token.FileSet, filename string, input string) ([
})
}

return result, nil
return result
}
40 changes: 3 additions & 37 deletions codegen/internal/scanner/examples-parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,6 @@ func (x TestableExample) Render() string {
}

return x.renderBody()

// Previous routing: stripped package/imports/main scaffolding.
// if x.WholeFile && x.play != nil {
// return x.renderWholeFile()
// }
// return x.renderBody()
}

// renderPlay renders the Play AST as-is:
Expand Down Expand Up @@ -323,7 +317,9 @@ func (x TestableExample) renderBody() string {
return ""
}

// Print the raw AST node.
// Print the raw AST node as valid, formatted go code.
// This allows to generate code from the testable values
// captured in the godoc of an assertion.
var buf bytes.Buffer
p := printer.Config{Mode: printer.UseSpaces, Tabwidth: tabWidth}
if err := p.Fprint(&buf, x.fset, x.code); err != nil {
Expand Down Expand Up @@ -352,36 +348,6 @@ func (x TestableExample) renderBody() string {
return extractFuncBody(string(formatted))
}

/*
// renderWholeFile renders a whole-file example, stripping the package clause
// and imports, and renaming "func main()" back to the example function name.
func (x TestableExample) renderWholeFile() string {
// Print the entire Play file.
var buf bytes.Buffer
p := printer.Config{Mode: printer.UseSpaces, Tabwidth: tabWidth}
if err := p.Fprint(&buf, x.fset, x.play); err != nil {
return ""
}

raw := buf.String()

// Remove "// Output:" comments.
raw = stripOutputComments(raw)

// Format with goimports.
formatted, err := imports.Process("example.go", []byte(raw), &imports.Options{
Fragment: true,
FormatOnly: true,
})
if err != nil {
formatted = []byte(raw)
}

// Strip package clause and imports, rename main -> Example function.
return extractWholeFileBody(string(formatted), "Example"+x.Name)
}
*/

// extractWholeFileBody strips the package clause and import blocks from a
// formatted Go file, and renames "func main()" to the given example function name.
func extractWholeFileBody(src, exampleFuncName string) string {
Expand Down
8 changes: 5 additions & 3 deletions docs/doc-site/api/number.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,15 @@ func main() {
|--|--|
| [`assertions.InDeltaMapValues(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDeltaMapValues) | internal implementation |

**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaMapValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L367)
**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaMapValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L375)
{{% /tab %}}
{{< /tabs >}}

### InDeltaSlice{#indeltaslice}
InDeltaSlice is the same as [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta), except it compares two slices.

It returns false if the compared slices are not of the same length.

See [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta).

{{% expand title="Examples" %}}
Expand Down Expand Up @@ -394,7 +396,7 @@ func main() {
|--|--|
| [`assertions.InDeltaSlice(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDeltaSlice) | internal implementation |

**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L331)
**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L333)
{{% /tab %}}
{{< /tabs >}}

Expand Down Expand Up @@ -752,7 +754,7 @@ func main() {
|--|--|
| [`assertions.InEpsilonSlice(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSlice) | internal implementation |

**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L422)
**Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L430)
{{% /tab %}}
{{< /tabs >}}

Expand Down
22 changes: 0 additions & 22 deletions internal/assertions/enable/colors/colors.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,6 @@ const (
brightYellowMark = "\033[0;93m" // aka yellow
brightCyanMark = "\033[0;96m" // aka turquoise

// color codes for future use.

// blackMark = "\033[0;30m"
// blueMark = "\033[0;34m"
// magentaMark = "\033[0;35m"
// greyMark = "\033[0;37m".

// darkGreyMark = "\033[0;90m"
// brightBlueMark = "\033[0;94m"
// brightMagentaMark = "\033[0;95m"
// brightWhiteMark = "\033[0;97m".

endMark = "\033[0m"
)

Expand All @@ -56,13 +44,9 @@ func noopColorizer(s string) string {
var (
greenColorizer = makeColorizer(greenMark)
redColorizer = makeColorizer(redMark)
// yellowColorizer = makeColorizer(yellowMark)
// cyanColorizer = makeColorizer(cyanMark).

brightGreenColorizer = makeColorizer(brightGreenMark)
brightRedColorizer = makeColorizer(brightRedMark)
// brightYellowColorizer = makeColorizer(brightYellowMark)
// brightCyanColorizer = makeColorizer(brightCyanMark).
)

//nolint:gochecknoglobals // internal printer builders may safely be shared at the package-level
Expand All @@ -76,12 +60,6 @@ var (
brightRedPrinterBuilder = ansiPrinterBuilder(brightRedMark)
brightYellowPrinterBuilder = ansiPrinterBuilder(brightYellowMark)
brightCyanPrinterBuilder = ansiPrinterBuilder(brightCyanMark)

// magentaPrinterBuilder = ansiPrinterBuilder(magentaMark)
// bluePrinterBuilder = ansiPrinterBuilder(blueMark).

// brightMagentaPrinterBuilder = ansiPrinterBuilder(brightMagentaMark)
// brightBluePrinterBuilder = ansiPrinterBuilder(brightBlueMark).
)

func ansiPrinterBuilder(mark string) difflib.PrinterBuilder {
Expand Down
Loading
Loading