1// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package logger
16
17import (
18	"bytes"
19	"fmt"
20	"io"
21	"io/ioutil"
22	"os"
23	"os/exec"
24	"path/filepath"
25	"reflect"
26	"sort"
27	"syscall"
28	"testing"
29)
30
31func TestCreateFileWithRotation(t *testing.T) {
32	dir, err := ioutil.TempDir("", "test-rotation")
33	if err != nil {
34		t.Fatalf("Failed to get TempDir: %v", err)
35	}
36	defer os.RemoveAll(dir)
37
38	file := filepath.Join(dir, "build.log")
39
40	writeFile := func(name string, data string) {
41		f, err := CreateFileWithRotation(name, 3)
42		if err != nil {
43			t.Fatalf("Failed to create file: %v", err)
44		}
45		if n, err := io.WriteString(f, data); err == nil && n < len(data) {
46			t.Fatalf("Short write")
47		} else if err != nil {
48			t.Fatalf("Failed to write: %v", err)
49		}
50		if err := f.Close(); err != nil {
51			t.Fatalf("Failed to close: %v", err)
52		}
53	}
54
55	writeFile(file, "a")
56	writeFile(file, "b")
57	writeFile(file, "c")
58	writeFile(file, "d")
59	writeFile(file, "e")
60
61	d, err := os.Open(dir)
62	if err != nil {
63		t.Fatalf("Failed to open dir: %v", err)
64	}
65	names, err := d.Readdirnames(0)
66	if err != nil {
67		t.Fatalf("Failed to read dir: %v", err)
68	}
69	sort.Strings(names)
70	expected := []string{".lock_build.log", "build.1.log", "build.2.log", "build.3.log", "build.log"}
71	if !reflect.DeepEqual(names, expected) {
72		t.Errorf("File list does not match.")
73		t.Errorf("     got: %v", names)
74		t.Errorf("expected: %v", expected)
75		t.FailNow()
76	}
77
78	expectFileContents := func(name, expected string) {
79		data, err := ioutil.ReadFile(filepath.Join(dir, name))
80		if err != nil {
81			t.Errorf("Error reading file: %v", err)
82			return
83		}
84		str := string(data)
85		if str != expected {
86			t.Errorf("Contents of %v does not match.", name)
87			t.Errorf("     got: %v", data)
88			t.Errorf("expected: %v", expected)
89		}
90	}
91
92	expectFileContents("build.log", "e")
93	expectFileContents("build.1.log", "d")
94	expectFileContents("build.2.log", "c")
95	expectFileContents("build.3.log", "b")
96}
97
98func TestPanic(t *testing.T) {
99	if os.Getenv("ACTUALLY_PANIC") == "1" {
100		panicValue := "foo"
101		log := New(&bytes.Buffer{})
102
103		defer func() {
104			p := recover()
105
106			if p == panicValue {
107				os.Exit(42)
108			} else {
109				fmt.Fprintf(os.Stderr, "Expected %q, got %v\n", panicValue, p)
110				os.Exit(3)
111			}
112		}()
113		defer log.Cleanup()
114
115		log.Panic(panicValue)
116		os.Exit(2)
117		return
118	}
119
120	// Run this in an external process so that we don't pollute stderr
121	cmd := exec.Command(os.Args[0], "-test.run=TestPanic")
122	cmd.Env = append(os.Environ(), "ACTUALLY_PANIC=1")
123	err := cmd.Run()
124	if e, ok := err.(*exec.ExitError); ok && e.Sys().(syscall.WaitStatus).ExitStatus() == 42 {
125		return
126	}
127	t.Errorf("Expected process to exit with status 42, got %v", err)
128}
129
130func TestFatal(t *testing.T) {
131	if os.Getenv("ACTUALLY_FATAL") == "1" {
132		log := New(&bytes.Buffer{})
133		defer func() {
134			// Shouldn't get here
135			os.Exit(3)
136		}()
137		defer log.Cleanup()
138		log.Fatal("Test")
139		os.Exit(0)
140		return
141	}
142
143	cmd := exec.Command(os.Args[0], "-test.run=TestFatal")
144	cmd.Env = append(os.Environ(), "ACTUALLY_FATAL=1")
145	err := cmd.Run()
146	if e, ok := err.(*exec.ExitError); ok && e.Sys().(syscall.WaitStatus).ExitStatus() == 1 {
147		return
148	}
149	t.Errorf("Expected process to exit with status 1, got %v", err)
150}
151
152func TestNonFatal(t *testing.T) {
153	if os.Getenv("ACTUAL_TEST") == "1" {
154		log := New(&bytes.Buffer{})
155		defer log.Cleanup()
156		log.Println("Test")
157		return
158	}
159
160	cmd := exec.Command(os.Args[0], "-test.run=TestNonFatal")
161	cmd.Env = append(os.Environ(), "ACTUAL_TEST=1")
162	err := cmd.Run()
163	if e, ok := err.(*exec.ExitError); ok || (ok && !e.Success()) {
164		t.Errorf("Expected process to exit cleanly, got %v", err)
165	}
166}
167
168func TestRecoverFatal(t *testing.T) {
169	log := New(&bytes.Buffer{})
170	defer func() {
171		if p := recover(); p != nil {
172			t.Errorf("Unexpected panic: %#v", p)
173		}
174	}()
175	defer Recover(func(err error) {
176		if err.Error() != "Test" {
177			t.Errorf("Expected %q, but got %q", "Test", err.Error())
178		}
179	})
180	log.Fatal("Test")
181	t.Errorf("Should not get here")
182}
183
184func TestRecoverNonFatal(t *testing.T) {
185	log := New(&bytes.Buffer{})
186	defer func() {
187		if p := recover(); p == nil {
188			t.Errorf("Panic not thrown")
189		} else if p != "Test" {
190			t.Errorf("Expected %q, but got %#v", "Test", p)
191		}
192	}()
193	defer Recover(func(err error) {
194		t.Errorf("Recover function should not be called")
195	})
196	log.Panic("Test")
197	t.Errorf("Should not get here")
198}
199
200func TestRuntimePanic(t *testing.T) {
201	defer func() {
202		if p := recover(); p == nil {
203			t.Errorf("Panic not thrown")
204		}
205	}()
206	defer Recover(func(err error) {
207		t.Errorf("Recover function should not be called")
208	})
209	var i *int
210	*i = 0
211	t.Errorf("Should not get here")
212}
213