Fix channel closing race in event tests.
Divide event matching into two functions, a matcher and a processor. That way, the error handling doesn't call the channel closing logic at all. Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
parent
2705fa573b
commit
27b060492c
3 changed files with 34 additions and 15 deletions
|
@ -178,7 +178,8 @@ func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
matcher := matchEventLine(buildID, "container", testActions)
|
matcher := matchEventLine(buildID, "container", testActions)
|
||||||
go observer.Match(matcher)
|
processor := processEventMatch(testActions)
|
||||||
|
go observer.Match(matcher, processor)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-time.After(10 * time.Second):
|
case <-time.After(10 * time.Second):
|
||||||
|
|
|
@ -224,7 +224,8 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
matcher := matchEventLine(containerID, "container", testActions)
|
matcher := matchEventLine(containerID, "container", testActions)
|
||||||
go observer.Match(matcher)
|
processor := processEventMatch(testActions)
|
||||||
|
go observer.Match(matcher, processor)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-time.After(5 * time.Second):
|
case <-time.After(5 * time.Second):
|
||||||
|
@ -280,7 +281,8 @@ func (s *DockerSuite) TestEventsImageUntagDelete(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
matcher := matchEventLine(imageID, "image", testActions)
|
matcher := matchEventLine(imageID, "image", testActions)
|
||||||
go observer.Match(matcher)
|
processor := processEventMatch(testActions)
|
||||||
|
go observer.Match(matcher, processor)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-time.After(10 * time.Second):
|
case <-time.After(10 * time.Second):
|
||||||
|
|
|
@ -28,7 +28,13 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// eventMatcher is a function that tries to match an event input.
|
// eventMatcher is a function that tries to match an event input.
|
||||||
type eventMatcher func(text string) bool
|
// It returns true if the event matches and a map with
|
||||||
|
// a set of key/value to identify the match.
|
||||||
|
type eventMatcher func(text string) (map[string]string, bool)
|
||||||
|
|
||||||
|
// eventMatchProcessor is a function to handle an event match.
|
||||||
|
// It receives a map of key/value with the information extracted in a match.
|
||||||
|
type eventMatchProcessor func(matches map[string]string)
|
||||||
|
|
||||||
// eventObserver runs an events commands and observes its output.
|
// eventObserver runs an events commands and observes its output.
|
||||||
type eventObserver struct {
|
type eventObserver struct {
|
||||||
|
@ -79,13 +85,15 @@ func (e *eventObserver) Stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match tries to match the events output with a given matcher.
|
// Match tries to match the events output with a given matcher.
|
||||||
func (e *eventObserver) Match(match eventMatcher) {
|
func (e *eventObserver) Match(match eventMatcher, process eventMatchProcessor) {
|
||||||
for e.scanner.Scan() {
|
for e.scanner.Scan() {
|
||||||
text := e.scanner.Text()
|
text := e.scanner.Text()
|
||||||
e.buffer.WriteString(text)
|
e.buffer.WriteString(text)
|
||||||
e.buffer.WriteString("\n")
|
e.buffer.WriteString("\n")
|
||||||
|
|
||||||
match(text)
|
if matches, ok := match(text); ok {
|
||||||
|
process(matches)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.scanner.Err()
|
err := e.scanner.Err()
|
||||||
|
@ -106,7 +114,7 @@ func (e *eventObserver) CheckEventError(c *check.C, id, event string, match even
|
||||||
out, _ := dockerCmd(c, "events", "--since", e.startTime, "--until", until)
|
out, _ := dockerCmd(c, "events", "--since", e.startTime, "--until", until)
|
||||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||||
for _, e := range events {
|
for _, e := range events {
|
||||||
if match(e) {
|
if _, ok := match(e); ok {
|
||||||
foundEvent = true
|
foundEvent = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -119,22 +127,30 @@ func (e *eventObserver) CheckEventError(c *check.C, id, event string, match even
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchEventLine matches a text with the event regular expression.
|
// matchEventLine matches a text with the event regular expression.
|
||||||
// It returns the action and true if the regular expression matches with the given id and event type.
|
// It returns the matches and true if the regular expression matches with the given id and event type.
|
||||||
// It returns an empty string and false if there is no match.
|
// It returns an empty map and false if there is no match.
|
||||||
func matchEventLine(id, eventType string, actions map[string]chan bool) eventMatcher {
|
func matchEventLine(id, eventType string, actions map[string]chan bool) eventMatcher {
|
||||||
return func(text string) bool {
|
return func(text string) (map[string]string, bool) {
|
||||||
matches := parseEventText(text)
|
matches := parseEventText(text)
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
return false
|
return matches, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if matchIDAndEventType(matches, id, eventType) {
|
if matchIDAndEventType(matches, id, eventType) {
|
||||||
if ch, ok := actions[matches["action"]]; ok {
|
if _, ok := actions[matches["action"]]; ok {
|
||||||
close(ch)
|
return matches, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return matches, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// processEventMatch closes an action channel when an event line matches the expected action.
|
||||||
|
func processEventMatch(actions map[string]chan bool) eventMatchProcessor {
|
||||||
|
return func(matches map[string]string) {
|
||||||
|
if ch, ok := actions[matches["action"]]; ok {
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue