From fc2e047ed17468b59fdef33dd0cbfd2712cb7f80 Mon Sep 17 00:00:00 2001 From: Fedor Korotkiy Date: Fri, 12 Mar 2021 19:47:54 +0300 Subject: [PATCH] Add fileleak task --- fileleak/README.md | 23 ++++++++++++ fileleak/fileleak.go | 12 ++++++ fileleak/fileleak_test.go | 79 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 fileleak/README.md create mode 100644 fileleak/fileleak.go create mode 100644 fileleak/fileleak_test.go diff --git a/fileleak/README.md b/fileleak/README.md new file mode 100644 index 0000000..384d97e --- /dev/null +++ b/fileleak/README.md @@ -0,0 +1,23 @@ +# fileleak + +Реализуйте библиотеку для поиска утечек файлов в тестах. + +Библиотека содержит единственную функцию - `VerifyNone`. Пользователь должен вызвать эту функцию в начале своего теста. + +Библиотека смотрит на все открытые файлы процесса в начале и в конце теста. Если в конце теста будут находятся открытые файлы, +которые не были открыты на момент старта теста, то библиотека фейлит весь тест. + +Эта задача будет корректно работать только на `linux`. Чтобы узнать открытые файлы процесса, нужно прочитать директорию +`/proc/self/fd`. Эта директория содержит символические ссылки. Именем ссылки является номер файлового дескриптора, а значением +ссылки является какое-то текстовое описание открытого файла. Значение ссылки можно прочитать, используя `os.Readlink`. + +```bash +prime@bee ~/C/shad-go> ls -lah /proc/self/fd +total 0 +dr-x------ 2 prime prime 0 мар 12 19:44 ./ +dr-xr-xr-x 9 prime prime 0 мар 12 19:44 ../ +lrwx------ 1 prime prime 64 мар 12 19:44 0 -> /dev/pts/7 +lrwx------ 1 prime prime 64 мар 12 19:44 1 -> /dev/pts/7 +lrwx------ 1 prime prime 64 мар 12 19:44 2 -> /dev/pts/7 +lr-x------ 1 prime prime 64 мар 12 19:44 3 -> /proc/871308/fd/ +``` diff --git a/fileleak/fileleak.go b/fileleak/fileleak.go new file mode 100644 index 0000000..645339b --- /dev/null +++ b/fileleak/fileleak.go @@ -0,0 +1,12 @@ +// +build !solution + +package fileleak + +type testingT interface { + Errorf(msg string, args ...interface{}) + Cleanup(func()) +} + +func VerifyNone(t testingT) { + panic("implement me") +} diff --git a/fileleak/fileleak_test.go b/fileleak/fileleak_test.go new file mode 100644 index 0000000..e8e36d7 --- /dev/null +++ b/fileleak/fileleak_test.go @@ -0,0 +1,79 @@ +package fileleak_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "gitlab.com/slon/shad-go/fileleak" +) + +type fakeT struct { + failed bool + buffer bytes.Buffer + cleanup []func() +} + +func (f *fakeT) Errorf(msg string, args ...interface{}) { + f.failed = true + fmt.Fprintf(&f.buffer, msg, args...) +} + +func (f *fakeT) Cleanup(cb func()) { + f.cleanup = append([]func(){cb}, f.cleanup...) +} + +func checkLeak(t *testing.T, shouldDetect bool, leaker func()) { + var fake fakeT + fileleak.VerifyNone(&fake) + leaker() + for _, cb := range fake.cleanup { + cb() + } + + switch { + case shouldDetect && !fake.failed: + t.Errorf("fileleak didn't detect a leak") + case !shouldDetect && fake.failed: + t.Errorf("fileleak detected a leak when there is none:\n%s", fake.buffer.String()) + } +} + +func TestFileLeak_OpenFile(t *testing.T) { + var f *os.File + checkLeak(t, true, func() { + var err error + f, err = os.Open("/proc/self/exe") + require.NoError(t, err) + }) + + if f != nil { + _ = f.Close() + } +} + +func TestFileLeak_AlwaysOpenFile(t *testing.T) { + f, err := os.Open("/proc/self/exe") + require.NoError(t, err) + defer f.Close() + + checkLeak(t, false, func() {}) +} + +func TestFileLeak_ReopenFile(t *testing.T) { + f, err := os.Open("/proc/self/exe") + require.NoError(t, err) + defer f.Close() + + checkLeak(t, true, func() { + _ = f.Close() + + ff, err := ioutil.TempFile("", "") + require.NoError(t, err) + f = ff + }) +}