Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
rez1dent3 committed Jul 7, 2024
1 parent e7f7acc commit bf49efc
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 31 deletions.
19 changes: 6 additions & 13 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
run:
go: "1.22"
timeout: 1m
linters:
enable-all: true
disable:
# turn on later
- godox
# deprecated
- nosnakecase
- structcheck
- interfacer
- deadcode
- exhaustivestruct
- maligned
- ifshort
- varcheck
- golint
- scopelint
- gomnd
- execinquery
# not relevant
- varnamelen
- wrapcheck
Expand All @@ -23,7 +16,7 @@ linters:
- dupl
linters-settings:
lll:
line-length: 160
line-length: 140
gci:
sections:
- Standard
Expand All @@ -39,4 +32,4 @@ issues:
exclude-rules:
- path: (.+)_test.go
linters:
- dupl
- dupl
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.PHONY: *

test:
go test -tags mock -race -cover ./...

lint:
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.1 run --color always ${args}

lint-fix:
make lint args=--fix
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
# deeply

Stub filtering algorithms.
`deeply` is a package that provides stub filtering algorithms, which is used in `bavix/gripmock`.

---
Supported by
Stub filtering algorithms is a technique used to filter stubs based on expectations. It is a powerful tool for testing and development.

[![Supported by JetBrains](https://cdn.rawgit.com/bavix/development-through/46475b4b/jetbrains.svg)](https://www.jetbrains.com/)
These algorithms are written in a form of a function that takes an expectation and an actual value and returns a boolean result.

In a nutshell, `Matches` checks if the actual value matches the expectation, `Contains` checks if the actual value contains the expectation, `ContainsIgnoreArrayOrder` checks if the actual value contains the expectation ignoring the order of elements in arrays, and `MatchesIgnoreArrayOrder` checks if the actual value matches the expectation ignoring the order of elements in arrays.

Furthermore, `deeply` provides methods `Equals` and `EqualsIgnoreArrayOrder` that checks if the expected and actual values are deeply equal. The `Equals` method checks if the expected and actual values are deeply equal and ignores the order of arrays, while the `EqualsIgnoreArrayOrder` method checks if the expected and actual values are deeply equal ignoring the order of arrays.

Both `Equals` and `EqualsIgnoreArrayOrder` methods return true if any of the following conditions are met:

- The expected and actual values are both maps and have the same number of keys.
- The expected and actual values are both slices and have the same length.
- The expected and actual values are deeply equal using reflect.DeepEqual.
34 changes: 30 additions & 4 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,67 @@ import (
"reflect"
)

// cmp is a function type used to compare two values.
type cmp func(expect, actual any) bool

// slicesDeepEquals checks if the expected and actual values are deeply equal as slices.
// It returns true if any of the following conditions are met:
// - The expected and actual values are both nil.
// - The expected and actual values are both slices and have the same length.
// - The expected and actual values are deeply equal using the provided compare function.
func slicesDeepEquals(expect, actual reflect.Value, compare cmp) bool {
// Iterate over the values of the expected slice.
for i := range expect.Len() {
// Compare the values of the expected and actual slices.
if !compare(expect.Index(i).Interface(), actual.Index(i).Interface()) {
return false
}
}

// Return true if all values are deeply equal.
return true
}

// slicesDeepEqualContains checks if the expected slice contains all the values of the actual slice.
// It returns true if any of the following conditions are met:
// - The expected and actual values are both nil.
// - The expected and actual values are both slices and have the same length.
// - The expected and actual values are deeply equal using the provided compare function.
func slicesDeepEqualContains(expect, actual reflect.Value, compare cmp) bool {
marks := make([]bool, actual.Len())
res := 0
marks := make([]bool, actual.Len()) // Create a map to keep track of the keys that have been marked as matched.
res := 0 // Initialize the total number of matched values.

// Iterate over the values of the expected slice.
for i := range expect.Len() {
// Iterate over the values of the actual slice.
for j := range actual.Len() {
// Skip the value if it has already been marked as matched.
if !marks[j] && compare(expect.Index(i).Interface(), actual.Index(j).Interface()) {
marks[j] = true
res++
marks[j] = true // Mark the value as matched.
res++ // Increment the total number of matched values.
}
}
}

// Return true if the total number of matched values is equal to the length of the expected slice.
return res == expect.Len()
}

// mapDeepEquals checks if the expected and actual values are deeply equal as maps.
// It returns true if any of the following conditions are met:
// - The expected and actual values are both nil.
// - The expected and actual values are both maps and have the same number of keys.
// - The expected and actual values are deeply equal using the provided compare function.
func mapDeepEquals(expect, actual reflect.Value, compare cmp) bool {
// Iterate over the keys of the expected map.
for _, v := range expect.MapKeys() {
// Check if the actual value has a corresponding key and if the values are deeply equal.
if actual.MapIndex(v).Kind() == reflect.Invalid ||
!compare(expect.MapIndex(v).Interface(), actual.MapIndex(v).Interface()) {
return false
}
}

// Return true if all values are deeply equal.
return true
}
26 changes: 26 additions & 0 deletions contains.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,81 @@ import (
"reflect"
)

// Contains checks if the expected value is contained in the actual value.
// It returns true if any of the following conditions are met:
// - The expected and actual values are deeply equal using reflect.DeepEqual.
// - The expected and actual values are maps and all keys and values in the expected map
// are contained in the actual map.
// - The expected and actual values are slices and the expected slice is completely
// contained in the actual slice.
func Contains(expect, actual any) bool {
return mapDeepContains(expect, actual, Contains) || reflect.DeepEqual(expect, actual)
}

// ContainsIgnoreArrayOrder checks if the expected value is contained in the actual value.
// It returns true if any of the following conditions are met:
// - The expected and actual values are deeply equal using reflect.DeepEqual.
// - The expected and actual values are maps and all keys and values in the expected map
// are contained in the actual map.
// - The expected and actual values are slices and the expected slice is partially
// contained in the actual slice. The order of elements in the slice is not important.
func ContainsIgnoreArrayOrder(expect, actual any) bool {
return mapDeepContains(expect, actual, ContainsIgnoreArrayOrder) ||
slicesDeepContains(expect, actual, ContainsIgnoreArrayOrder) ||
reflect.DeepEqual(expect, actual)
}

// mapDeepContains checks if the expected map is contained in the actual map.
// It returns true if all keys and values in the expected map are contained in the actual map.
func mapDeepContains(expect, actual any, compare cmp) bool {
// Check if the types are the same.
if reflect.TypeOf(expect) != reflect.TypeOf(actual) {
return false
}

// Check if both values are nil.
if reflect.TypeOf(expect) == nil {
return true
}

// Check if both values are maps.
if reflect.TypeOf(expect).Kind() != reflect.Map {
return false
}

left := reflect.ValueOf(expect)
right := reflect.ValueOf(actual)

// Check if the length of the expected map is less than or equal to the length of the actual map.
if left.Len() > right.Len() {
return false
}

return mapDeepEquals(left, right, compare)
}

// slicesDeepContains checks if the expected slice is contained in the actual slice.
// It returns true if the expected slice is completely contained in the actual slice.
func slicesDeepContains(expect, actual any, compare cmp) bool {
// Check if the types are the same.
if reflect.TypeOf(expect) != reflect.TypeOf(actual) {
return false
}

// Check if both values are nil.
if reflect.TypeOf(expect) == nil {
return true
}

// Check if both values are slices.
if reflect.TypeOf(expect).Kind() != reflect.Slice {
return false
}

a := reflect.ValueOf(expect)
b := reflect.ValueOf(actual)

// Check if the length of the expected slice is less than or equal to the length of the actual slice.
if a.Len() > b.Len() {
return false
}
Expand Down
28 changes: 28 additions & 0 deletions equals.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,86 @@ import (
"reflect"
)

// Equals checks if the expected and actual values are deeply equal.
// It returns true if any of the following conditions are met:
// - The expected and actual values are both maps and have the same number of keys.
// - The expected and actual values are both slices and have the same length.
// - The expected and actual values are deeply equal using reflect.DeepEqual.
func Equals(expect, actual any) bool {
return mapDeepEqual(expect, actual, Equals) || reflect.DeepEqual(expect, actual)
}

// EqualsIgnoreArrayOrder checks if the expected and actual values are deeply equal
// ignoring the order of arrays. It behaves similarly to Equals except that it
// uses slicesDeepEqualContains instead of slicesDeepEqual to compare slices.
func EqualsIgnoreArrayOrder(expect, actual any) bool {
return mapDeepEqual(expect, actual, EqualsIgnoreArrayOrder) ||
slicesDeepEqual(expect, actual, EqualsIgnoreArrayOrder) ||
reflect.DeepEqual(expect, actual)
}

// mapDeepEqual checks if the expected and actual values are deeply equal as maps.
// It returns true if any of the following conditions are met:
// - The expected and actual values are both nil.
// - The expected and actual values are both maps and have the same number of keys.
// - The expected and actual values are deeply equal using the provided compare function.
func mapDeepEqual(expect, actual any, compare cmp) bool {
// Check if the types of the expected and actual values are equal.
if reflect.TypeOf(expect) != reflect.TypeOf(actual) {
return false
}

// If both values are nil, return true.
if reflect.TypeOf(expect) == nil {
return true
}

// If the expected value is not a map, return false.
if reflect.TypeOf(expect).Kind() != reflect.Map {
return false
}

left := reflect.ValueOf(expect)
right := reflect.ValueOf(actual)

// If the number of keys in the maps are not equal, return false.
if left.Len() != right.Len() {
return false
}

// Compare the values of the maps.
return mapDeepEquals(left, right, compare)
}

// slicesDeepEqual checks if the expected and actual values are deeply equal as slices.
// It returns true if any of the following conditions are met:
// - The expected and actual values are both nil.
// - The expected and actual values are both slices and have the same length.
// - The expected and actual values are deeply equal using the provided compare function.
func slicesDeepEqual(expect, actual any, compare cmp) bool {
// Check if the types of the expected and actual values are equal.
if reflect.TypeOf(expect) != reflect.TypeOf(actual) {
return false
}

// If both values are nil, return true.
if reflect.TypeOf(expect) == nil {
return true
}

// If the expected value is not a slice, return false.
if reflect.TypeOf(expect).Kind() != reflect.Slice {
return false
}

a := reflect.ValueOf(expect)
b := reflect.ValueOf(actual)

// If the lengths of the slices are not equal, return false.
if a.Len() != b.Len() {
return false
}

// Compare the values of the slices.
return slicesDeepEqualContains(a, b, compare)
}
Loading

0 comments on commit bf49efc

Please sign in to comment.