mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 02:01:14 +01:00 
			
		
		
		
	Updates #11058 Change-Id: I35e7ef9b90e83cac04ca93fd964ad00ed5b48430 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			211 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) Tailscale Inc & AUTHORS
 | 
						|
// SPDX-License-Identifier: BSD-3-Clause
 | 
						|
 | 
						|
package filch
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"os"
 | 
						|
	"runtime"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
	"unicode"
 | 
						|
	"unsafe"
 | 
						|
 | 
						|
	"tailscale.com/tstest"
 | 
						|
)
 | 
						|
 | 
						|
type filchTest struct {
 | 
						|
	*Filch
 | 
						|
}
 | 
						|
 | 
						|
func newFilchTest(t *testing.T, filePrefix string, opts Options) *filchTest {
 | 
						|
	f, err := New(filePrefix, opts)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	return &filchTest{Filch: f}
 | 
						|
}
 | 
						|
 | 
						|
func (f *filchTest) write(t *testing.T, s string) {
 | 
						|
	t.Helper()
 | 
						|
	if _, err := f.Write([]byte(s)); err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (f *filchTest) read(t *testing.T, want string) {
 | 
						|
	t.Helper()
 | 
						|
	if b, err := f.TryReadLine(); err != nil {
 | 
						|
		t.Fatalf("r.ReadLine() err=%v", err)
 | 
						|
	} else if got := strings.TrimRightFunc(string(b), unicode.IsSpace); got != want {
 | 
						|
		t.Errorf("r.ReadLine()=%q, want %q", got, want)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (f *filchTest) readEOF(t *testing.T) {
 | 
						|
	t.Helper()
 | 
						|
	if b, err := f.TryReadLine(); b != nil || err != nil {
 | 
						|
		t.Fatalf("r.ReadLine()=%q err=%v, want nil slice", string(b), err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (f *filchTest) close(t *testing.T) {
 | 
						|
	t.Helper()
 | 
						|
	if err := f.Close(); err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestDropOldLogs(t *testing.T) {
 | 
						|
	const line1 = "123456789" // 10 bytes (9+newline)
 | 
						|
	tests := []struct {
 | 
						|
		write, read int
 | 
						|
	}{
 | 
						|
		{10, 10},
 | 
						|
		{100, 100},
 | 
						|
		{200, 200},
 | 
						|
		{250, 150},
 | 
						|
		{500, 200},
 | 
						|
	}
 | 
						|
	for _, tc := range tests {
 | 
						|
		t.Run(fmt.Sprintf("w%d-r%d", tc.write, tc.read), func(t *testing.T) {
 | 
						|
			filePrefix := t.TempDir()
 | 
						|
			f := newFilchTest(t, filePrefix, Options{ReplaceStderr: false, MaxFileSize: 1000})
 | 
						|
			defer f.close(t)
 | 
						|
			// Make filch rotate the logs 3 times
 | 
						|
			for range tc.write {
 | 
						|
				f.write(t, line1)
 | 
						|
			}
 | 
						|
			// We should only be able to read the last 150 lines
 | 
						|
			for i := range tc.read {
 | 
						|
				f.read(t, line1)
 | 
						|
				if t.Failed() {
 | 
						|
					t.Logf("could only read %d lines", i)
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
			f.readEOF(t)
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestQueue(t *testing.T) {
 | 
						|
	filePrefix := t.TempDir()
 | 
						|
	f := newFilchTest(t, filePrefix, Options{ReplaceStderr: false})
 | 
						|
 | 
						|
	f.readEOF(t)
 | 
						|
	const line1 = "Hello, World!"
 | 
						|
	const line2 = "This is a test."
 | 
						|
	const line3 = "Of filch."
 | 
						|
	f.write(t, line1)
 | 
						|
	f.write(t, line2)
 | 
						|
	f.read(t, line1)
 | 
						|
	f.write(t, line3)
 | 
						|
	f.read(t, line2)
 | 
						|
	f.read(t, line3)
 | 
						|
	f.readEOF(t)
 | 
						|
	f.write(t, line1)
 | 
						|
	f.read(t, line1)
 | 
						|
	f.readEOF(t)
 | 
						|
	f.close(t)
 | 
						|
}
 | 
						|
 | 
						|
func TestRecover(t *testing.T) {
 | 
						|
	t.Run("empty", func(t *testing.T) {
 | 
						|
		filePrefix := t.TempDir()
 | 
						|
		f := newFilchTest(t, filePrefix, Options{ReplaceStderr: false})
 | 
						|
		f.write(t, "hello")
 | 
						|
		f.read(t, "hello")
 | 
						|
		f.readEOF(t)
 | 
						|
		f.close(t)
 | 
						|
 | 
						|
		f = newFilchTest(t, filePrefix, Options{ReplaceStderr: false})
 | 
						|
		f.readEOF(t)
 | 
						|
		f.close(t)
 | 
						|
	})
 | 
						|
 | 
						|
	t.Run("cur", func(t *testing.T) {
 | 
						|
		filePrefix := t.TempDir()
 | 
						|
		f := newFilchTest(t, filePrefix, Options{ReplaceStderr: false})
 | 
						|
		f.write(t, "hello")
 | 
						|
		f.close(t)
 | 
						|
 | 
						|
		f = newFilchTest(t, filePrefix, Options{ReplaceStderr: false})
 | 
						|
		f.read(t, "hello")
 | 
						|
		f.readEOF(t)
 | 
						|
		f.close(t)
 | 
						|
	})
 | 
						|
 | 
						|
	t.Run("alt", func(t *testing.T) {
 | 
						|
		t.Skip("currently broken on linux, passes on macOS")
 | 
						|
		/* --- FAIL: TestRecover/alt (0.00s)
 | 
						|
		filch_test.go:128: r.ReadLine()="world", want "hello"
 | 
						|
		filch_test.go:129: r.ReadLine()="hello", want "world"
 | 
						|
		*/
 | 
						|
 | 
						|
		filePrefix := t.TempDir()
 | 
						|
		f := newFilchTest(t, filePrefix, Options{ReplaceStderr: false})
 | 
						|
		f.write(t, "hello")
 | 
						|
		f.read(t, "hello")
 | 
						|
		f.write(t, "world")
 | 
						|
		f.close(t)
 | 
						|
 | 
						|
		f = newFilchTest(t, filePrefix, Options{ReplaceStderr: false})
 | 
						|
		// TODO(crawshaw): The "hello" log is replayed in recovery.
 | 
						|
		//                 We could reduce replays by risking some logs loss.
 | 
						|
		//                 What should our policy here be?
 | 
						|
		f.read(t, "hello")
 | 
						|
		f.read(t, "world")
 | 
						|
		f.readEOF(t)
 | 
						|
		f.close(t)
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestFilchStderr(t *testing.T) {
 | 
						|
	if runtime.GOOS == "windows" {
 | 
						|
		// TODO(bradfitz): this is broken on Windows but not
 | 
						|
		// fully sure why. Investigate.  But notably, the
 | 
						|
		// stderrFD variable (defined in filch.go) and set
 | 
						|
		// below is only ever read in filch_unix.go. So just
 | 
						|
		// skip this for test for now.
 | 
						|
		t.Skip("test broken on Windows")
 | 
						|
	}
 | 
						|
	pipeR, pipeW, err := os.Pipe()
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	defer pipeR.Close()
 | 
						|
	defer pipeW.Close()
 | 
						|
 | 
						|
	tstest.Replace(t, &stderrFD, int(pipeW.Fd()))
 | 
						|
 | 
						|
	filePrefix := t.TempDir()
 | 
						|
	f := newFilchTest(t, filePrefix, Options{ReplaceStderr: true})
 | 
						|
	f.write(t, "hello")
 | 
						|
	if _, err := fmt.Fprintf(pipeW, "filch\n"); err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	f.read(t, "hello")
 | 
						|
	f.read(t, "filch")
 | 
						|
	f.readEOF(t)
 | 
						|
	f.close(t)
 | 
						|
 | 
						|
	pipeW.Close()
 | 
						|
	b, err := io.ReadAll(pipeR)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	if len(b) > 0 {
 | 
						|
		t.Errorf("unexpected write to fake stderr: %s", b)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestSizeOf(t *testing.T) {
 | 
						|
	s := unsafe.Sizeof(Filch{})
 | 
						|
	if s > 4096 {
 | 
						|
		t.Fatalf("Filch{} has size %d on %v, decrease size of buf field", s, runtime.GOARCH)
 | 
						|
	}
 | 
						|
}
 |