Skip to content

Commit

Permalink
Add All/Any assertions
Browse files Browse the repository at this point in the history
Closes #2
  • Loading branch information
rliebz committed Jan 25, 2024
1 parent fd8c0d1 commit ef2d634
Show file tree
Hide file tree
Showing 4 changed files with 287 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ g.Should(be.Eventually(func() ghost.Result {
}, 3*time.Second, 100*time.Millisecond))
```

For details on other composers such as `be.Any` or `be.All`, see the [godoc][].

#### Custom Assertions

Custom assertions are easy to write and easy to use.
Expand Down
68 changes: 68 additions & 0 deletions be/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,80 @@ package be

import (
"fmt"
"regexp"
"strings"
"time"

"github.com/rliebz/ghost"
"github.com/rliebz/ghost/ghostlib"
)

// All asserts that every one of the provided assertions is true.
func All(results ...ghost.Result) ghost.Result {
args := ghostlib.ArgsFromAST(results)
return applyVariadicBooleanLogic(
true,
func(acc, val bool) bool {
return acc && val
},
results,
args,
)
}

// Any asserts that at least one of the provided assertions is true.
func Any(results ...ghost.Result) ghost.Result {
args := ghostlib.ArgsFromAST(results)
return applyVariadicBooleanLogic(
false,
func(acc, val bool) bool {
return acc || val
},
results,
args,
)
}

func applyVariadicBooleanLogic(
initial bool,
apply func(acc, val bool) bool,
results []ghost.Result,
args []string,
) ghost.Result {
if len(results) == 0 {
return ghost.Result{
Ok: initial,
Message: "no assertions were provided",
}
}

out := ghost.Result{Ok: initial}
for i, result := range results {
out.Ok = apply(out.Ok, result.Ok)

var b strings.Builder
if i != 0 {
b.WriteString("\n\n")
}
fmt.Fprintf(&b, "assertion `%s` is %t", args[i], result.Ok)
b.WriteString("\n\t")
b.WriteString(indentString(result.Message))

out.Message += b.String()
}

return out
}

var reWhitespaceLine = regexp.MustCompile(`\n[ \t]+\n`)

func indentString(s string) string {
s = strings.ReplaceAll(s, "\n", "\n\t")
s = reWhitespaceLine.ReplaceAllString(s, "\n\n")
s = strings.TrimSpace(s)
return s
}

// Eventually asserts that a function eventually returns an Ok [ghost.Result].
func Eventually(
f func() ghost.Result,
Expand Down
190 changes: 190 additions & 0 deletions be/compose_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,203 @@
package be_test

import (
"fmt"
"testing"
"time"

"github.com/rliebz/ghost"
"github.com/rliebz/ghost/be"
)

func TestAll(t *testing.T) {
t.Run("no arguments passed", func(t *testing.T) {
g := ghost.New(t)

result := be.All()
g.Should(be.True(result.Ok))
g.Should(be.Equal(result.Message, "no assertions were provided"))
})

t.Run("one valid", func(t *testing.T) {
g := ghost.New(t)

result := be.All(
be.Equal(1, 0),
be.Equal(1, 1),
be.Equal(1, 2),
)

g.Should(be.False(result.Ok))
g.Should(be.Equal(
result.Message,
fmt.Sprintf(`assertion %s is false
1 != 0
got: 1
want: 0
assertion %s is true
1 == 1
assertion %s is false
1 != 2
got: 1
want: 2`,
"`be.Equal(1, 0)`",
"`be.Equal(1, 1)`",
"`be.Equal(1, 2)`",
),
))
})

t.Run("all valid", func(t *testing.T) {
g := ghost.New(t)

result := be.All(
be.Equal(1, 1),
be.Equal(2, 2),
)

g.Should(be.True(result.Ok))
g.Should(be.Equal(
result.Message,
fmt.Sprintf("assertion %s is true"+`
1 == 1
assertion %s is true
2 == 2`,
"`be.Equal(1, 1)`",
"`be.Equal(2, 2)`",
),
))
})

t.Run("nested", func(t *testing.T) {
g := ghost.New(t)

result := be.Any(
be.Any(
be.Equal(1, 0),
be.Equal(1, 2),
),
)

g.Should(be.False(result.Ok))
g.Should(be.Equal(
result.Message,
fmt.Sprintf(`assertion %s is false
assertion %s is false
1 != 0
got: 1
want: 0
assertion %s is false
1 != 2
got: 1
want: 2`,
"`be.Any(be.Equal(1, 0), be.Equal(1, 2))`",
"`be.Equal(1, 0)`",
"`be.Equal(1, 2)`",
),
))
})
}

Check failure on line 103 in be/compose_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not `gofumpt`-ed (gofumpt)
func TestAny(t *testing.T) {
t.Run("no arguments passed", func(t *testing.T) {
g := ghost.New(t)

result := be.Any()
g.Should(be.False(result.Ok))
g.Should(be.Equal(result.Message, "no assertions were provided"))
})

t.Run("one valid", func(t *testing.T) {
g := ghost.New(t)

result := be.Any(
be.Equal(1, 0),
be.Equal(1, 1),
be.Equal(1, 2),
)

g.Should(be.True(result.Ok))
g.Should(be.Equal(
result.Message,
fmt.Sprintf(`assertion %s is false
1 != 0
got: 1
want: 0
assertion %s is true
1 == 1
assertion %s is false
1 != 2
got: 1
want: 2`,
"`be.Equal(1, 0)`",
"`be.Equal(1, 1)`",
"`be.Equal(1, 2)`",
),
))
})

t.Run("none valid", func(t *testing.T) {
g := ghost.New(t)

result := be.Any(
be.Equal(1, 0),
be.Equal(1, 2),
)

g.Should(be.False(result.Ok))
g.Should(be.Equal(
result.Message,
fmt.Sprintf("assertion %s is false"+`
1 != 0
got: 1
want: 0
assertion %s is false
1 != 2
got: 1
want: 2`,
"`be.Equal(1, 0)`",
"`be.Equal(1, 2)`",
),
))
})

t.Run("nested", func(t *testing.T) {
g := ghost.New(t)

result := be.Any(
be.Any(
be.Equal(1, 0),
be.Equal(1, 2),
),
)

g.Should(be.False(result.Ok))
g.Should(be.Equal(
result.Message,
fmt.Sprintf(`assertion %s is false
assertion %s is false
1 != 0
got: 1
want: 0
assertion %s is false
1 != 2
got: 1
want: 2`,
"`be.Any(be.Equal(1, 0), be.Equal(1, 2))`",
"`be.Equal(1, 0)`",
"`be.Equal(1, 2)`",
),
))
})
}

func TestEventually(t *testing.T) {
g := ghost.New(t)

Expand Down
27 changes: 27 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,37 @@ func TestExample(t *testing.T) {

g.Should(be.JSONEqual(`{"b": 1, "a": 0}`, `{"a": 0, "b": 1}`))
g.ShouldNot(be.JSONEqual(`{"a":1}`, `{"a":2}`))
}

func ExampleEventually() {
t := new(testing.T) // from the test
g := ghost.New(t)

count := 0
g.Should(be.Eventually(func() ghost.Result {
count++
return be.Equal(count, 3)
}, 100*time.Millisecond, 10*time.Millisecond))
}

func ExampleAny() {
t := new(testing.T) // from the test
g := ghost.New(t)

g.Should(be.Any(
be.Equal(0, 1),
be.Equal(1, 1),
be.Equal(2, 1),
))
}

func ExampleAll() {
t := new(testing.T) // from the test
g := ghost.New(t)

g.Should(be.All(
be.Equal(1, 1),
be.Equal(2, 2),
be.Equal(3, 3),
))
}

0 comments on commit ef2d634

Please sign in to comment.