Skip to content

Commit

Permalink
ARM64/Linux support (#4)
Browse files Browse the repository at this point in the history
* Support for ARM64 on Linux
* Test coverage bumped to 95%
  • Loading branch information
qrdl authored Feb 10, 2024
1 parent 3f77089 commit 7d9040a
Show file tree
Hide file tree
Showing 11 changed files with 738 additions and 49 deletions.
9 changes: 7 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,10 @@ jobs:
with:
go-version-file: './go.mod'

- name: Test
run: go test -v -gcflags=-l -p=1 ./...
- name: Test examples
run: go test -v -gcflags="all=-N -l" ./examples/...

- name: Test coverage
run: |
go test -v -gcflags="all=-N -l" -coverprofile=coverage.out ./.
go tool cover -func coverage.out | grep total: | awk '{print "Test coverage: " $3}'
16 changes: 8 additions & 8 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
![Tests](https://github.com/qrdl/testaroli/actions/workflows/go.yml/badge.svg)
![CodeQL](https://github.com/qrdl/testaroli/workflows/CodeQL/badge.svg)

Package `testaroli` allows to monkey patch Go test binary, e.g. override functions and methods with stubs/mocks to simplify unit testing.
Package `testaroli` allows to [monkey patch](https://en.wikipedia.org/wiki/Monkey_patch) Go test binary, e.g. override functions and methods with stubs/mocks to simplify unit testing.
It can be used only for unit testing and never in production.

## Platforms suported
Expand All @@ -14,18 +14,18 @@ This package modifies actual executable at runtime, therefore is OS- and CPU arc

OS/arch combinations:

| | x86_64 | ARM64 |
|---------|-----------|---------|
| Linux | Supported | Planned |
| Windows | Supported | - |
| macOS | Planned | Planned |
| | x86_64 | ARM64 |
|---------|-----------|-----------|
| Linux | Supported | Supported |
| Windows | Supported | - |
| macOS | Planned | Planned |


## Command line options

Inlined functions cannot be overridden, so to prevent inlining use `-gcflags=all=-l` CLI option when running tests, like this:
It is recommended to switch off compiler optimisations and disable function inlining using `-gcflags="all=-N -l"` CLI option when running tests, like this:

`go test -gcflags=all=-l [<path>]`
`go test -gcflags="all=-N -l" [<path>]`

Typical use:
```
Expand Down
272 changes: 272 additions & 0 deletions equal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
package testaroli

import (
"reflect"
"testing"
)

type testCase struct {
name string
expected reflect.Value
actual reflect.Value
equal bool
message string
}

func TestBasicTypes(t *testing.T) {
cases := []testCase{
{
"equal bool", reflect.ValueOf(true), reflect.ValueOf(true), true, "",
},
{
"non-equal bool", reflect.ValueOf(true), reflect.ValueOf(false), false, "",
},
{
"equal int", reflect.ValueOf(int(1)), reflect.ValueOf(int(1)), true, "",
},
{
"non-equal int", reflect.ValueOf(int(1)), reflect.ValueOf(int(2)), false, "",
},
{
"equal int8", reflect.ValueOf(int8(1)), reflect.ValueOf(int8(1)), true, "",
},
{
"non-equal int8", reflect.ValueOf(int8(1)), reflect.ValueOf(int8(2)), false, "",
},
{
"equal int16", reflect.ValueOf(int16(1)), reflect.ValueOf(int16(1)), true, "",
},
{
"non-equal int16", reflect.ValueOf(int16(1)), reflect.ValueOf(int16(2)), false, "",
},
{
"equal int32", reflect.ValueOf(int32(1)), reflect.ValueOf(int32(1)), true, "",
},
// rune is an alias for int32 so no need to test it separately
{
"non-equal int32", reflect.ValueOf(int32(1)), reflect.ValueOf(int32(2)), false, "",
},
{
"equal int64", reflect.ValueOf(int64(1)), reflect.ValueOf(int64(1)), true, "",
},
{
"non-equal int64", reflect.ValueOf(int64(1)), reflect.ValueOf(int64(2)), false, "",
},
{
"equal uint", reflect.ValueOf(uint(1)), reflect.ValueOf(uint(1)), true, "",
},
{
"non-equal uint", reflect.ValueOf(uint(1)), reflect.ValueOf(uint(2)), false, "",
},
{
"equal uint8", reflect.ValueOf(uint8(1)), reflect.ValueOf(uint8(1)), true, "",
},
{
"non-equal uint8", reflect.ValueOf(uint8(1)), reflect.ValueOf(uint8(2)), false, "",
},
{
"equal uint16", reflect.ValueOf(uint16(1)), reflect.ValueOf(uint16(1)), true, "",
},
{
"non-equal uint16", reflect.ValueOf(uint16(1)), reflect.ValueOf(uint16(2)), false, "",
},
{
"equal uint32", reflect.ValueOf(uint32(1)), reflect.ValueOf(uint32(1)), true, "",
},
{
"non-equal uint32", reflect.ValueOf(uint32(1)), reflect.ValueOf(uint32(2)), false, "",
},
{
"equal uint64", reflect.ValueOf(uint64(1)), reflect.ValueOf(uint64(1)), true, "",
},
{
"non-equal uint64", reflect.ValueOf(uint64(1)), reflect.ValueOf(uint64(2)), false, "",
},
{
"equal uintptr", reflect.ValueOf(uintptr(1)), reflect.ValueOf(uintptr(1)), true, "",
},
{
"non-equal uintptr", reflect.ValueOf(uintptr(1)), reflect.ValueOf(uintptr(2)), false, "",
},
{
"equal float32", reflect.ValueOf(float32(1.5)), reflect.ValueOf(float32(1.5)), true, "",
},
{
"non-equal float32", reflect.ValueOf(float32(1.5)), reflect.ValueOf(float32(2.5)), false, "",
},
{
"equal float64", reflect.ValueOf(float64(1.5)), reflect.ValueOf(float64(1.5)), true, "",
},
{
"non-equal float64", reflect.ValueOf(float64(1.5)), reflect.ValueOf(float64(2.5)), false, "",
},
{
"equal complex64", reflect.ValueOf(complex(1, 2)), reflect.ValueOf(complex(1, 2)), true, "",
},
{
"non-equal complex64", reflect.ValueOf(complex64(1 + 2i)), reflect.ValueOf(complex64(1 + 4i)), false, "",
},
{
"equal complex128", reflect.ValueOf(complex(1, 2)), reflect.ValueOf(complex(1, 2)), true, "",
},
{
"non-equal complex128", reflect.ValueOf(complex(1, 2)), reflect.ValueOf(complex(1, 4)), false, "",
},
{
"equal string", reflect.ValueOf("foo"), reflect.ValueOf("foo"), true, "",
},
{
"non-equal string", reflect.ValueOf("foo"), reflect.ValueOf("bar"), false, "",
},
{
"different types", reflect.ValueOf(1), reflect.ValueOf("bar"), false, "",
},
}

for _, c := range cases {
res, _ := equal(c.actual, c.expected)
if res != c.equal {
t.Errorf("Case '%s' result mismatched", c.name)
}
}
}

func TestCompositeTypes(t *testing.T) {
chan1 := make(chan int)
chan2 := make(chan int)
ptr1 := new(int)
ptr2 := new(int)
ptr3 := new(int)
*ptr1 = 1
*ptr2 = 1
*ptr3 = 3
arr1 := [...]int{1, 2}
arr2 := [...]int{1, 2}
arr3 := [...]int{1, 2, 3}
arr4 := [...]int{1, 3}
zeroArr := [...]int{}
str1 := struct {
a int
b string
}{5, "foo"}
str2 := struct {
a int
b string
}{5, "foo"}
str3 := struct {
a int
b string
}{5, "bar"}
str4 := struct {
a int
b string
c bool
}{5, "foo", true}
map1 := map[int]string{1: "foo", 2: "bar"}
map2 := map[int]string{1: "foo", 2: "bar"}
map3 := map[int]string{1: "foo", 3: "bar"}
map4 := map[int]string{1: "foo", 2: "baz"}
map5 := map[int]string{1: "foo", 2: "bar", 3: "baz"}
map6 := map[int]int{1: 42}
sl1 := []int{1, 2}
sl2 := []int{1, 2}
sl3 := []int{1, 2, 3}
sl4 := []int{2, 1}
sl5 := []float32{1, 2}
f := func(_ int) {}

cases := []testCase{
// channel can only match to itself
{
"same channel", reflect.ValueOf(chan1), reflect.ValueOf(chan1), true, "",
},
{
"different channel", reflect.ValueOf(chan1), reflect.ValueOf(chan2), false, "",
},
{
"same pointer", reflect.ValueOf(ptr1), reflect.ValueOf(ptr1), true, "",
},
{
"pointer with the same value", reflect.ValueOf(ptr1), reflect.ValueOf(ptr2), true, "",
},
{
"pointer with the different value", reflect.ValueOf(ptr1), reflect.ValueOf(ptr3), false, "",
},
{
"same array", reflect.ValueOf(arr1), reflect.ValueOf(arr1), true, "",
},
{
"matching array", reflect.ValueOf(arr1), reflect.ValueOf(arr2), true, "",
},
{
"array of diff length", reflect.ValueOf(arr1), reflect.ValueOf(arr3), false, "",
},
{
"zero array", reflect.ValueOf(zeroArr), reflect.ValueOf(zeroArr), true, "",
},
{
"non-matching array", reflect.ValueOf(arr1), reflect.ValueOf(arr4), false, "",
},
{
"same struct", reflect.ValueOf(str1), reflect.ValueOf(str1), true, "",
},
{
"matching struct", reflect.ValueOf(str1), reflect.ValueOf(str2), true, "",
},
{
"struct of diff type", reflect.ValueOf(str1), reflect.ValueOf(str4), false, "",
},
{
"struct with different fields", reflect.ValueOf(str1), reflect.ValueOf(str3), false, "",
},
{
"same map", reflect.ValueOf(map1), reflect.ValueOf(map1), true, "",
},
{
"matching map", reflect.ValueOf(map1), reflect.ValueOf(map2), true, "",
},
{
"map with different keys", reflect.ValueOf(map1), reflect.ValueOf(map3), false, "",
},
{
"map with different values", reflect.ValueOf(map1), reflect.ValueOf(map4), false, "",
},
{
"map with extra keys", reflect.ValueOf(map1), reflect.ValueOf(map5), false, "",
},
{
"map of different type", reflect.ValueOf(map1), reflect.ValueOf(map6), false, "",
},
{
"same func", reflect.ValueOf(f), reflect.ValueOf(f), true, "",
},
{
"func", reflect.ValueOf(func() {}), reflect.ValueOf(func() {}), false, "",
},
{
"same slice", reflect.ValueOf(sl1), reflect.ValueOf(sl1), true, "",
},
{
"matching slice", reflect.ValueOf(sl1), reflect.ValueOf(sl2), true, "",
},
{
"longer slice", reflect.ValueOf(sl1), reflect.ValueOf(sl3), false, "",
},
{
"slice with different order of elements", reflect.ValueOf(sl1), reflect.ValueOf(sl4), false, "",
},
{
"slice of different base type", reflect.ValueOf(sl1), reflect.ValueOf(sl5), false, "",
},
{
"zero slice", reflect.ValueOf(sl1[:0]), reflect.ValueOf(sl3[:0]), true, "",
},
}

for _, c := range cases {
res, _ := equal(c.actual, c.expected)
if res != c.equal {
t.Errorf("Case '%s' result mismatched", c.name)
}
}
}
7 changes: 5 additions & 2 deletions examples/functions/functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,17 @@ func TestNoEnoughFunds(t *testing.T) {
testError(t, nil, mock.ExpectationsWereMet())
}

type contextKey int
const key = contextKey(1)

func TestNotCreditable(t *testing.T) {
mock := testaroli.New(context.WithValue(context.TODO(), 1, "1024"), t)
mock := testaroli.New(context.WithValue(context.TODO(), key, "1024"), t)
defer func() { testError(t, nil, mock.ExpectationsWereMet()) }()

testaroli.Override(2, accStatus, func(acc string) AccStatus {
f := testaroli.Expectation()
if f.RunNumber() == 0 {
f.Expect(f.Context().Value(1).(string))
f.Expect(f.Context().Value(key).(string))
} else {
f.Expect("2048")
}
Expand Down
24 changes: 9 additions & 15 deletions expect.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,6 @@ func equal(a, e reflect.Value) (bool, string) {
// u and v have the same type so they have the same length
vl := a.Len()
if vl == 0 {
// error reported on exit from func
if !a.Type().Elem().Comparable() {
break
}
return true, ""
}
for i := 0; i < vl; i++ {
Expand Down Expand Up @@ -245,6 +241,9 @@ func equal(a, e reflect.Value) (bool, string) {
}
return true, ""
case reflect.Map: // my change
if a.Pointer() == e.Pointer() {
return true, ""
}
keys := a.MapKeys()
if len(keys) != len(e.MapKeys()) {
return false, "map lengths differ"
Expand All @@ -261,20 +260,17 @@ func equal(a, e reflect.Value) (bool, string) {
}
return true, ""
case reflect.Func:
break
return a.Pointer() == e.Pointer(), ""
// function can be equal only to itself
case reflect.Slice: // my change
if a.Pointer() == e.Pointer() {
return true, ""
}
vl := a.Len()
if vl != e.Len() {
return false, "slice lengths differ"
}
if a.Pointer() == e.Pointer() {
return true, ""
}
if vl == 0 {
// error reported on exit from func
if !a.Type().Elem().Comparable() {
break
}
return true, ""
}
for i := 0; i < vl; i++ {
Expand All @@ -288,8 +284,6 @@ func equal(a, e reflect.Value) (bool, string) {
}
}
return true, ""
default:
return false, "invalid variable Kind"
}
return false, "values of type " + a.Type().String() + " are not comparable"
return false, "invalid variable Kind"
}
Loading

0 comments on commit 7d9040a

Please sign in to comment.