Skip to content

Commit 838d1f9

Browse files
authored
Fix 2021/day04 flaky tests (#65)
* fix: 2021/day04 deadlock and race issues * chore: Update test run scripts * tests: Update regression tests * refactor: Add board id * fix: 2021/day04 flaky bug
1 parent 4e97154 commit 838d1f9

File tree

5 files changed

+74
-58
lines changed

5 files changed

+74
-58
lines changed

internal/puzzles/solutions/2021/day04/solution.go

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io"
99
"regexp"
1010
"strconv"
11+
"sync"
1112

1213
"github.com/obalunenko/advent-of-code/internal/puzzles"
1314
)
@@ -105,10 +106,6 @@ func (b *bingo) start(ctx context.Context, rule winRule) (wonBoard *board, lastN
105106
for i := range players {
106107
p := players[i]
107108

108-
if !p.isActive() {
109-
continue
110-
}
111-
112109
p.input() <- n
113110
}
114111
}
@@ -142,17 +139,22 @@ func checkWinner(cancelFunc context.CancelFunc, in, out chan winner, rule winRul
142139
}
143140

144141
type player struct {
142+
mu *sync.Mutex
145143
id int
146144
in chan int
147-
win chan winSig
145+
win chan winner
148146
active bool
149147
b *board
150148
}
151149

152-
func (p player) isActive() bool {
150+
func (p *player) isActive() bool {
153151
return p.active
154152
}
155153

154+
func (p *player) setInactive() {
155+
p.active = false
156+
}
157+
156158
func (p *player) input() chan int {
157159
return p.in
158160
}
@@ -161,25 +163,36 @@ func (p *player) play(ctx context.Context) {
161163
for {
162164
select {
163165
case <-ctx.Done():
164-
p.active = false
166+
p.mu.Lock()
165167

166-
return
168+
p.setInactive()
169+
170+
p.mu.Unlock()
167171
case num := <-p.in:
172+
p.mu.Lock()
173+
if !p.isActive() {
174+
p.mu.Unlock()
175+
176+
continue
177+
}
178+
168179
pos, ok := p.b.isPresent(num)
169180
if ok {
170181
p.b.state.update(ctx, pos)
182+
171183
p.b.numbers[pos.vertical][pos.horizontal].setMarked()
172184
}
173185

174186
if p.b.state.isWon() {
175-
p.win <- winSig{
187+
p.win <- winner{
188+
id: p.id,
176189
num: num,
177190
}
178191

179-
p.active = false
180-
181-
return
192+
p.setInactive()
182193
}
194+
195+
p.mu.Unlock()
183196
}
184197
}
185198
}
@@ -189,28 +202,16 @@ type winner struct {
189202
num int
190203
}
191204

192-
type winSig struct {
193-
num int
194-
}
195-
196205
func newPlayer(ctx context.Context, id int, wonSig chan winner, b *board) *player {
197206
p := player{
207+
mu: &sync.Mutex{},
198208
id: id,
199209
in: make(chan int),
200-
win: make(chan winSig),
210+
win: wonSig,
201211
active: true,
202212
b: b,
203213
}
204214

205-
go func() {
206-
sig := <-p.win
207-
208-
wonSig <- winner{
209-
id: p.id,
210-
num: sig.num,
211-
}
212-
}()
213-
214215
go p.play(ctx)
215216

216217
return &p
@@ -230,6 +231,7 @@ func (n *number) setMarked() {
230231
}
231232

232233
type board struct {
234+
id int
233235
numbers [boardSize][boardSize]number
234236
state state
235237
}
@@ -258,8 +260,15 @@ func (p position) String() string {
258260
}
259261

260262
type state struct {
261-
verticals map[int]int
262-
horizontals map[int]int
263+
verticals [boardSize]int
264+
horizontals [boardSize]int
265+
}
266+
267+
func newState() state {
268+
return state{
269+
verticals: [boardSize]int{},
270+
horizontals: [boardSize]int{},
271+
}
263272
}
264273

265274
func (s *state) String() string {
@@ -271,7 +280,7 @@ func (s *state) update(_ context.Context, p position) {
271280
s.horizontals[p.vertical]++
272281
}
273282

274-
func (s state) isWon() bool {
283+
func (s *state) isWon() bool {
275284
for i := 0; i < boardSize; i++ {
276285
if s.verticals[i] == boardSize || s.horizontals[i] == boardSize {
277286
return true
@@ -339,7 +348,7 @@ func newBingoGame(input io.Reader) (*bingo, error) {
339348
case emptyLine:
340349
boardsNum++
341350

342-
bg.boards = append(bg.boards, newBoard())
351+
bg.boards = append(bg.boards, newBoard(boardsNum))
343352

344353
cursor = 0
345354
case boardLine:
@@ -363,13 +372,11 @@ func newBingoGame(input io.Reader) (*bingo, error) {
363372
return &bg, nil
364373
}
365374

366-
func newBoard() *board {
375+
func newBoard(id int) *board {
367376
return &board{
368-
numbers: [5][5]number{},
369-
state: state{
370-
verticals: make(map[int]int),
371-
horizontals: make(map[int]int),
372-
},
377+
id: id,
378+
numbers: [boardSize][boardSize]number{},
379+
state: newState(),
373380
}
374381
}
375382

internal/puzzles/solutions/2021/day04/solution_test.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -121,42 +121,45 @@ func Test_newBingoGame(t *testing.T) {
121121
input: []int{7, 4, 9, 5, 11, 17, 23, 2, 0, 14, 21, 24, 10, 16, 13, 6, 15, 25, 12, 22, 18, 20, 8, 19, 3, 26, 1},
122122
boards: []*board{
123123
{
124-
numbers: [5][5]number{
124+
id: 1,
125+
numbers: [boardSize][boardSize]number{
125126
{number{val: 22}, number{val: 13}, number{val: 17}, number{val: 11}, number{val: 0}},
126127
{number{val: 8}, number{val: 2}, number{val: 23}, number{val: 4}, number{val: 24}},
127128
{number{val: 21}, number{val: 9}, number{val: 14}, number{val: 16}, number{val: 7}},
128129
{number{val: 6}, number{val: 10}, number{val: 3}, number{val: 18}, number{val: 5}},
129130
{number{val: 1}, number{val: 12}, number{val: 20}, number{val: 15}, number{val: 19}},
130131
},
131132
state: state{
132-
verticals: map[int]int{},
133-
horizontals: map[int]int{},
133+
verticals: [boardSize]int{},
134+
horizontals: [boardSize]int{},
134135
},
135136
},
136137
{
137-
numbers: [5][5]number{
138+
id: 2,
139+
numbers: [boardSize][boardSize]number{
138140
{number{val: 3}, number{val: 15}, number{val: 0}, number{val: 2}, number{val: 22}},
139141
{number{val: 9}, number{val: 18}, number{val: 13}, number{val: 17}, number{val: 5}},
140142
{number{val: 19}, number{val: 8}, number{val: 7}, number{val: 25}, number{val: 23}},
141143
{number{val: 20}, number{val: 11}, number{val: 10}, number{val: 24}, number{val: 4}},
142144
{number{val: 14}, number{val: 21}, number{val: 16}, number{val: 12}, number{val: 6}},
143145
},
144146
state: state{
145-
verticals: map[int]int{},
146-
horizontals: map[int]int{},
147+
verticals: [boardSize]int{},
148+
horizontals: [boardSize]int{},
147149
},
148150
},
149151
{
150-
numbers: [5][5]number{
152+
id: 3,
153+
numbers: [boardSize][boardSize]number{
151154
{number{val: 14}, number{val: 21}, number{val: 17}, number{val: 24}, number{val: 4}},
152155
{number{val: 10}, number{val: 16}, number{val: 15}, number{val: 9}, number{val: 19}},
153156
{number{val: 18}, number{val: 8}, number{val: 23}, number{val: 26}, number{val: 20}},
154157
{number{val: 22}, number{val: 11}, number{val: 13}, number{val: 6}, number{val: 5}},
155158
{number{val: 2}, number{val: 0}, number{val: 12}, number{val: 3}, number{val: 7}},
156159
},
157160
state: state{
158-
verticals: map[int]int{},
159-
horizontals: map[int]int{},
161+
verticals: [boardSize]int{},
162+
horizontals: [boardSize]int{},
160163
},
161164
},
162165
},
@@ -185,6 +188,7 @@ func Test_bingo_start(t *testing.T) {
185188

186189
type args struct {
187190
ctx context.Context
191+
wr winRule
188192
}
189193

190194
type expected struct {
@@ -201,9 +205,11 @@ func Test_bingo_start(t *testing.T) {
201205
name: "",
202206
args: args{
203207
ctx: ctx,
208+
wr: rule(1),
204209
},
205210
expected: expected{
206211
board: &board{
212+
id: 3,
207213
numbers: [5][5]number{
208214
{
209215
number{val: 14, isMarked: true},
@@ -242,14 +248,14 @@ func Test_bingo_start(t *testing.T) {
242248
},
243249
},
244250
state: state{
245-
verticals: map[int]int{
251+
verticals: [boardSize]int{
246252
0: 2,
247253
1: 3,
248254
2: 2,
249255
3: 2,
250256
4: 3,
251257
},
252-
horizontals: map[int]int{
258+
horizontals: [boardSize]int{
253259
0: 5,
254260
1: 1,
255261
2: 1,
@@ -267,7 +273,7 @@ func Test_bingo_start(t *testing.T) {
267273
t.Run(tt.name, func(t *testing.T) {
268274
b := bgame
269275

270-
gotBoard, gotNum := b.start(tt.args.ctx, rule(1))
276+
gotBoard, gotNum := b.start(tt.args.ctx, tt.args.wr)
271277

272278
equalBoards(t, tt.expected.board, gotBoard)
273279
assert.Equal(t, tt.expected.num, gotNum)
@@ -278,13 +284,16 @@ func Test_bingo_start(t *testing.T) {
278284
func equalBoards(t testing.TB, expected, got *board) {
279285
assert.Equal(t, expected.numbers, got.numbers, "numbers")
280286

287+
assert.Equal(t, expected.id, got.id, "id")
288+
281289
assert.Equal(t, fmt.Sprint(expected.state.horizontals), fmt.Sprint(got.state.horizontals), "horizontals")
282290

283291
assert.Equal(t, fmt.Sprint(expected.state.verticals), fmt.Sprint(got.state.verticals), "verticals")
284292
}
285293

286294
func Test_board_sumMarked(t *testing.T) {
287295
b := board{
296+
id: 0,
288297
numbers: [5][5]number{
289298
{
290299
number{val: 14, isMarked: true},
@@ -323,14 +332,14 @@ func Test_board_sumMarked(t *testing.T) {
323332
},
324333
},
325334
state: state{
326-
verticals: map[int]int{
335+
verticals: [boardSize]int{
327336
0: 2,
328337
1: 3,
329338
2: 2,
330339
3: 2,
331340
4: 3,
332341
},
333-
horizontals: map[int]int{
342+
horizontals: [boardSize]int{
334343
0: 5,
335344
1: 1,
336345
2: 1,

scripts/tests/run-regression.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ export AOC_REGRESSION_ENABLED=true
1010

1111
REGRESSION_TESTS_PKG=$(go list -m)/tests
1212

13-
GOTEST="go test -v -race"
14-
if [ -f "$(go env GOPATH)/bin/gotestsum" ] || [ -f "/usr/local/bin/gotestsum" ]; then
13+
GOTEST="go test "
14+
if command -v "gotestsum" &>/dev/null; then
1515
GOTEST="gotestsum --format pkgname --"
1616
fi
1717

18-
${GOTEST} "${REGRESSION_TESTS_PKG}"
18+
${GOTEST} -race "${REGRESSION_TESTS_PKG}"
1919

2020
unset AOC_REGRESSION_ENABLED
2121

scripts/tests/run.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
#!/bin/sh
1+
#!/bin/bash
22

33
set -eu pipefail
44

55
SCRIPT_NAME="$(basename "$0")"
66

77
echo "${SCRIPT_NAME} is running... "
88

9-
GOTEST="go test -v -race"
10-
if [ -f "$(go env GOPATH)/bin/gotestsum" ] || [ -f "/usr/local/bin/gotestsum" ]; then
9+
GOTEST="go test -v "
10+
if command -v "gotestsum" &>/dev/null; then
1111
GOTEST="gotestsum --format pkgname --"
1212
fi
1313

14-
${GOTEST} ./...
14+
${GOTEST} -race ./...
1515

1616
echo "${SCRIPT_NAME} done."

tests/regression_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2303,7 +2303,7 @@ func testcases2021() []testcase {
23032303
Year: year,
23042304
Name: puzzles.Day04.String(),
23052305
Part1: "58374",
2306-
Part2: "",
2306+
Part2: "11377",
23072307
},
23082308
wantErr: false,
23092309
},

0 commit comments

Comments
 (0)