github.com/grailbio/bigslice@v0.0.0-20230519005545-30c4c12152ad/exec/invocation_test.go (about) 1 // Copyright 2022 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache 2.0 3 // license that can be found in the LICENSE file. 4 5 package exec 6 7 import ( 8 "bytes" 9 "encoding/gob" 10 "testing" 11 12 "github.com/grailbio/bigslice" 13 "github.com/grailbio/testutil/assert" 14 "github.com/grailbio/testutil/expect" 15 ) 16 17 type testInterface interface{ IsTestInterface() } 18 type testInterfaceImpl struct { 19 Dummy int 20 } 21 22 func (s *testInterfaceImpl) IsTestInterface() {} 23 24 func init() { 25 // When we declare a Func argument to be an interface (testInterface), we 26 // still need to register the implementation, per normal gob usage 27 // expectation. 28 gob.Register(&testInterfaceImpl{}) 29 } 30 31 // unregistered is a gob-encodable type for testing invocation argument 32 // encoding/decoding. Note that it is not registered with gob.Register, so it 33 // would normally not be allowed to be encoded as an interface value. 34 type unregistered struct { 35 Dummy int 36 } 37 38 var fnTestInvocationEncoding = bigslice.Func( 39 // See TestInvocationGob for the cases exercised by each argument type. 40 func( 41 int, 42 unregistered, 43 *unregistered, 44 testInterfaceImpl, 45 testInterface, 46 interface{}, 47 *Result, 48 bigslice.Slice, 49 ) bigslice.Slice { 50 return bigslice.Const(1, []int{}) 51 }) 52 53 // TestInvocationGob verifies that we can round-trip through gob. We implement 54 // custom encoding/decoding so that we can serialize arbitary Func arguments as 55 // interface values without registering the types with gob, as we capture 56 // the argument types ourselves (for typechecking). 57 func TestInvocationGob(t *testing.T) { 58 inv := makeExecInvocation( 59 fnTestInvocationEncoding.Invocation( 60 "test", 61 []interface{}{ 62 2, // primitive 63 unregistered{Dummy: 3}, // struct not registered with gob 64 &unregistered{Dummy: 5}, // pointer to struct not registered with gob 65 testInterfaceImpl{Dummy: 7}, // struct registered with gob 66 &testInterfaceImpl{Dummy: 11}, // concrete as interface 67 &testInterfaceImpl{Dummy: 13}, // concrete as empty interface 68 &Result{}, // *Result as concrete 69 &Result{}, // *Result as interface (Slice) 70 }..., 71 ), 72 ) 73 // Simulate replacement of *Result arguments with invocation references, as 74 // we do when we send invocations to workers. 75 inv.Args[6] = invocationRef{Index: 17} 76 inv.Args[7] = invocationRef{Index: 19} 77 var ( 78 b bytes.Buffer 79 enc = gob.NewEncoder(&b) 80 dec = gob.NewDecoder(&b) 81 got execInvocation 82 ) 83 assert.NoError(t, enc.Encode(inv)) 84 assert.NoError(t, dec.Decode(&got)) 85 expect.EQ(t, got, inv) 86 }