go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/run/runtest/runtest.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package runtest 16 17 import ( 18 "context" 19 "fmt" 20 "sort" 21 "time" 22 23 "google.golang.org/protobuf/proto" 24 "google.golang.org/protobuf/types/known/timestamppb" 25 26 "go.chromium.org/luci/common/clock" 27 "go.chromium.org/luci/server/tq/tqtesting" 28 29 "go.chromium.org/luci/cv/internal/common" 30 "go.chromium.org/luci/cv/internal/common/eventbox" 31 "go.chromium.org/luci/cv/internal/cvtesting" 32 "go.chromium.org/luci/cv/internal/run" 33 "go.chromium.org/luci/cv/internal/run/eventpb" 34 35 . "github.com/smartystreets/goconvey/convey" 36 ) 37 38 // Runs returns list of runs from tasks for Run Manager. 39 func Runs(in tqtesting.TaskList) (runs common.RunIDs) { 40 for _, t := range in.SortByETA() { 41 switch v := t.Payload.(type) { 42 case *eventpb.ManageRunTask: 43 runs = append(runs, common.RunID(v.GetRunId())) 44 case *eventpb.KickManageRunTask: 45 runs = append(runs, common.RunID(v.GetRunId())) 46 } 47 } 48 return runs 49 } 50 51 // SortedRuns returns sorted list of runs from tasks for Run Manager. 52 func SortedRuns(in tqtesting.TaskList) common.RunIDs { 53 runs := Runs(in) 54 sort.Sort(runs) 55 return runs 56 } 57 58 // Tasks returns all Run tasks sorted by ETA. 59 func Tasks(in tqtesting.TaskList) tqtesting.TaskList { 60 ret := make(tqtesting.TaskList, 0, len(in)) 61 for _, t := range in.SortByETA() { 62 switch t.Payload.(type) { 63 case *eventpb.ManageRunTask, *eventpb.KickManageRunTask: 64 ret = append(ret, t) 65 } 66 } 67 return ret 68 } 69 70 func iterEventBox(ctx context.Context, runID common.RunID, cb func(*eventpb.Event)) { 71 events, err := eventbox.List(ctx, run.EventboxRecipient(ctx, runID)) 72 So(err, ShouldBeNil) 73 for _, item := range events { 74 evt := &eventpb.Event{} 75 So(proto.Unmarshal(item.Value, evt), ShouldBeNil) 76 cb(evt) 77 } 78 } 79 80 func matchEventBox(ctx context.Context, runID common.RunID, targets []*eventpb.Event) (matched, remaining []*eventpb.Event) { 81 remaining = make([]*eventpb.Event, len(targets)) 82 copy(remaining, targets) 83 iterEventBox(ctx, runID, func(evt *eventpb.Event) { 84 for i, r := range remaining { 85 if proto.Equal(evt, r) { 86 matched = append(matched, r) 87 remaining[i] = remaining[len(remaining)-1] 88 remaining[len(remaining)-1] = nil 89 remaining = remaining[:len(remaining)-1] 90 return 91 } 92 } 93 }) 94 return 95 } 96 97 // AssertEventboxEmpty asserts the eventbox of the provided Run is empty. 98 func AssertEventboxEmpty(ctx context.Context, runID common.RunID) { 99 iterEventBox(ctx, runID, func(evt *eventpb.Event) { 100 So(fmt.Sprintf("%s eventbox received event %s", runID, evt), ShouldBeEmpty) 101 }) 102 } 103 104 // AssertNotInEventbox asserts none of the target events exists in the Eventbox. 105 func AssertNotInEventbox(ctx context.Context, runID common.RunID, targets ...*eventpb.Event) { 106 matched, _ := matchEventBox(ctx, runID, targets) 107 So(matched, ShouldBeEmpty) 108 } 109 110 // AssertInEventbox asserts all target events exist in the Eventbox. 111 func AssertInEventbox(ctx context.Context, runID common.RunID, targets ...*eventpb.Event) { 112 _, remaining := matchEventBox(ctx, runID, targets) 113 So(remaining, ShouldBeEmpty) 114 } 115 116 // AssertReceivedStart asserts Run has received Start event. 117 func AssertReceivedStart(ctx context.Context, runID common.RunID) { 118 target := &eventpb.Event{ 119 Event: &eventpb.Event_Start{ 120 Start: &eventpb.Start{}, 121 }, 122 } 123 AssertInEventbox(ctx, runID, target) 124 } 125 126 // AssertReceivedPoke asserts Run has received Poke event that should be 127 // processed at `eta`. 128 // 129 // If `eta` is not later than now, assert that Poke event should be processed 130 // immediately. 131 func AssertReceivedPoke(ctx context.Context, runID common.RunID, eta time.Time) { 132 expect := &eventpb.Event{ 133 Event: &eventpb.Event_Poke{ 134 Poke: &eventpb.Poke{}, 135 }, 136 } 137 if eta.After(clock.Now(ctx)) { 138 expect.ProcessAfter = timestamppb.New(eta) 139 } 140 AssertInEventbox(ctx, runID, expect) 141 } 142 143 // AssertReceivedReadyForSubmission asserts Run has received ReadyForSubmission 144 // event. 145 func AssertReceivedReadyForSubmission(ctx context.Context, runID common.RunID, eta time.Time) { 146 target := &eventpb.Event{ 147 Event: &eventpb.Event_ReadyForSubmission{ 148 ReadyForSubmission: &eventpb.ReadyForSubmission{}, 149 }, 150 } 151 if !eta.IsZero() { 152 target.ProcessAfter = timestamppb.New(eta) 153 } 154 AssertInEventbox(ctx, runID, target) 155 } 156 157 // AssertReceivedCLsSubmitted asserts Run has received CLsSubmitted event for 158 // the provided CL. 159 func AssertReceivedCLsSubmitted(ctx context.Context, runID common.RunID, clids ...common.CLID) { 160 target := &eventpb.Event{ 161 Event: &eventpb.Event_ClsSubmitted{ 162 ClsSubmitted: &eventpb.CLsSubmitted{ 163 Clids: common.CLIDsAsInt64s(common.CLIDs(clids)), 164 }, 165 }, 166 } 167 AssertInEventbox(ctx, runID, target) 168 } 169 170 // AssertNotReceivedCLsSubmitted asserts Run has NOT received CLsSubmitted event 171 // for the provided CL. 172 func AssertNotReceivedCLsSubmitted(ctx context.Context, runID common.RunID, clids ...common.CLID) { 173 target := &eventpb.Event{ 174 Event: &eventpb.Event_ClsSubmitted{ 175 ClsSubmitted: &eventpb.CLsSubmitted{ 176 Clids: common.CLIDsAsInt64s(common.CLIDs(clids)), 177 }, 178 }, 179 } 180 AssertNotInEventbox(ctx, runID, target) 181 } 182 183 // AssertReceivedSubmissionCompleted asserts Run has received the provided 184 // SubmissionCompleted event. 185 func AssertReceivedSubmissionCompleted(ctx context.Context, runID common.RunID, sc *eventpb.SubmissionCompleted) { 186 target := &eventpb.Event{ 187 Event: &eventpb.Event_SubmissionCompleted{ 188 SubmissionCompleted: sc, 189 }, 190 } 191 AssertInEventbox(ctx, runID, target) 192 } 193 194 // AssertReceivedLongOpCompleted asserts Run has received LongOpCompleted event. 195 func AssertReceivedLongOpCompleted(ctx context.Context, runID common.RunID, result *eventpb.LongOpCompleted) { 196 target := &eventpb.Event{ 197 Event: &eventpb.Event_LongOpCompleted{ 198 LongOpCompleted: result, 199 }, 200 } 201 AssertInEventbox(ctx, runID, target) 202 } 203 204 // AssertReceivedParentRunCompleted asserts Run has received ParentRunCompleted event. 205 func AssertReceivedParentRunCompleted(ctx context.Context, runID common.RunID) { 206 target := &eventpb.Event{ 207 Event: &eventpb.Event_ParentRunCompleted{ 208 ParentRunCompleted: &eventpb.ParentRunCompleted{}, 209 }, 210 } 211 AssertInEventbox(ctx, runID, target) 212 } 213 214 // AssertNotReceivedParentRunCompleted asserts Run has not received ParentRunCompleted event. 215 func AssertNotReceivedParentRunCompleted(ctx context.Context, runID common.RunID) { 216 target := &eventpb.Event{ 217 Event: &eventpb.Event_ParentRunCompleted{ 218 ParentRunCompleted: &eventpb.ParentRunCompleted{}, 219 }, 220 } 221 AssertNotInEventbox(ctx, runID, target) 222 } 223 224 // MockDispatch installs and returns MockDispatcher for Run Manager. 225 func MockDispatch(ctx context.Context) (context.Context, MockDispatcher) { 226 m := MockDispatcher{&cvtesting.DispatchRecorder{}} 227 ctx = eventpb.InstallMockDispatcher(ctx, m.Dispatch) 228 return ctx, m 229 } 230 231 // MockDispatcher records in memory what would have resulted in task enqueues 232 // for a Run Manager. 233 type MockDispatcher struct { 234 *cvtesting.DispatchRecorder 235 } 236 237 // Runs returns sorted list of Run IDs. 238 func (m *MockDispatcher) Runs() common.RunIDs { 239 return common.MakeRunIDs(m.Targets()...) 240 } 241 242 // PopRuns returns sorted list of Run IDs and resets the state. 243 func (m *MockDispatcher) PopRuns() common.RunIDs { 244 return common.MakeRunIDs(m.PopTargets()...) 245 }