Skip to content

Commit

Permalink
implement unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
koron committed Jul 30, 2024
1 parent 41e61da commit 59dce42
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 40 deletions.
68 changes: 28 additions & 40 deletions inflater.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,39 +33,35 @@ func Keep[V any]() Inflater[V] {
})
}

func Map[V any](inflater Inflater[V], apply func(V) V) Inflater[V] {
func Map[V any](apply func(V) V) Inflater[V] {
if apply == nil {
return inflater
return Keep[V]()
}
return InflaterFunc[V](func(seed V) iter.Seq[V] {
return func(yield func(V) bool) {
for v := range inflater.Inflate(seed) {
if !yield(apply(v)) {
return
}
if !yield(apply(seed)) {
return
}
}
})
}

// Filter provides an Inflater which inflate a seed if check(seed) returns true.
func Filter[V any](inflater Inflater[V], check func(V) bool) Inflater[V] {
func Filter[V any](check func(V) bool) Inflater[V] {
if check == nil {
return inflater
return Keep[V]()
}
return InflaterFunc[V](func(seed V) iter.Seq[V] {
return func(yield func(V) bool) {
for v := range inflater.Inflate(seed) {
if check(v) && !yield(v) {
return
}
if check(seed) && !yield(seed) {
return
}
}
})
}

// Join2 creates an Inflater with distribute a seed to two Inflaters.
func Join2[V any](first, second Inflater[V]) Inflater[V] {
// Parallel2 creates an Inflater with distribute a seed to two Inflaters.
func Parallel2[V any](first, second Inflater[V]) Inflater[V] {
return InflaterFunc[V](func(seed V) iter.Seq[V] {
return func(yield func(V) bool) {
for s := range first.Inflate(seed) {
Expand All @@ -82,27 +78,23 @@ func Join2[V any](first, second Inflater[V]) Inflater[V] {
})
}

// Join creates an Inflater which distibute a seed to multiple Inflaters.
func Join[V any](inflaters ...Inflater[V]) Inflater[V] {
// Parallel creates an Inflater which distibute a seed to multiple Inflaters.
func Parallel[V any](inflaters ...Inflater[V]) Inflater[V] {
switch len(inflaters) {
case 0:
return None[V]()
case 1:
return inflaters[0]
case 2:
return Join2(inflaters[0], inflaters[1])
return Parallel2(inflaters[0], inflaters[1])
default:
return Join2(inflaters[0], Join(inflaters[1:]...))
return Parallel2(inflaters[0], Parallel(inflaters[1:]...))
}
}

func JoinSeed[V any](inflaters ...Inflater[V]) Inflater[V] {
return Join2[V](Keep[V](), Join(inflaters...))
}

// Reinflate2 creates an Inflater that inflates the result of the first
// Serial2 creates an Inflater that inflates the result of the first
// Inflater with the second Inflater.
func Reinflate2[V any](first, second Inflater[V]) Inflater[V] {
func Serial2[V any](first, second Inflater[V]) Inflater[V] {
return InflaterFunc[V](func(seed V) iter.Seq[V] {
return func(yield func(V) bool) {
for s := range first.Inflate(seed) {
Expand All @@ -116,51 +108,47 @@ func Reinflate2[V any](first, second Inflater[V]) Inflater[V] {
})
}

// Reinflate creates an Inflater that applies multiple Inflaters in sequence to
// Serial creates an Inflater that applies multiple Inflaters in sequence to
// its result repeatedly.
func Reinflate[V any](inflaters ...Inflater[V]) Inflater[V] {
func Serial[V any](inflaters ...Inflater[V]) Inflater[V] {
switch len(inflaters) {
case 0:
return None[V]()
case 1:
return inflaters[0]
case 2:
return Reinflate2[V](inflaters[0], inflaters[1])
return Serial2[V](inflaters[0], inflaters[1])
default:
return Reinflate2[V](inflaters[0], Reinflate(inflaters[1:]...))
return Serial2[V](inflaters[0], Serial(inflaters[1:]...))
}
}

// Prefix provides an Inflater which inflate with prefixes.
func Prefix[V ~string](inflater Inflater[V], prefixes ...V) Inflater[V] {
func Prefix[V ~string](prefixes ...V) Inflater[V] {
if len(prefixes) == 0 {
return None[V]()
}
return InflaterFunc[V](func(seed V) iter.Seq[V] {
return func(yield func(V) bool) {
for v := range inflater.Inflate(seed) {
for _, prefix := range prefixes {
if !yield(prefix + v) {
return
}
for _, prefix := range prefixes {
if !yield(prefix + seed) {
return
}
}
}
})
}

// Suffix provides an Inflater which inflate with suffixes.
func Suffix[V ~string](inflater Inflater[V], suffixes ...V) Inflater[V] {
func Suffix[V ~string](suffixes ...V) Inflater[V] {
if len(suffixes) == 0 {
return None[V]()
}
return InflaterFunc[V](func(seed V) iter.Seq[V] {
return func(yield func(V) bool) {
for v := range inflater.Inflate(seed) {
for _, suffix := range suffixes {
if !yield(v + suffix) {
return
}
for _, suffix := range suffixes {
if !yield(seed + suffix) {
return
}
}
}
Expand Down
203 changes: 203 additions & 0 deletions inflater_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package inflater_test

import (
"iter"
"strings"
"testing"

"github.com/koron-go/inflater"
)

type staticInflater []string

func (iter staticInflater) Inflate(string) iter.Seq[string] {
return func(yield func(string) bool) {
for _, s := range iter {
if !yield(s) {
return
}
}
}
}

var static1 = staticInflater{"aaa", "bbb", "ccc"}
var static2 = staticInflater{"111", "222", "333"}

func testInflater[T ~string | ~int](t *testing.T, target inflater.Inflater[T], seed T, wants ...T) {
i := 0
for got := range target.Inflate(seed) {
if len(wants) == 0 {
t.Fatal("length of wants array is insufficient")
}
want := wants[0]
wants = wants[1:]
if got != want {
t.Errorf("#%d unmatch: want=%+v got=%+v", i, want, got)
}
i++
}
if len(wants) > 0 {
t.Helper()
t.Errorf("wants array have extra elements")
}
}

func TestStatic(t *testing.T) {
testInflater(t, static1, "", "aaa", "bbb", "ccc")
testInflater(t, static1, "dummy", "aaa", "bbb", "ccc")
testInflater(t, static2, "", "111", "222", "333")
testInflater(t, static2, "dummy", "111", "222", "333")
}

func TestPrefix(t *testing.T) {
testInflater(t, inflater.Prefix("1", "2", "3"), "",
"1", "2", "3")
testInflater(t, inflater.Prefix("XXX", "YYY", "ZZZ"), "seed",
"XXXseed", "YYYseed", "ZZZseed")

testInflater(t, inflater.Prefix("ONLY"), "", "ONLY")
testInflater(t, inflater.Prefix("ONLY"), "seed", "ONLYseed")

// no prefixes, no outputs
testInflater(t, inflater.Prefix[string](), "")
testInflater(t, inflater.Prefix[string](), "seed")
}

func TestSuffix(t *testing.T) {
testInflater(t, inflater.Suffix("1", "2", "3"), "",
"1", "2", "3")
testInflater(t, inflater.Suffix("XXX", "YYY", "ZZZ"), "seed",
"seedXXX", "seedYYY", "seedZZZ")

testInflater(t, inflater.Suffix("ONLY"), "", "ONLY")
testInflater(t, inflater.Suffix("ONLY"), "seed", "seedONLY")

// no suffixes, no outputs
testInflater(t, inflater.Suffix[string](), "")
testInflater(t, inflater.Suffix[string](), "seed")
}

func TestSerial2(t *testing.T) {
testInflater(t, inflater.Serial2(static1, inflater.Suffix("1", "2", "3")), "",
"aaa1", "aaa2", "aaa3", "bbb1", "bbb2", "bbb3", "ccc1", "ccc2", "ccc3")

testInflater(t, inflater.Serial2(static1, static2), "",
"111", "222", "333", "111", "222", "333", "111", "222", "333")
}

func TestParallel2(t *testing.T) {
testInflater(t, inflater.Parallel2(static1, static2), "",
"aaa", "bbb", "ccc", "111", "222", "333")
}

func TestNone(t *testing.T) {
testInflater(t, inflater.None[string](), "")
testInflater(t, inflater.None[string](), "seed")

testInflater(t, inflater.Serial2(static1, inflater.None[string]()), "")
}

func TestKeep(t *testing.T) {
testInflater(t, inflater.Keep[string](), "", "")
testInflater(t, inflater.Keep[string](), "seed", "seed")

testInflater(t, inflater.Serial2(static1, inflater.Keep[string]()), "", "aaa", "bbb", "ccc")
}

func TestMap(t *testing.T) {
testInflater(t, inflater.Serial2(static1, inflater.Map(strings.ToUpper)), "", "AAA", "BBB", "CCC")

testInflater(t, inflater.Serial2(static1, inflater.Map[string](nil)), "", "aaa", "bbb", "ccc")
}

func TestFilter(t *testing.T) {
testInflater(t, inflater.Serial2(static1, inflater.Filter(func(s string) bool {
return s > "aaa"
})), "", "bbb", "ccc")
testInflater(t, inflater.Serial2(static1, inflater.Filter(func(s string) bool {
return s != "bbb"
})), "", "aaa", "ccc")
testInflater(t, inflater.Serial2(static1, inflater.Filter(func(s string) bool {
return s == "bbb"
})), "", "bbb")

testInflater(t, inflater.Serial2(static1, inflater.Filter[string](nil)), "", "aaa", "bbb", "ccc")
}

func TestParallel(t *testing.T) {
t.Run("0", func(t *testing.T) {
testInflater(t, inflater.Parallel[string](), "")
testInflater(t, inflater.Parallel[string](), "seed")
})
t.Run("1", func(t *testing.T) {
testInflater(t, inflater.Parallel(static1), "", "aaa", "bbb", "ccc")
testInflater(t, inflater.Parallel(static2), "", "111", "222", "333")
})
t.Run("2", func(t *testing.T) {
testInflater(t, inflater.Parallel(static1, static2), "", "aaa", "bbb", "ccc", "111", "222", "333")
testInflater(t, inflater.Parallel(static2, static1), "", "111", "222", "333", "aaa", "bbb", "ccc")
})
t.Run("2+", func(t *testing.T) {
testInflater(t, inflater.Parallel(static1, static2, inflater.Keep[string]()), "seed",
"aaa", "bbb", "ccc", "111", "222", "333", "seed")
testInflater(t, inflater.Parallel(static1, static2, inflater.Keep[string](), static1), "seed",
"aaa", "bbb", "ccc", "111", "222", "333", "seed", "aaa", "bbb", "ccc")
})
}

func TestSerial(t *testing.T) {
t.Run("0", func(t *testing.T) {
testInflater(t, inflater.Serial[string](), "")
testInflater(t, inflater.Serial[string](), "seed")
})
t.Run("1", func(t *testing.T) {
testInflater(t, inflater.Serial(static1), "", "aaa", "bbb", "ccc")
testInflater(t, inflater.Serial(static2), "", "111", "222", "333")
})
t.Run("2", func(t *testing.T) {
testInflater(t, inflater.Serial(static1, inflater.Suffix("1", "2", "3")), "",
"aaa1", "aaa2", "aaa3", "bbb1", "bbb2", "bbb3", "ccc1", "ccc2", "ccc3")
testInflater(t, inflater.Serial(static1, static2), "",
"111", "222", "333", "111", "222", "333", "111", "222", "333")
})
t.Run("2+", func(t *testing.T) {
testInflater(t, inflater.Serial(static1, static2, inflater.Keep[string]()), "seed",
"111", "222", "333", "111", "222", "333", "111", "222", "333")
})
}

func TestMigemoGraph(t *testing.T) {
var (
keep = inflater.Keep[string]()

roma2hira = inflater.Suffix(":roma2hira")
hira2kata = inflater.Suffix(":hira2kata")
wide2narrow = inflater.Suffix(":wide2narrow")
narrow2wide = inflater.Suffix(":narrow2wide")
dict = inflater.Suffix(":dict")
)
compose := inflater.Parallel(
keep,
dict,
inflater.Serial(roma2hira, inflater.Parallel(
keep,
dict,
inflater.Serial(hira2kata, inflater.Parallel(
keep,
dict,
wide2narrow,
)),
)),
narrow2wide,
)
testInflater(t, compose, "query",
"query",
"query:dict",
"query:roma2hira",
"query:roma2hira:dict",
"query:roma2hira:hira2kata",
"query:roma2hira:hira2kata:dict",
"query:roma2hira:hira2kata:wide2narrow",
"query:narrow2wide",
)
}

0 comments on commit 59dce42

Please sign in to comment.