From a7e5674020ffcbaa299d476cfddf5bb694074434 Mon Sep 17 00:00:00 2001 From: itchyny Date: Fri, 18 Oct 2024 05:42:49 +0900 Subject: [PATCH] fix goroutine leaks and close channels in window tests --- window/manager_test.go | 124 ++++++++++++++++++++++++++--------------- window/window_test.go | 75 +++++++++++-------------- 2 files changed, 112 insertions(+), 87 deletions(-) diff --git a/window/manager_test.go b/window/manager_test.go index 4d28d64..51fa119 100644 --- a/window/manager_test.go +++ b/window/manager_test.go @@ -33,7 +33,15 @@ func TestManagerOpenEmpty(t *testing.T) { eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) go func() { - <-redrawCh + defer close(eventCh) + defer close(redrawCh) + ev := <-eventCh + if ev.Type != event.Error { + t.Errorf("event type should be %d but got: %d", event.Error, ev.Type) + } + if expected := "no file name"; ev.Error.Error() != expected { + t.Errorf("err should be %q but got: %v", expected, ev.Error) + } }() wm.SetSize(110, 20) if err := wm.Open(""); err != nil { @@ -62,14 +70,7 @@ func TestManagerOpenEmpty(t *testing.T) { if err != nil { t.Errorf("err should be nil but got: %v", err) } - go wm.Emit(event.Event{Type: event.Write}) - ev := <-eventCh - if ev.Type != event.Error { - t.Errorf("event type should be %d but got: %d", event.Error, ev.Type) - } - if expected := "no file name"; ev.Error.Error() != expected { - t.Errorf("err should be %q but got: %v", expected, ev.Error) - } + wm.Emit(event.Event{Type: event.Write}) wm.Close() } @@ -77,6 +78,10 @@ func TestManagerOpenStates(t *testing.T) { wm := NewManager() eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) + go func() { + defer close(eventCh) + defer close(redrawCh) + }() wm.SetSize(110, 20) str := "Hello, world! こんにちは、世界!" f, err := createTemp(t.TempDir(), str) @@ -117,11 +122,13 @@ func TestManagerOpenNonExistsWrite(t *testing.T) { eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) go func() { - for { - select { - case <-eventCh: - case <-redrawCh: - } + defer close(eventCh) + defer close(redrawCh) + for range 16 { + <-redrawCh + } + if ev := <-eventCh; ev.Type != event.QuitAll { + t.Errorf("event type should be %d but got: %d", event.QuitAll, ev.Type) } }() wm.SetSize(110, 20) @@ -175,6 +182,10 @@ func TestManagerOpenExpandBacktick(t *testing.T) { wm := NewManager() eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) + go func() { + defer close(eventCh) + defer close(redrawCh) + }() wm.SetSize(110, 20) cmd, name := "`which ls`", "ls" if runtime.GOOS == "windows" { @@ -210,6 +221,10 @@ func TestEditorOpenExpandHomedir(t *testing.T) { wm := NewManager() eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) + go func() { + defer close(eventCh) + defer close(redrawCh) + }() wm.SetSize(110, 20) str := "Hello, world!" f, err := createTemp(t.TempDir(), str) @@ -248,19 +263,39 @@ func TestManagerOpenChdirWrite(t *testing.T) { wm := NewManager() eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) - go func() { - for { - select { - case <-eventCh: - case <-redrawCh: - } - } - }() - wm.SetSize(110, 20) f, err := createTemp(t.TempDir(), "Hello") if err != nil { t.Fatalf("err should be nil but got: %v", err) } + go func() { + defer close(eventCh) + defer close(redrawCh) + ev, dir := <-eventCh, filepath.Dir(f.Name()) + if ev.Type != event.Info { + t.Errorf("event type should be %d but got: %d", event.Info, ev.Type) + } + if expected := dir; ev.Error.Error() != expected { + t.Errorf("err should be %q but got: %v", expected, ev.Error) + } + ev = <-eventCh + if ev.Type != event.Info { + t.Errorf("event type should be %d but got: %d", event.Info, ev.Type) + } + if expected := filepath.Dir(dir); ev.Error.Error() != expected { + t.Errorf("err should be %q but got: %v", expected, ev.Error) + } + for range 11 { + <-redrawCh + } + ev = <-eventCh + if ev.Type != event.Info { + t.Errorf("event type should be %d but got: %d", event.Info, ev.Type) + } + if expected := "13 (0xd) bytes written"; !strings.HasSuffix(ev.Error.Error(), expected) { + t.Errorf("err should be %q but got: %v", expected, ev.Error) + } + }() + wm.SetSize(110, 20) wm.Emit(event.Event{Type: event.Chdir, Arg: filepath.Dir(f.Name())}) if err := wm.Open(filepath.Base(f.Name())); err != nil { t.Fatalf("err should be nil but got: %v", err) @@ -292,12 +327,8 @@ func TestManagerOpenDirectory(t *testing.T) { eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) go func() { - for { - select { - case <-eventCh: - case <-redrawCh: - } - } + defer close(eventCh) + defer close(redrawCh) }() wm.SetSize(110, 20) dir := t.TempDir() @@ -315,6 +346,10 @@ func TestManagerRead(t *testing.T) { wm := NewManager() eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) + go func() { + defer close(eventCh) + defer close(redrawCh) + }() wm.SetSize(110, 20) r := strings.NewReader("Hello, world!") if err := wm.Read(r); err != nil { @@ -348,11 +383,10 @@ func TestManagerOnly(t *testing.T) { eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) go func() { - for { - select { - case <-eventCh: - case <-redrawCh: - } + defer close(eventCh) + defer close(redrawCh) + for range 4 { + <-eventCh } }() wm.SetSize(110, 20) @@ -387,11 +421,10 @@ func TestManagerAlternative(t *testing.T) { eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) go func() { - for { - select { - case <-eventCh: - case <-redrawCh: - } + defer close(eventCh) + defer close(redrawCh) + for range 9 { + <-eventCh } }() wm.SetSize(110, 20) @@ -483,11 +516,10 @@ func TestManagerWincmd(t *testing.T) { eventCh, redrawCh := make(chan event.Event), make(chan struct{}) wm.Init(eventCh, redrawCh) go func() { - for { - select { - case <-eventCh: - case <-redrawCh: - } + defer close(eventCh) + defer close(redrawCh) + for range 17 { + <-eventCh } }() wm.SetSize(110, 20) @@ -556,6 +588,9 @@ func TestManagerCopyCutPaste(t *testing.T) { } _, _, _, _ = wm.State() go func() { + defer close(eventCh) + defer close(redrawCh) + defer close(waitCh) <-redrawCh <-redrawCh <-redrawCh @@ -620,7 +655,6 @@ func TestManagerCopyCutPaste(t *testing.T) { if expected := "Hefoobarfoobarfoobarlrld!"; !strings.HasPrefix(string(ws.Bytes), expected) { t.Errorf("Bytes should start with %q but got %q", expected, string(ws.Bytes)) } - close(waitCh) }() wm.Emit(event.Event{Type: event.CursorNext, Mode: mode.Normal, Count: 3}) wm.Emit(event.Event{Type: event.StartVisual}) diff --git a/window/window_test.go b/window/window_test.go index 1f69ccf..d1569a2 100644 --- a/window/window_test.go +++ b/window/window_test.go @@ -14,7 +14,7 @@ import ( func TestWindowState(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 10 - window, err := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, err := newWindow(r, "test", "test", nil, nil) if err != nil { t.Fatal(err) } @@ -70,7 +70,7 @@ func TestWindowState(t *testing.T) { func TestWindowEmptyState(t *testing.T) { r := strings.NewReader("") width, height := 16, 10 - window, err := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, err := newWindow(r, "test", "test", nil, nil) if err != nil { t.Fatal(err) } @@ -136,7 +136,7 @@ func TestWindowEmptyState(t *testing.T) { func TestWindowCursorMotions(t *testing.T) { r := strings.NewReader(strings.Repeat("Hello, world!", 100)) width, height := 16, 10 - window, err := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, err := newWindow(r, "test", "test", nil, nil) if err != nil { t.Fatal(err) } @@ -437,7 +437,7 @@ func TestWindowCursorMotions(t *testing.T) { func TestWindowScreenMotions(t *testing.T) { r := strings.NewReader(strings.Repeat("Hello, world!", 100)) width, height := 16, 10 - window, err := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, err := newWindow(r, "test", "test", nil, nil) if err != nil { t.Fatal(err) } @@ -619,13 +619,7 @@ func TestWindowScreenMotions(t *testing.T) { func TestWindowDeleteBytes(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 10 - eventCh := make(chan event.Event) - go func() { - for { - <-eventCh - } - }() - window, _ := newWindow(r, "test", "test", eventCh, make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.cursorNext(mode.Normal, 7) @@ -687,13 +681,7 @@ func TestWindowDeleteBytes(t *testing.T) { func TestWindowDeletePrevBytes(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 10 - eventCh := make(chan event.Event) - go func() { - for { - <-eventCh - } - }() - window, _ := newWindow(r, "test", "test", eventCh, make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.cursorNext(mode.Normal, 5) @@ -728,7 +716,7 @@ func TestWindowDeletePrevBytes(t *testing.T) { func TestWindowIncrementDecrement(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.increment(0) @@ -778,7 +766,7 @@ func TestWindowIncrementDecrement(t *testing.T) { func TestWindowIncrementDecrementEmpty(t *testing.T) { r := strings.NewReader("") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) s, _ := window.state(width, height) @@ -801,7 +789,7 @@ func TestWindowIncrementDecrementEmpty(t *testing.T) { t.Errorf("s.Length should be %d but got %d", expected, s.Length) } - window, _ = newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ = newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.decrement(0) @@ -820,7 +808,7 @@ func TestWindowIncrementDecrementEmpty(t *testing.T) { func TestWindowInsertByte(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 1 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.cursorNext(mode.Normal, 7) @@ -879,7 +867,7 @@ func TestWindowInsertByte(t *testing.T) { func TestWindowInsertEmpty(t *testing.T) { r := strings.NewReader("") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.startInsert() @@ -915,7 +903,7 @@ func TestWindowInsertEmpty(t *testing.T) { func TestWindowInsertHead(t *testing.T) { r := strings.NewReader(strings.Repeat("Hello, world!", 2)) width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.pageEnd() @@ -948,7 +936,7 @@ func TestWindowInsertHead(t *testing.T) { func TestWindowInsertHeadEmpty(t *testing.T) { r := strings.NewReader("") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.startInsertHead() @@ -984,7 +972,7 @@ func TestWindowInsertHeadEmpty(t *testing.T) { func TestWindowAppend(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.cursorNext(mode.Normal, 7) @@ -1028,7 +1016,7 @@ func TestWindowAppend(t *testing.T) { func TestWindowAppendEmpty(t *testing.T) { r := strings.NewReader("") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.startAppend() @@ -1075,7 +1063,7 @@ func TestWindowAppendEmpty(t *testing.T) { func TestWindowReplaceByte(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.cursorNext(mode.Normal, 7) @@ -1102,7 +1090,7 @@ func TestWindowReplaceByte(t *testing.T) { func TestWindowReplaceByteEmpty(t *testing.T) { r := strings.NewReader("") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.startReplaceByte() @@ -1128,7 +1116,7 @@ func TestWindowReplaceByteEmpty(t *testing.T) { func TestWindowReplace(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.cursorNext(mode.Normal, 10) @@ -1175,7 +1163,7 @@ func TestWindowReplace(t *testing.T) { func TestWindowReplaceEmpty(t *testing.T) { r := strings.NewReader("") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.startReplace() @@ -1204,7 +1192,7 @@ func TestWindowReplaceEmpty(t *testing.T) { func TestWindowInsertByte2(t *testing.T) { r := strings.NewReader("") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.startInsert() @@ -1234,7 +1222,7 @@ func TestWindowInsertByte2(t *testing.T) { func TestWindowBackspace(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.cursorNext(mode.Normal, 5) @@ -1258,7 +1246,7 @@ func TestWindowBackspace(t *testing.T) { func TestWindowBackspacePending(t *testing.T) { r := strings.NewReader("Hello, world!") width, height := 16, 10 - window, _ := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, _ := newWindow(r, "test", "test", nil, nil) window.setSize(width, height) window.cursorNext(mode.Normal, 5) @@ -1288,11 +1276,12 @@ func TestWindowBackspacePending(t *testing.T) { func TestWindowEventRune(t *testing.T) { width, height := 16, 10 redrawCh := make(chan struct{}) - window, _ := newWindow(strings.NewReader(""), "test", "test", make(chan event.Event), redrawCh) + window, _ := newWindow(strings.NewReader(""), "test", "test", nil, redrawCh) window.setSize(width, height) str := "48723fffab" go func() { + defer close(redrawCh) window.emit(event.Event{Type: event.StartInsert}) for _, r := range str { window.emit(event.Event{Type: event.Rune, Rune: r, Mode: mode.Insert}) @@ -1306,16 +1295,18 @@ func TestWindowEventRune(t *testing.T) { if expected := "\x48\x72\x3f\xff\xab\x00"; !strings.HasPrefix(string(s.Bytes), expected) { t.Errorf("s.Bytes should start with %q but got %q", expected, string(s.Bytes)) } + <-redrawCh } func TestWindowEventRuneText(t *testing.T) { width, height := 16, 10 redrawCh := make(chan struct{}) - window, _ := newWindow(strings.NewReader(""), "test", "test", make(chan event.Event), redrawCh) + window, _ := newWindow(strings.NewReader(""), "test", "test", nil, redrawCh) window.setSize(width, height) str := "Hello, World!\nこんにちは、世界!\n鰰は魚の一種" go func() { + defer close(redrawCh) window.emit(event.Event{Type: event.SwitchFocus}) window.emit(event.Event{Type: event.StartInsert}) for _, r := range str { @@ -1331,18 +1322,15 @@ func TestWindowEventRuneText(t *testing.T) { if expected := str + "\x00"; !strings.HasPrefix(string(s.Bytes), expected) { t.Errorf("s.Bytes should start with %q but got %q", expected, string(s.Bytes)) } + <-redrawCh } func TestWindowEventUndoRedo(t *testing.T) { width, height := 16, 10 redrawCh := make(chan struct{}) - window, _ := newWindow(strings.NewReader("Hello, world!"), "test", "test", make(chan event.Event), redrawCh) + window, _ := newWindow(strings.NewReader("Hello, world!"), "test", "test", nil, redrawCh) window.setSize(width, height) waitCh := make(chan struct{}) - defer func() { - close(waitCh) - close(redrawCh) - }() waitRedraw := func(count int) { for range count { @@ -1350,6 +1338,8 @@ func TestWindowEventUndoRedo(t *testing.T) { } } go func() { + defer close(redrawCh) + defer close(waitCh) window.emit(event.Event{Type: event.Undo}) window.emit(event.Event{Type: event.SwitchFocus}) window.emit(event.Event{Type: event.StartAppend, Mode: mode.Insert}) @@ -1439,11 +1429,12 @@ func TestWindowEventUndoRedo(t *testing.T) { if expected := int64(4); s.Cursor != expected { t.Errorf("s.Cursor should be %d but got %d", expected, s.Cursor) } + <-redrawCh } func TestWindowWriteTo(t *testing.T) { r := strings.NewReader("Hello, world!") - window, err := newWindow(r, "test", "test", make(chan event.Event), make(chan struct{})) + window, err := newWindow(r, "test", "test", nil, nil) if err != nil { t.Fatal(err) }