1package monitor 2 3import ( 4 "fmt" 5 "testing" 6 "time" 7 8 log "github.com/hashicorp/go-hclog" 9 "github.com/stretchr/testify/require" 10) 11 12func TestMonitor_Start(t *testing.T) { 13 require := require.New(t) 14 15 logger := log.NewInterceptLogger(&log.LoggerOptions{ 16 Level: log.Error, 17 }) 18 19 m := New(Config{ 20 BufferSize: 512, 21 Logger: logger, 22 LoggerOptions: &log.LoggerOptions{ 23 Level: log.Debug}, 24 }) 25 26 logCh := m.Start() 27 defer m.Stop() 28 29 logger.Debug("test log") 30 31 for { 32 select { 33 case log := <-logCh: 34 require.Contains(string(log), "[DEBUG] test log") 35 return 36 case <-time.After(3 * time.Second): 37 t.Fatal("Expected to receive from log channel") 38 } 39 } 40} 41 42func TestMonitor_Stop(t *testing.T) { 43 if testing.Short() { 44 t.Skip("too slow for testing.Short") 45 } 46 47 require := require.New(t) 48 49 logger := log.NewInterceptLogger(&log.LoggerOptions{ 50 Level: log.Error, 51 }) 52 53 m := New(Config{ 54 BufferSize: 512, 55 Logger: logger, 56 LoggerOptions: &log.LoggerOptions{ 57 Level: log.Debug, 58 }, 59 }) 60 logCh := m.Start() 61 logger.Debug("test log") 62 63 require.Eventually(func() bool { 64 return len(logCh) == 1 65 }, 3*time.Second, 100*time.Millisecond, "expected logCh to have 1 log in it") 66 67 m.Stop() 68 69 // This log line should not be output to the log channel 70 logger.Debug("After Stop") 71 72 for { 73 select { 74 case log := <-logCh: 75 if string(log) != "" { 76 require.Contains(string(log), "[DEBUG] test log") 77 } else { 78 return 79 } 80 case <-time.After(3 * time.Second): 81 t.Fatal("Expected to receive from log channel") 82 } 83 } 84} 85 86func TestMonitor_DroppedMessages(t *testing.T) { 87 if testing.Short() { 88 t.Skip("too slow for testing.Short") 89 } 90 91 require := require.New(t) 92 93 logger := log.NewInterceptLogger(&log.LoggerOptions{ 94 Level: log.Warn, 95 }) 96 97 mcfg := Config{ 98 BufferSize: 5, 99 Logger: logger, 100 LoggerOptions: &log.LoggerOptions{ 101 Level: log.Debug, 102 }, 103 } 104 m := New(mcfg) 105 106 doneCh := make(chan struct{}) 107 defer close(doneCh) 108 109 logCh := m.Start() 110 111 // Overflow the configured buffer size before we attempt to receive from the 112 // logCh. We choose (2*bufSize+1) to account for: 113 // - buffer size of internal write channel 114 // - buffer size of channel returned from Start() 115 // - A message guaranteed to be dropped because all buffers are full 116 for i := 0; i <= 2*mcfg.BufferSize+1; i++ { 117 logger.Debug(fmt.Sprintf("test message %d", i)) 118 } 119 120 // Make sure we do not stop before the goroutines have time to process. 121 require.Eventually(func() bool { 122 return len(logCh) == mcfg.BufferSize 123 }, 3*time.Second, 100*time.Millisecond, "expected logCh to have a full log buffer") 124 125 dropped := m.Stop() 126 127 // The number of dropped messages is non-deterministic, so we only assert 128 // that we dropped at least 1. 129 require.GreaterOrEqual(dropped, 1) 130} 131 132func TestMonitor_ZeroBufSizeDefault(t *testing.T) { 133 if testing.Short() { 134 t.Skip("too slow for testing.Short") 135 } 136 137 require := require.New(t) 138 139 logger := log.NewInterceptLogger(&log.LoggerOptions{ 140 Level: log.Error, 141 }) 142 143 m := New(Config{ 144 BufferSize: 0, 145 Logger: logger, 146 LoggerOptions: &log.LoggerOptions{ 147 Level: log.Debug, 148 }, 149 }) 150 logCh := m.Start() 151 defer m.Stop() 152 153 logger.Debug("test log") 154 155 // If we do not default the buffer size, the monitor will be unable to buffer 156 // a log line. 157 require.Eventually(func() bool { 158 return len(logCh) == 1 159 }, 3*time.Second, 100*time.Millisecond, "expected logCh to have 1 log buffered") 160 161 for { 162 select { 163 case log := <-logCh: 164 require.Contains(string(log), "[DEBUG] test log") 165 return 166 case <-time.After(3 * time.Second): 167 t.Fatal("Expected to receive from log channel") 168 } 169 } 170} 171 172func TestMonitor_WriteStopped(t *testing.T) { 173 require := require.New(t) 174 175 logger := log.NewInterceptLogger(&log.LoggerOptions{ 176 Level: log.Error, 177 }) 178 179 mwriter := &monitor{ 180 logger: logger, 181 doneCh: make(chan struct{}, 1), 182 } 183 184 mwriter.Stop() 185 n, err := mwriter.Write([]byte("write after close")) 186 require.Equal(n, 0) 187 require.EqualError(err, "monitor stopped") 188} 189