github.com/blueinnovationsgroup/can-go@v0.0.0-20230518195432-d0567cda0028/internal/generate/example_test.go (about)

     1  package generate
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"reflect"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/blueinnovationsgroup/can-go"
    12  	"github.com/blueinnovationsgroup/can-go/pkg/generated"
    13  	"github.com/blueinnovationsgroup/can-go/pkg/socketcan"
    14  	examplecan "github.com/blueinnovationsgroup/can-go/testdata/gen/go/example"
    15  	"golang.org/x/sync/errgroup"
    16  	"gotest.tools/v3/assert"
    17  )
    18  
    19  func TestExampleDatabase_MarshalUnmarshal(t *testing.T) {
    20  	for _, tt := range []struct {
    21  		name string
    22  		m    can.Message
    23  		f    can.Frame
    24  	}{
    25  		{
    26  			name: "IODebug",
    27  			m: examplecan.NewIODebug().
    28  				SetTestUnsigned(5).
    29  				SetTestEnum(examplecan.IODebug_TestEnum_Two).
    30  				SetTestSigned(-42).
    31  				SetTestFloat(61.5).
    32  				SetTestBoolEnum(examplecan.IODebug_TestBoolEnum_One).
    33  				SetRawTestScaledEnum(examplecan.IODebug_TestScaledEnum_Four),
    34  			f: can.Frame{
    35  				ID:     500,
    36  				Length: 6,
    37  				Data:   can.Data{5, 2, 214, 123, 1, 2},
    38  			},
    39  		},
    40  
    41  		{
    42  			name: "MotorStatus1",
    43  			m: examplecan.NewMotorStatus().
    44  				SetSpeedKph(0.423).
    45  				SetWheelError(true),
    46  			f: can.Frame{
    47  				ID:     400,
    48  				Length: 3,
    49  				Data:   can.Data{0x1, 0xa7, 0x1},
    50  			},
    51  		},
    52  
    53  		{
    54  			name: "MotorStatus2",
    55  			m: examplecan.NewMotorStatus().
    56  				SetSpeedKph(12),
    57  			f: can.Frame{
    58  				ID:     400,
    59  				Length: 3,
    60  				Data:   can.Data{0x00, 0xe0, 0x2e},
    61  			},
    62  		},
    63  	} {
    64  		tt := tt
    65  		t.Run(tt.name, func(t *testing.T) {
    66  			f, err := tt.m.MarshalFrame()
    67  			assert.NilError(t, err)
    68  			assert.Equal(t, tt.f, f)
    69  			// allocate new message of same type as tt.m
    70  			msg := reflect.New(reflect.ValueOf(tt.m).Elem().Type()).Interface().(generated.Message)
    71  			assert.NilError(t, msg.UnmarshalFrame(f))
    72  			assert.Assert(t, reflect.DeepEqual(tt.m, msg))
    73  		})
    74  	}
    75  }
    76  
    77  func TestExampleDatabase_UnmarshalFrame_Error(t *testing.T) {
    78  	for _, tt := range []struct {
    79  		name string
    80  		f    can.Frame
    81  		m    generated.Message
    82  		err  string
    83  	}{
    84  		{
    85  			name: "wrong ID",
    86  			f:    can.Frame{ID: 11, Length: 8},
    87  			m:    examplecan.NewSensorSonars(),
    88  			err:  "unmarshal SensorSonars: expects ID 200 (got 00B#0000000000000000 with ID 11)",
    89  		},
    90  		{
    91  			name: "wrong length",
    92  			f:    can.Frame{ID: 200, Length: 4},
    93  			m:    examplecan.NewSensorSonars(),
    94  			err:  "unmarshal SensorSonars: expects length 8 (got 0C8#00000000 with length 4)",
    95  		},
    96  		{
    97  			name: "remote frame",
    98  			f:    can.Frame{ID: 200, Length: 8, IsRemote: true},
    99  			m:    examplecan.NewSensorSonars(),
   100  			err:  "unmarshal SensorSonars: expects non-remote frame (got remote frame 0C8#R8)",
   101  		},
   102  		{
   103  			name: "extended ID",
   104  			f:    can.Frame{ID: 200, Length: 8, IsExtended: true},
   105  			m:    examplecan.NewSensorSonars(),
   106  			err:  "unmarshal SensorSonars: expects standard ID (got 000000C8#0000000000000000 with extended ID)",
   107  		},
   108  	} {
   109  		tt := tt
   110  		t.Run(tt.name, func(t *testing.T) {
   111  			assert.Equal(t, tt.err, tt.m.UnmarshalFrame(tt.f).Error())
   112  		})
   113  	}
   114  }
   115  
   116  func TestExampleDatabase_TestEnum_String(t *testing.T) {
   117  	assert.Equal(t, "One", examplecan.IODebug_TestEnum_One.String())
   118  	assert.Equal(t, "Two", examplecan.IODebug_TestEnum_Two.String())
   119  	assert.Equal(t, "IODebug_TestEnum(3)", examplecan.IODebug_TestEnum(3).String())
   120  }
   121  
   122  func TestExampleDatabase_Message_String(t *testing.T) {
   123  	const expected = "{WheelError: true, SpeedKph: 42km/h}"
   124  	msg := examplecan.NewMotorStatus().
   125  		SetSpeedKph(42).
   126  		SetWheelError(true)
   127  	assert.Equal(t, expected, msg.String())
   128  	assert.Equal(t, expected, fmt.Sprintf("%v", msg))
   129  }
   130  
   131  func TestExampleDatabase_OutOfBoundsValue(t *testing.T) {
   132  	const expected = examplecan.IODebug_TestEnum(63)
   133  	actual := examplecan.NewIODebug().SetTestEnum(255).TestEnum()
   134  	assert.Equal(t, expected, actual)
   135  }
   136  
   137  func TestExampleDatabase_MultiplexedSignals(t *testing.T) {
   138  	// Given a message with multiplexed signals
   139  	msg := examplecan.NewSensorSonars().
   140  		SetErrCount(1).
   141  		SetMux(1).
   142  		SetLeft(20).
   143  		SetMiddle(30).
   144  		SetRight(40).
   145  		SetRear(50).
   146  		SetNoFiltLeft(60).
   147  		SetNoFiltMiddle(70).
   148  		SetNoFiltRight(80).
   149  		SetNoFiltRear(90)
   150  	for _, tt := range []struct {
   151  		expectedMux          uint8
   152  		expectedErrCount     uint16
   153  		expectedLeft         float64
   154  		expectedMiddle       float64
   155  		expectedRight        float64
   156  		expectedRear         float64
   157  		expectedNoFiltLeft   float64
   158  		expectedNoFiltMiddle float64
   159  		expectedNoFiltRight  float64
   160  		expectedNoFiltRear   float64
   161  	}{
   162  		{
   163  			expectedMux:          0,
   164  			expectedErrCount:     1,
   165  			expectedLeft:         20,
   166  			expectedMiddle:       30,
   167  			expectedRight:        40,
   168  			expectedRear:         50,
   169  			expectedNoFiltLeft:   0,
   170  			expectedNoFiltMiddle: 0,
   171  			expectedNoFiltRight:  0,
   172  			expectedNoFiltRear:   0,
   173  		},
   174  		{
   175  			expectedMux:          1,
   176  			expectedErrCount:     1,
   177  			expectedLeft:         0,
   178  			expectedMiddle:       0,
   179  			expectedRight:        0,
   180  			expectedRear:         0,
   181  			expectedNoFiltLeft:   60,
   182  			expectedNoFiltMiddle: 70,
   183  			expectedNoFiltRight:  80,
   184  			expectedNoFiltRear:   90,
   185  		},
   186  	} {
   187  		tt := tt
   188  		t.Run(fmt.Sprintf("mux=%v", tt.expectedMux), func(t *testing.T) {
   189  			unmarshal1 := examplecan.NewSensorSonars()
   190  			// When the multiplexer signal is 0 and we marshal the message
   191  			// to a CAN frame
   192  			msg.SetMux(tt.expectedMux)
   193  			f1, err := msg.MarshalFrame()
   194  			assert.NilError(t, err)
   195  			// When we unmarshal the CAN frame back to a message
   196  			assert.NilError(t, unmarshal1.UnmarshalFrame(f1))
   197  			// Then only the multiplexed signals with multiplexer value 0
   198  			// should be unmarshaled
   199  			assert.Equal(t, tt.expectedMux, unmarshal1.Mux(), "Mux")
   200  			assert.Equal(t, tt.expectedErrCount, unmarshal1.ErrCount(), "ErrCount")
   201  			assert.Equal(t, tt.expectedLeft, unmarshal1.Left(), "Left")
   202  			assert.Equal(t, tt.expectedMiddle, unmarshal1.Middle(), "Middle")
   203  			assert.Equal(t, tt.expectedRight, unmarshal1.Right(), "Right")
   204  			assert.Equal(t, tt.expectedRear, unmarshal1.Rear(), "Rear")
   205  			assert.Equal(t, tt.expectedNoFiltLeft, unmarshal1.NoFiltLeft(), "NoFiltLeft")
   206  			assert.Equal(t, tt.expectedNoFiltMiddle, unmarshal1.NoFiltMiddle(), "NoFiltMiddle")
   207  			assert.Equal(t, tt.expectedNoFiltRight, unmarshal1.NoFiltRight(), "NoFiltRight")
   208  			assert.Equal(t, tt.expectedNoFiltRear, unmarshal1.NoFiltRear(), "NoFiltRear")
   209  		})
   210  	}
   211  }
   212  
   213  func TestExampleDatabase_CopyFrom(t *testing.T) {
   214  	// Given: an original message
   215  	from := examplecan.NewIODebug().
   216  		SetRawTestScaledEnum(examplecan.IODebug_TestScaledEnum_Four).
   217  		SetTestBoolEnum(true).
   218  		SetTestFloat(0.1).
   219  		SetTestSigned(-10).
   220  		SetTestUnsigned(10)
   221  	// When: another message copies from the original message
   222  	to := examplecan.NewIODebug().CopyFrom(from)
   223  	// Then:
   224  	// all fields should be equal...
   225  	assert.Equal(t, from.String(), to.String())
   226  	assert.Equal(t, from.TestScaledEnum(), to.TestScaledEnum())
   227  	assert.Equal(t, from.TestBoolEnum(), to.TestBoolEnum())
   228  	assert.Equal(t, from.TestFloat(), to.TestFloat())
   229  	assert.Equal(t, from.TestSigned(), to.TestSigned())
   230  	assert.Equal(t, from.TestUnsigned(), to.TestUnsigned())
   231  	// ...and changes to the original should not affect the new message
   232  	from.SetTestUnsigned(100)
   233  	assert.Equal(t, uint8(10), to.TestUnsigned())
   234  }
   235  
   236  func TestExample_Nodes(t *testing.T) {
   237  	const testTimeout = 2 * time.Second
   238  	requireVCAN0(t)
   239  	// given a DRIVER node and a MOTOR node
   240  	motor := examplecan.NewMOTOR("can", "vcan0")
   241  	driver := examplecan.NewDRIVER("can", "vcan0")
   242  	// when starting them
   243  	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
   244  	g, ctx := errgroup.WithContext(ctx)
   245  	g.Go(func() error {
   246  		return motor.Run(ctx)
   247  	})
   248  	g.Go(func() error {
   249  		return driver.Run(ctx)
   250  	})
   251  	// and the MOTOR node is configured to send a speed report
   252  	const expectedSpeedKph = 42
   253  	motor.Lock()
   254  	motor.Tx().MotorStatus().SetSpeedKph(expectedSpeedKph)
   255  	motor.Tx().MotorStatus().SetCyclicTransmissionEnabled(true)
   256  	motor.Unlock()
   257  	// and the DRIVER node is configured to send a steering command
   258  	const expectedSteer = -4
   259  	driver.Lock()
   260  	driver.Tx().MotorCommand().SetSteer(expectedSteer)
   261  	driver.Tx().MotorCommand().SetCyclicTransmissionEnabled(true)
   262  	driver.Unlock()
   263  	// and the MOTOR node is listening for the steering command
   264  	expectedSteerReceivedChan := make(chan struct{})
   265  	motor.Lock()
   266  	motor.Rx().MotorCommand().SetAfterReceiveHook(func(context.Context) error {
   267  		motor.Lock()
   268  		if motor.Rx().MotorCommand().Steer() == expectedSteer {
   269  			close(expectedSteerReceivedChan)
   270  			motor.Rx().MotorCommand().SetAfterReceiveHook(func(context.Context) error { return nil })
   271  		}
   272  		motor.Unlock()
   273  		return nil
   274  	})
   275  	motor.Unlock()
   276  	// and the DRIVER node is listening for the speed report
   277  	expectedSpeedReceivedChan := make(chan struct{})
   278  	driver.Lock()
   279  	driver.Rx().MotorStatus().SetAfterReceiveHook(func(context.Context) error {
   280  		driver.Lock()
   281  		if driver.Rx().MotorStatus().SpeedKph() == expectedSpeedKph {
   282  			close(expectedSpeedReceivedChan)
   283  			driver.Rx().MotorStatus().SetAfterReceiveHook(func(context.Context) error { return nil })
   284  		}
   285  		driver.Unlock()
   286  		return nil
   287  	})
   288  	driver.Unlock()
   289  	// then the steer command transmitted by DRIVER should be received by MOTOR
   290  	select {
   291  	case <-expectedSteerReceivedChan:
   292  	case <-ctx.Done():
   293  		t.Fatalf("expected steer not received: %v", expectedSteer)
   294  	}
   295  	// and the speed report transmitted by MOTOR should be received by DRIVER
   296  	select {
   297  	case <-expectedSpeedReceivedChan:
   298  	case <-ctx.Done():
   299  		t.Fatalf("expected speed not received: %v", expectedSpeedKph)
   300  	}
   301  	cancel()
   302  	assert.NilError(t, g.Wait())
   303  }
   304  
   305  func TestExample_Node_NoEmptyMessages(t *testing.T) {
   306  	const testTimeout = 2 * time.Second
   307  	requireVCAN0(t)
   308  	// given a DRIVER node and a MOTOR node
   309  	motor := examplecan.NewMOTOR("can", "vcan0")
   310  	// when starting them
   311  	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
   312  	handler := func(ctx context.Context) error {
   313  		motor.Lock()
   314  		motor.Tx().MotorStatus().SetSpeedKph(100).SetWheelError(true)
   315  		motor.Unlock()
   316  		return nil
   317  	}
   318  	motor.Tx().MotorStatus().SetBeforeTransmitHook(handler)
   319  	motor.Tx().MotorStatus().SetCyclicTransmissionEnabled(true)
   320  	c, err := socketcan.Dial("can", "vcan0")
   321  	r := socketcan.NewReceiver(c)
   322  	assert.NilError(t, err)
   323  	g := errgroup.Group{}
   324  	g.Go(func() error {
   325  		return motor.Run(ctx)
   326  	})
   327  	assert.Assert(t, r.Receive())
   328  	assert.Equal(t, examplecan.NewMotorStatus().SetSpeedKph(100).SetWheelError(true).Frame(), r.Frame())
   329  	cancel()
   330  	assert.NilError(t, g.Wait())
   331  }
   332  
   333  func requireVCAN0(t *testing.T) {
   334  	t.Helper()
   335  	if _, err := net.InterfaceByName("vcan0"); err != nil {
   336  		t.Skip("interface vcan0 does not exist")
   337  	}
   338  }