| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- // Copyright (C) 2017 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- package main
- import (
- "io"
- "os"
- "path/filepath"
- "testing"
- "time"
- )
- func TestRotatedFile(t *testing.T) {
- // Verify that log rotation happens.
- dir := t.TempDir()
- open := func(name string) (io.WriteCloser, error) {
- f, err := os.Create(name)
- t.Cleanup(func() {
- if f != nil {
- _ = f.Close()
- }
- })
- return f, err
- }
- logName := filepath.Join(dir, "log.txt")
- testData := []byte("12345678\n")
- maxSize := int64(len(testData) + len(testData)/2)
- // We allow the log file plus two rotated copies.
- rf, err := newRotatedFile(logName, open, maxSize, 2)
- if err != nil {
- t.Fatal(err)
- }
- // Write some bytes.
- if _, err := rf.Write(testData); err != nil {
- t.Fatal(err)
- }
- // They should be in the log.
- checkSize(t, logName, len(testData))
- checkNotExist(t, logName+".0")
- // Write some more bytes. We should rotate and write into a new file as the
- // new bytes don't fit.
- if _, err := rf.Write(testData); err != nil {
- t.Fatal(err)
- }
- checkSize(t, logName, len(testData))
- checkSize(t, numberedFile(logName, 0), len(testData))
- checkNotExist(t, logName+".1")
- // Write another byte. That should fit without causing an extra rotate.
- _, _ = rf.Write([]byte{42})
- checkSize(t, logName, len(testData)+1)
- checkSize(t, numberedFile(logName, 0), len(testData))
- checkNotExist(t, numberedFile(logName, 1))
- // Write some more bytes. We should rotate and write into a new file as the
- // new bytes don't fit.
- if _, err := rf.Write(testData); err != nil {
- t.Fatal(err)
- }
- checkSize(t, logName, len(testData))
- checkSize(t, numberedFile(logName, 0), len(testData)+1) // the one we wrote extra to, now rotated
- checkSize(t, numberedFile(logName, 1), len(testData))
- checkNotExist(t, numberedFile(logName, 2))
- // Write some more bytes. We should rotate and write into a new file as the
- // new bytes don't fit.
- if _, err := rf.Write(testData); err != nil {
- t.Fatal(err)
- }
- checkSize(t, logName, len(testData))
- checkSize(t, numberedFile(logName, 0), len(testData))
- checkSize(t, numberedFile(logName, 1), len(testData)+1)
- checkNotExist(t, numberedFile(logName, 2)) // exceeds maxFiles so deleted
- }
- func TestNumberedFile(t *testing.T) {
- // Mostly just illustrates where the number ends up and makes sure it
- // doesn't crash without an extension.
- cases := []struct {
- in string
- num int
- out string
- }{
- {
- in: "syncthing.log",
- num: 42,
- out: "syncthing.42.log",
- },
- {
- in: filepath.Join("asdfasdf", "syncthing.log.txt"),
- num: 42,
- out: filepath.Join("asdfasdf", "syncthing.log.42.txt"),
- },
- {
- in: "syncthing-log",
- num: 42,
- out: "syncthing-log.42",
- },
- }
- for _, tc := range cases {
- res := numberedFile(tc.in, tc.num)
- if res != tc.out {
- t.Errorf("numberedFile(%q, %d) => %q, expected %q", tc.in, tc.num, res, tc.out)
- }
- }
- }
- func checkSize(t *testing.T, name string, size int) {
- t.Helper()
- info, err := os.Lstat(name)
- if err != nil {
- t.Fatal(err)
- }
- if info.Size() != int64(size) {
- t.Errorf("%s wrong size: %d != expected %d", name, info.Size(), size)
- }
- }
- func checkNotExist(t *testing.T, name string) {
- t.Helper()
- _, err := os.Lstat(name)
- if !os.IsNotExist(err) {
- t.Errorf("%s should not exist", name)
- }
- }
- func TestAutoClosedFile(t *testing.T) {
- os.RemoveAll("_autoclose")
- defer os.RemoveAll("_autoclose")
- os.Mkdir("_autoclose", 0755)
- file := filepath.FromSlash("_autoclose/tmp")
- data := []byte("hello, world\n")
- // An autoclosed file that closes very quickly
- ac, err := newAutoclosedFile(file, time.Millisecond, time.Millisecond)
- if err != nil {
- t.Fatal(err)
- }
- // Write some data.
- if _, err := ac.Write(data); err != nil {
- t.Fatal(err)
- }
- // Wait for it to close
- start := time.Now()
- for {
- time.Sleep(time.Millisecond)
- ac.mut.Lock()
- fd := ac.fd
- ac.mut.Unlock()
- if fd == nil {
- break
- }
- if time.Since(start) > time.Second {
- t.Fatal("File should have been closed after first write")
- }
- }
- // Write more data, which should be an append.
- if _, err := ac.Write(data); err != nil {
- t.Fatal(err)
- }
- // Close.
- if err := ac.Close(); err != nil {
- t.Fatal(err)
- }
- // The file should have both writes in it.
- bs, err := os.ReadFile(file)
- if err != nil {
- t.Fatal(err)
- }
- if len(bs) != 2*len(data) {
- t.Fatalf("Writes failed, expected %d bytes, not %d", 2*len(data), len(bs))
- }
- // Open the file again.
- ac, err = newAutoclosedFile(file, time.Second, time.Second)
- if err != nil {
- t.Fatal(err)
- }
- // Write something
- if _, err := ac.Write(data); err != nil {
- t.Fatal(err)
- }
- // It should now contain three writes, as the file is always opened for appending
- bs, err = os.ReadFile(file)
- if err != nil {
- t.Fatal(err)
- }
- if len(bs) != 3*len(data) {
- t.Fatalf("Write failed, expected %d bytes, not %d", 3*len(data), len(bs))
- }
- // Close.
- if err := ac.Close(); err != nil {
- t.Fatal(err)
- }
- }
|