github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/store/testing_store.go (about) 1 package store 2 3 import ( 4 "fmt" 5 "reflect" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/tilt-dev/tilt/pkg/model" 11 ) 12 13 var _ RStore = &TestingStore{} 14 15 type TestingStore struct { 16 state *EngineState 17 stateMu sync.RWMutex 18 19 actions []Action 20 actionsMu sync.RWMutex 21 } 22 23 func NewTestingStore() *TestingStore { 24 return &TestingStore{ 25 state: NewState(), 26 } 27 } 28 29 func (s *TestingStore) LockMutableStateForTesting() *EngineState { 30 s.stateMu.Lock() 31 return s.state 32 } 33 34 func (s *TestingStore) UnlockMutableState() { 35 s.stateMu.Unlock() 36 } 37 38 func (s *TestingStore) SetState(state EngineState) { 39 s.stateMu.Lock() 40 defer s.stateMu.Unlock() 41 s.state = &state 42 } 43 44 func (s *TestingStore) WithState(f func(state *EngineState)) { 45 s.stateMu.Lock() 46 defer s.stateMu.Unlock() 47 f(s.state) 48 } 49 50 func (s *TestingStore) WithManifestState(name model.ManifestName, f func(ms *ManifestState)) { 51 s.WithState(func(state *EngineState) { 52 mt, ok := state.ManifestTargets[name] 53 if !ok { 54 panic(fmt.Sprintf("no manifest target for %s", name)) 55 } 56 f(mt.State) 57 }) 58 } 59 60 func (s *TestingStore) StateMutex() *sync.RWMutex { 61 return &s.stateMu 62 } 63 64 func (s *TestingStore) RLockState() EngineState { 65 s.stateMu.RLock() 66 return *(s.state) 67 } 68 69 func (s *TestingStore) RUnlockState() { 70 s.stateMu.RUnlock() 71 } 72 73 func (s *TestingStore) Dispatch(action Action) { 74 s.actionsMu.Lock() 75 defer s.actionsMu.Unlock() 76 77 s.actions = append(s.actions, action) 78 } 79 80 func (s *TestingStore) ClearActions() { 81 s.actionsMu.Lock() 82 defer s.actionsMu.Unlock() 83 84 s.actions = nil 85 } 86 87 func (s *TestingStore) Actions() []Action { 88 s.actionsMu.RLock() 89 defer s.actionsMu.RUnlock() 90 91 return append([]Action{}, s.actions...) 92 } 93 94 func (s *TestingStore) AssertNoErrorActions(t testing.TB) bool { 95 t.Helper() 96 s.actionsMu.RLock() 97 defer s.actionsMu.RUnlock() 98 for _, action := range s.actions { 99 errAction, ok := action.(ErrorAction) 100 if ok { 101 t.Errorf("Error action: %s", errAction) 102 return false 103 } 104 } 105 return true 106 } 107 108 func (s *TestingStore) WaitForAction(t testing.TB, typ reflect.Type) Action { 109 return WaitForAction(t, typ, s.Actions) 110 } 111 112 // for use by tests (with a real channel-based store, NOT a TestingStore), to wait until 113 // an action of the specified type comes out of the given chan at some point we might want 114 // it to return the index it was found at, and then take an index, so that we can start 115 // searching from the next index 116 func WaitForAction(t testing.TB, typ reflect.Type, getActions func() []Action) Action { 117 start := time.Now() 118 timeout := 500 * time.Millisecond 119 120 current := 0 121 for time.Since(start) < timeout { 122 actions := getActions() 123 for ; current < len(actions); current++ { 124 a := actions[current] 125 if reflect.TypeOf(a) == typ { 126 return a 127 } else if la, ok := a.(LogAction); ok { 128 fmt.Println(string(la.Message())) 129 } 130 } 131 } 132 t.Fatalf("timed out waiting for action of type %s. Saw the following actions while waiting: %+v", 133 typ.Name(), 134 getActions()) 135 return nil 136 } 137 138 // for use by tests (with a real channel-based store, NOT a TestingStore). Assert that 139 // we don't see an action of the given type 140 func AssertNoActionOfType(t testing.TB, typ reflect.Type, getActions func() []Action) Action { 141 start := time.Now() 142 timeout := 300 * time.Millisecond 143 144 current := 0 145 for time.Since(start) < timeout { 146 actions := getActions() 147 for ; current < len(actions); current++ { 148 a := actions[current] 149 if reflect.TypeOf(a) == typ { 150 t.Fatalf("Found action of type %s where none was expected: %+v", typ.Name(), a) 151 } else if la, ok := a.(LogAction); ok { 152 fmt.Println(string(la.Message())) 153 } 154 } 155 } 156 return nil 157 }