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
133 changes: 133 additions & 0 deletions extras/integration_test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
## Integration test for the RPC library

This is a test that runs on the Arduino Zero board. It could be easily extended to other boards with multiple serial ports.

### Running the test

Prerequisites:

* An Arduino Zero board, connected with both the USB ports.
* `arduino-cli`
* The platform `arduino:samd` installed through the `arduino-cli`.
* The dependencies of the RPClite library installed through the `arduino-cli`.
* A working `go` programming language compiler.

To run the test, from a terminal change directory to this folder, and run:

`go test -v`

it should compile and upload the test sketch, and perform the RPC call tests.

The output should look similar to the following:

```
RpcLite/test/RpcClientTest$ go test -v
=== RUN TestBasicComm
Lo sketch usa 27028 byte (10%) dello spazio disponibile per i programmi. Il massimo è 262144 byte.
Le variabili globali usano 4040 byte (12%) di memoria dinamica, lasciando altri 28728 byte liberi per le variabili locali. Il massimo è 32768 byte.
Atmel SMART device 0x10010005 found
Device : ATSAMD21G18A
Chip ID : 10010005
Version : v2.0 [Arduino:XYZ] Apr 11 2019 13:09:49
Address : 8192
Pages : 3968
Page Size : 64 bytes
Total Size : 248KB
Planes : 1
Lock Regions : 16
Locked : none
Security : false
Boot Flash : true
BOD : true
BOR : true
Arduino : FAST_CHIP_ERASE
Arduino : FAST_MULTI_PAGE_WRITE
Arduino : CAN_CHECKSUM_MEMORY_BUFFER
Erase flash
done in 0.873 seconds

Write 27028 bytes to flash (423 pages)
[==============================] 100% (423/423 pages)
done in 0.155 seconds

Verify 27028 bytes of flash with checksum.
Verify successful
done in 0.026 seconds
CPU reset.
=== RUN TestBasicComm/RPCClientCallFloatArgs
/dev/ttyACM0 READ << 94
/dev/ttyACM0 READ << 0001
/dev/ttyACM0 READ << a4
/dev/ttyACM0 READ << 6d75
/dev/ttyACM0 READ << 6c
/dev/ttyACM0 READ << 74
/dev/ttyACM0 READ << 92cb
/dev/ttyACM0 READ << 40
/dev/ttyACM0 READ << 0000
/dev/ttyACM0 READ << 00
/dev/ttyACM0 READ << 0000
/dev/ttyACM0 READ << 00
/dev/ttyACM0 READ << 00cb
/dev/ttyACM0 READ << 40
/dev/ttyACM0 READ << 08
/dev/ttyACM0 READ << 0000
/dev/ttyACM0 READ << 00
/dev/ttyACM0 READ << 0000
/dev/ttyACM0 READ << 00
/dev/ttyACM0 WRITE >> 94
/dev/ttyACM0 WRITE >> 01
/dev/ttyACM0 WRITE >> 01
/dev/ttyACM0 WRITE >> c0
/dev/ttyACM0 WRITE >> cb4018000000000000
=== RUN TestBasicComm/RPCClientCallFloatArgsError
/dev/ttyACM0 READ << 9400
/dev/ttyACM0 READ << 02a46d
/dev/ttyACM0 READ << 75
/dev/ttyACM0 READ << 6c74
/dev/ttyACM0 READ << 91
/dev/ttyACM0 READ << cb40
/dev/ttyACM0 READ << 00
/dev/ttyACM0 READ << 00
/dev/ttyACM0 READ << 0000
/dev/ttyACM0 READ << 00
/dev/ttyACM0 READ << 0000
/dev/ttyACM0 WRITE >> 94
/dev/ttyACM0 WRITE >> 01
/dev/ttyACM0 WRITE >> 02
/dev/ttyACM0 WRITE >> 92
/dev/ttyACM0 WRITE >> 01
/dev/ttyACM0 WRITE >> b1
/dev/ttyACM0 WRITE >> 6d697373696e6720706172616d65746572
/dev/ttyACM0 WRITE >> c0
=== RUN TestBasicComm/RPCClientCallBoolArgs
/dev/ttyACM0 READ << 9400
/dev/ttyACM0 READ << 03
/dev/ttyACM0 READ << a26f
/dev/ttyACM0 READ << 72
/dev/ttyACM0 READ << 92c3
/dev/ttyACM0 READ << c2
/dev/ttyACM0 WRITE >> 94
/dev/ttyACM0 WRITE >> 01
/dev/ttyACM0 WRITE >> 03
/dev/ttyACM0 WRITE >> c0
/dev/ttyACM0 WRITE >> c3
/dev/ttyACM0 READ << 9400
/dev/ttyACM0 READ << 04
/dev/ttyACM0 READ << a26f
/dev/ttyACM0 READ << 72
/dev/ttyACM0 READ << 91
/dev/ttyACM0 READ << c2
/dev/ttyACM0 WRITE >> 94
/dev/ttyACM0 WRITE >> 01
/dev/ttyACM0 WRITE >> 04
/dev/ttyACM0 WRITE >> c0
/dev/ttyACM0 WRITE >> c2
/dev/ttyACM0 CLOSE
--- PASS: TestBasicComm (10.21s)
--- PASS: TestBasicComm/RPCClientCallFloatArgs (0.03s)
--- PASS: TestBasicComm/RPCClientCallFloatArgsError (0.03s)
--- PASS: TestBasicComm/RPCClientCallBoolArgs (0.01s)
PASS
ok RpcClientZeroTest 10.216s
RpcLite/test/RpcClientTest$
```
112 changes: 112 additions & 0 deletions extras/integration_test/RPCClient_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package testsuite

import (
"testing"
"time"

"github.com/stretchr/testify/require"
"github.com/vmihailenco/msgpack/v5"
"go.bug.st/serial"
)

func TestRPCClient(t *testing.T) {
// Get the upload port to upload the sketch
rpcPort, debugPort := UploadSketchAndGetRPCAndDebugPorts(t)

// Connect to the RPC serial port
_rpcSer, err := serial.Open(rpcPort, &serial.Mode{BaudRate: 115200})
rpcSer := &DebugStream{Upstream: _rpcSer, Portname: rpcPort}
require.NoError(t, err)
t.Cleanup(func() { rpcSer.Close() })
in := msgpack.NewDecoder(rpcSer)
out := msgpack.NewEncoder(rpcSer)
out.UseCompactInts(true)

// Connect to the Debug serial port
debugSer, err := serial.Open(debugPort, &serial.Mode{BaudRate: 115200})
require.NoError(t, err)
t.Cleanup(func() { debugSer.Close() })
expectDebug := func(s string) { Expect(t, debugSer, s) }

// Timeout fallback: close the connection after 10 seconds, if the test do not go through
go func() {
time.Sleep(10 * time.Second)
rpcSer.Close()
debugSer.Close()
}()

msgID := 0

// 1: Receive an RPC call to the "mult" method with 2 arguments
// and send back the result
t.Run("RPCClientCallFloatArgs", func(t *testing.T) {
arr, err := in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{int8(0), int8(msgID), "mult", []any{2.0, 3.0}}, arr)
err = out.Encode([]any{1, msgID, nil, 6.0})
require.NoError(t, err)
expectDebug("mult(2.0, 3.0)\r\n")
expectDebug("-> 6.00\r\n")
msgID++
})

// 2: Receive an RPC call to the "mult" method with 1 argument (wrong number of arguments)
// and send back an error with [int, string] format
t.Run("RPCClientCallFloatArgsError", func(t *testing.T) {
arr, err := in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{int8(0), int8(msgID), "mult", []any{2.0}}, arr)
err = out.Encode([]any{1, msgID, []any{1, "missing parameter"}, nil})
require.NoError(t, err)
expectDebug("mult(2.0)\r\n")
expectDebug("-> error\r\n")
msgID++
})

// 3, 4: Receive an RPC call to the "or" method with 1 or 2 arguments
// and send back the result
t.Run("RPCClientCallBoolArgs", func(t *testing.T) {
arr, err := in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{int8(0), int8(msgID), "or", []any{true, false}}, arr)
err = out.Encode([]any{1, msgID, nil, true})
require.NoError(t, err)
expectDebug("or(true, false)\r\n")
expectDebug("-> true\r\n")
msgID++

arr, err = in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{int8(0), int8(msgID), "or", []any{false}}, arr)
err = out.Encode([]any{1, msgID, nil, false})
require.NoError(t, err)
expectDebug("or(false)\r\n")
expectDebug("-> false\r\n")
msgID++
})

// 5: Receive an RPC call to the "mult" method with 1 argument (wrong number of arguments)
// and send back an error with [int, string] format with a long string
t.Run("RPCClientCallFloatArgsErrorWithLongString", func(t *testing.T) {
arr, err := in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{int8(0), int8(msgID), "mult", []any{2.0}}, arr)
err = out.Encode([]any{1, msgID, []any{2, "method get_led_state not available"}, nil})
require.NoError(t, err)
expectDebug("mult(2.0)\r\n")
expectDebug("-> error\r\n")
msgID++
})

// RPC: Receive an RPC call to the "mult" method with 1 argument (wrong number of arguments)
// and send back a custom error without [int, string] format
// t.Run("RPCClientCallFloatArgsErrorCustom", func(t *testing.T) {
// arr, err := in.DecodeSlice()
// require.NoError(t, err)
// require.Equal(t, []any{int8(0), int8(3), "mult", []any{2.0}}, arr)
// err = out.Encode([]any{1, 3, "missing parameter", nil})
// require.NoError(t, err)
// expectDebug("mult(2.0)\r\n")
// expectDebug("-> error\r\n")
// })
}
93 changes: 93 additions & 0 deletions extras/integration_test/RPCServer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package testsuite

import (
"testing"
"time"

"github.com/stretchr/testify/require"
"github.com/vmihailenco/msgpack/v5"
"go.bug.st/serial"
)

func TestRPCServer(t *testing.T) {
// Get the upload port to upload the sketch
rpcPort, debugPort := UploadSketchAndGetRPCAndDebugPorts(t)

// Connect to the RPC serial port
_rpcSer, err := serial.Open(rpcPort, &serial.Mode{BaudRate: 115200})
rpcSer := &DebugStream{Upstream: _rpcSer, Portname: rpcPort}
require.NoError(t, err)
t.Cleanup(func() { rpcSer.Close() })
in := msgpack.NewDecoder(rpcSer)
out := msgpack.NewEncoder(rpcSer)
out.UseCompactInts(true)

// Connect to the Debug serial port
debugSer, err := serial.Open(debugPort, &serial.Mode{BaudRate: 115200})
require.NoError(t, err)
t.Cleanup(func() { debugSer.Close() })
expectDebug := func(s string) { Expect(t, debugSer, s) }

// Timeout fallback: close the connection after 10 seconds, if the test do not go through
go func() {
time.Sleep(10 * time.Second)
rpcSer.Close()
debugSer.Close()
}()

// 1: Send an RPC call to the "add" method with 2 arguments
// and get back the result
t.Run("RPCServerCallIntArgs", func(t *testing.T) {
err = out.Encode([]any{0, 1, "add", []any{2, 3}})
require.NoError(t, err)
expectDebug("add(2, 3)\r\n")
arr, err := in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{int8(1), int8(1), nil, int8(5)}, arr)
})

// 2, 3: Send the same RPC call with 1 and 3 arguments, and get back the error
t.Run("RPCServerCallWrongIntArgsCount", func(t *testing.T) {
err = out.Encode([]any{0, 2, "add", []any{2}})
require.NoError(t, err)
arr, err := in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{
int8(1),
int8(2),
[]any{uint8(253), "Missing call parameters (WARNING: Default param resolution is not implemented)"},
nil,
}, arr)

err = out.Encode([]any{0, 3, "add", []any{2, 3, 4}})
require.NoError(t, err)
arr, err = in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{
int8(1),
int8(3),
[]any{uint8(253), "Too many parameters"},
nil,
}, arr)
})

// 4: Send an RPC call to the "greet" method
t.Run("RPCServerCallNoArgsReturnString", func(t *testing.T) {
err = out.Encode([]any{0, 4, "greet", []any{}})
require.NoError(t, err)
expectDebug("greet()\r\n")
arr, err := in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{int8(1), int8(4), nil, "Hello World!"}, arr)
})

// 5: Send an RPC call to the "loopback" method with 1 string argument
t.Run("RPCServerCallStringArgsReturnString", func(t *testing.T) {
err = out.Encode([]any{0, 5, "loopback", []any{"Hello World!"}})
require.NoError(t, err)
expectDebug("loopback(\"Hello World!\")\r\n")
arr, err := in.DecodeSlice()
require.NoError(t, err)
require.Equal(t, []any{int8(1), int8(5), nil, "Hello World!"}, arr)
})
}
Loading