github.com/jordan-bonecutter/can-go@v0.0.0-20230901155856-d83995b18e50/pkg/socketcan/emulator_test.go (about)

     1  package socketcan
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"testing"
     8  	"time"
     9  
    10  	"go.einride.tech/can"
    11  	"golang.org/x/sync/errgroup"
    12  	"gotest.tools/v3/assert"
    13  	is "gotest.tools/v3/assert/cmp"
    14  )
    15  
    16  func TestEmulate_Close(t *testing.T) {
    17  	// Given: an emulator
    18  	e, err := NewEmulator()
    19  	assert.NilError(t, err)
    20  	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
    21  	defer cancel()
    22  	// When: I start the emulator
    23  	g, ctx := errgroup.WithContext(ctx)
    24  	g.Go(func() error {
    25  		return e.Run(ctx)
    26  	})
    27  	// Then: I should be able to close it
    28  	assert.NilError(t, g.Wait())
    29  }
    30  
    31  func TestEmulate_SendToAll(t *testing.T) {
    32  	for _, tt := range []struct {
    33  		receivers int
    34  	}{
    35  		{receivers: 1},
    36  		{receivers: 5},
    37  		{receivers: 100},
    38  	} {
    39  		tt := tt
    40  		t.Run(fmt.Sprintf("receivers:%v", tt.receivers), func(t *testing.T) {
    41  			// Given: A listener with an Emulator
    42  			ctx, cancel := context.WithCancel(context.Background())
    43  			eg, eCtx := errgroup.WithContext(ctx)
    44  			e, err := NewEmulator()
    45  			assert.NilError(t, err)
    46  			eg.Go(func() error {
    47  				return e.Run(eCtx)
    48  			})
    49  			// When: I start multiple receivers connected to the Emulator
    50  			g := errgroup.Group{}
    51  			for i := 0; i < tt.receivers; i++ {
    52  				r, err := e.Receiver()
    53  				assert.NilError(t, err)
    54  				g.Go(func() error {
    55  					if ok := r.Receive(); !ok {
    56  						return fmt.Errorf("failed to receive CAN frame: %w", r.Err())
    57  					}
    58  					if r.HasErrorFrame() {
    59  						return fmt.Errorf("received error frame: %v", r.ErrorFrame())
    60  					}
    61  					return r.Err()
    62  				})
    63  			}
    64  			// And then the emulator transmits a CAN frame
    65  			txFrame := can.Frame{ID: 42, Length: 4, Data: can.Data{1, 2, 3, 4}}
    66  			err = e.TransmitFrame(context.Background(), txFrame)
    67  			assert.NilError(t, err)
    68  			// Then: Every receiver should receive the frame and not return an error
    69  			assert.NilError(t, g.Wait())
    70  			cancel()
    71  			assert.NilError(t, eg.Wait())
    72  		})
    73  	}
    74  }
    75  
    76  func TestEmulate_ConnectMany(t *testing.T) {
    77  	// TODO: Fix raciness or remove this test.
    78  	t.Skip("racy")
    79  	for _, tt := range []struct {
    80  		noTransmitters int
    81  		canFrames      []can.Frame
    82  	}{
    83  		{
    84  			noTransmitters: 1,
    85  			canFrames: []can.Frame{
    86  				{ID: 42},
    87  				{ID: 43, Length: 4, Data: can.Data{1, 2, 3, 4}},
    88  			},
    89  		},
    90  		{
    91  			noTransmitters: 10,
    92  			canFrames: []can.Frame{
    93  				{ID: 42},
    94  				{ID: 43, Length: 4, Data: can.Data{1, 2, 3, 4}},
    95  				{ID: 44, IsRemote: true},
    96  			},
    97  		},
    98  		{
    99  			noTransmitters: 50,
   100  			canFrames: []can.Frame{
   101  				{ID: 42},
   102  				{ID: 43, Length: 4, Data: can.Data{1, 2, 3, 4}},
   103  				{ID: 44, IsRemote: true},
   104  				{ID: 45, Length: 7, Data: can.Data{1, 2, 3, 4, 5, 6, 7}},
   105  				{ID: 46, IsExtended: false},
   106  				{ID: 47, Length: 1, Data: can.Data{1}},
   107  				{ID: 48, IsRemote: false},
   108  			},
   109  		},
   110  	} {
   111  		tt := tt
   112  		name := fmt.Sprintf("transmitters:%v,frames:%v", tt.noTransmitters, len(tt.canFrames))
   113  		t.Run(name, func(t *testing.T) {
   114  			// Given: A listener with an Emulator
   115  			e, err := NewEmulator(NoLogger)
   116  			assert.NilError(t, err)
   117  			ctx, cancel := context.WithCancel(context.Background())
   118  			eg, eCtx := errgroup.WithContext(ctx)
   119  			eg.Go(func() error {
   120  				return e.Run(eCtx)
   121  			})
   122  			r, err := e.Receiver()
   123  			assert.NilError(t, err)
   124  			receiver := errgroup.Group{}
   125  			receiver.Go(func() error {
   126  				for i := 0; i < len(tt.canFrames)*tt.noTransmitters; i++ {
   127  					i := i
   128  					if ok := r.Receive(); !ok {
   129  						cancel()
   130  						assert.NilError(t, eg.Wait())
   131  						t.Fatal("Not all CAN frames were received", i, r.Err())
   132  					}
   133  					assert.Assert(t, is.Contains(tt.canFrames, r.Frame()))
   134  				}
   135  				return nil
   136  			})
   137  			// When: I connect multiple transmitters and transmit CAN frame on every transmitter
   138  			transmits, txCtx := errgroup.WithContext(ctx)
   139  			for i := 0; i < tt.noTransmitters; i++ {
   140  				transmits.Go(func() error {
   141  					conn, err := DialContext(txCtx, e.Addr().Network(), e.Addr().String())
   142  					if err != nil {
   143  						return err
   144  					}
   145  					tx := NewTransmitter(conn)
   146  					for _, frame := range tt.canFrames {
   147  						if err := tx.TransmitFrame(txCtx, frame); err != nil {
   148  							log.Printf("failed to transmit frame: %+v\n", err)
   149  							return err
   150  						}
   151  					}
   152  					return conn.Close()
   153  				})
   154  			}
   155  			assert.NilError(t, transmits.Wait())
   156  			// Then: Every CAN frame should have been delivered to the emulator
   157  			assert.NilError(t, receiver.Wait())
   158  			cancel()
   159  			assert.NilError(t, eg.Wait())
   160  		})
   161  	}
   162  }
   163  
   164  func TestEmulate_SendReceive(t *testing.T) {
   165  	for _, tt := range []struct {
   166  		transmitters int
   167  		receivers    int
   168  	}{
   169  		{
   170  			transmitters: 1,
   171  			receivers:    2,
   172  		},
   173  		{
   174  			transmitters: 10,
   175  			receivers:    50,
   176  		},
   177  		{
   178  			transmitters: 50,
   179  			receivers:    50,
   180  		},
   181  	} {
   182  		tt := tt
   183  		name := fmt.Sprintf("transmitters: %v,receivers: %v", tt.transmitters, tt.receivers)
   184  		t.Run(name, func(t *testing.T) {
   185  			// Given: A listener and an emulator
   186  			e, err := NewEmulator()
   187  			assert.NilError(t, err)
   188  			ctx, cancel := context.WithCancel(context.Background())
   189  			eg, eCtx := errgroup.WithContext(ctx)
   190  			eg.Go(func() error {
   191  				return e.Run(eCtx)
   192  			})
   193  			canFrames := make(map[uint32]can.Frame)
   194  			canFrames[42] = can.Frame{ID: 42}
   195  			canFrames[43] = can.Frame{ID: 43, IsRemote: true}
   196  			canFrames[44] = can.Frame{ID: 44, IsExtended: true}
   197  			// When: I start a number of receivers
   198  			rx := errgroup.Group{}
   199  			for i := 0; i < tt.receivers; i++ {
   200  				r, err := e.Receiver()
   201  				assert.NilError(t, err)
   202  				rx.Go(func() error {
   203  					for i := 0; i < tt.transmitters*len(canFrames); i++ {
   204  						if ok := r.Receive(); !ok {
   205  							return fmt.Errorf("receive frames: %w", r.Err())
   206  						}
   207  						if r.HasErrorFrame() {
   208  							return fmt.Errorf("received error frame: %v", r.ErrorFrame())
   209  						}
   210  						if _, ok := canFrames[r.Frame().ID]; !ok {
   211  							return fmt.Errorf("received unexpected frame: %v", r.Frame())
   212  						}
   213  					}
   214  					return nil
   215  				})
   216  			}
   217  			// And then start a number of transmitters that will transmit a number of CAN frames
   218  			tx, txCtx := errgroup.WithContext(ctx)
   219  			for i := 0; i < tt.transmitters; i++ {
   220  				conn, err := DialContext(txCtx, e.Addr().Network(), e.Addr().String())
   221  				assert.NilError(t, err)
   222  				tx.Go(func() (err error) {
   223  					t := NewTransmitter(conn)
   224  					for _, f := range canFrames {
   225  						if err := t.TransmitFrame(txCtx, f); err != nil {
   226  							return fmt.Errorf("transmit frame: %w", err)
   227  						}
   228  					}
   229  					if err := conn.Close(); err != nil {
   230  						return fmt.Errorf("close transmitter: %w", err)
   231  					}
   232  					return nil
   233  				})
   234  			}
   235  			// Then: The transmissions should not fail
   236  			assert.NilError(t, tx.Wait())
   237  			// And every receiver should receive every CAN frame
   238  			assert.NilError(t, rx.Wait())
   239  			cancel()
   240  			assert.NilError(t, eg.Wait())
   241  		})
   242  	}
   243  }
   244  
   245  func TestEmulator_Isolation(t *testing.T) {
   246  	// Given 5 separate emulators
   247  	const nEmulators = 5
   248  	emulators := make([]*Emulator, nEmulators)
   249  	ctx, cancel := context.WithCancel(context.Background())
   250  	eg, eCtx := errgroup.WithContext(ctx)
   251  	for i := 0; i < nEmulators; i++ {
   252  		e, err := NewEmulator()
   253  		assert.NilError(t, err)
   254  		emulators[i] = e
   255  		eg.Go(func() error {
   256  			return e.Run(eCtx)
   257  		})
   258  	}
   259  	// When starting one transmitter/receiver pair per emulator sending 10 frames
   260  	const nFrames = 10
   261  	rx := errgroup.Group{}
   262  	tx := errgroup.Group{}
   263  	for i := 0; i < nEmulators; i++ {
   264  		i := i
   265  		r, err := emulators[i].Receiver()
   266  		assert.NilError(t, err)
   267  		rx.Go(func() error {
   268  			for j := 0; j < nFrames; j++ {
   269  				if ok := r.Receive(); !ok {
   270  					return fmt.Errorf("receive frame: %w", r.Err())
   271  				}
   272  				if r.HasErrorFrame() {
   273  					return fmt.Errorf("received error frame: %v", r.ErrorFrame())
   274  				}
   275  				if r.Frame().ID != uint32(i) {
   276  					return fmt.Errorf("receiver(%v) received unexpected frame: %v", i, r.Frame())
   277  				}
   278  			}
   279  			return nil
   280  		})
   281  		for j := 0; j < nFrames; j++ {
   282  			frame := can.Frame{ID: uint32(i)}
   283  			tx.Go(func() error {
   284  				return emulators[i].TransmitFrame(context.Background(), frame)
   285  			})
   286  		}
   287  	}
   288  	// Then all transmitted frames should be received by correct receiver
   289  	assert.NilError(t, rx.Wait())
   290  	assert.NilError(t, tx.Wait())
   291  	cancel()
   292  	assert.NilError(t, eg.Wait())
   293  }
   294  
   295  func TestEmulator_WaitForSenders(t *testing.T) {
   296  	// Given a started emulator
   297  	ctx, cancel := context.WithCancel(context.Background())
   298  	eg, eCtx := errgroup.WithContext(ctx)
   299  	e, err := NewEmulator()
   300  	assert.NilError(t, err)
   301  	eg.Go(func() error {
   302  		return e.Run(eCtx)
   303  	})
   304  	// When one transmitter is transmitting a frame
   305  	txg := errgroup.Group{}
   306  	txg.Go(func() error {
   307  		return e.TransmitFrame(context.Background(), can.Frame{ID: 1234})
   308  	})
   309  	// Then WaitForSenders should return without an error
   310  	err = e.WaitForSenders(1, time.Second)
   311  	assert.NilError(t, err)
   312  	assert.NilError(t, txg.Wait())
   313  	cancel()
   314  	assert.NilError(t, eg.Wait())
   315  }
   316  
   317  func TestEmulator_WaitForSenders_Multiple(t *testing.T) {
   318  	// Given a started emulator
   319  	ctx, cancel := context.WithCancel(context.Background())
   320  	eg, eCtx := errgroup.WithContext(ctx)
   321  	e, err := NewEmulator()
   322  	assert.NilError(t, err)
   323  	eg.Go(func() error {
   324  		return e.Run(eCtx)
   325  	})
   326  	// When one transmitter is transmitting a frame
   327  	txg := errgroup.Group{}
   328  	txg.Go(func() error {
   329  		return e.TransmitFrame(context.Background(), can.Frame{ID: 1234})
   330  	})
   331  	txg.Go(func() error {
   332  		return e.TransmitFrame(context.Background(), can.Frame{ID: 4321})
   333  	})
   334  	// Then WaitForSenders should return without an error
   335  	err = e.WaitForSenders(2, time.Second)
   336  	assert.NilError(t, err)
   337  	assert.NilError(t, txg.Wait())
   338  	cancel()
   339  	assert.NilError(t, eg.Wait())
   340  }
   341  
   342  func TestEmulator_WaitForSenders_Timeout(t *testing.T) {
   343  	// Given a started emulator
   344  	ctx, cancel := context.WithCancel(context.Background())
   345  	eg, eCtx := errgroup.WithContext(ctx)
   346  	e, err := NewEmulator()
   347  	assert.NilError(t, err)
   348  	eg.Go(func() error {
   349  		return e.Run(eCtx)
   350  	})
   351  	// When no transmitters have connected and transmitted frames
   352  	// Then WaitForSenders should timeout
   353  	err = e.WaitForSenders(1, 100*time.Millisecond)
   354  	assert.ErrorContains(t, err, "timeout")
   355  	cancel()
   356  	assert.NilError(t, eg.Wait())
   357  }