github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/spyglass/lenses/lenses_test.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package lenses 18 19 import ( 20 "bytes" 21 "context" 22 "io/ioutil" 23 "testing" 24 25 "github.com/sirupsen/logrus" 26 ) 27 28 type FakeArtifact struct { 29 path string 30 content []byte 31 sizeLimit int64 32 } 33 34 func (fa *FakeArtifact) JobPath() string { 35 return fa.path 36 } 37 38 func (fa *FakeArtifact) Size() (int64, error) { 39 return int64(len(fa.content)), nil 40 } 41 42 func (fa *FakeArtifact) CanonicalLink() string { 43 return "linknotfound.io/404" 44 } 45 46 func (fa *FakeArtifact) ReadAt(b []byte, off int64) (int, error) { 47 r := bytes.NewReader(fa.content) 48 return r.ReadAt(b, off) 49 } 50 51 func (fa *FakeArtifact) ReadAll() ([]byte, error) { 52 size, err := fa.Size() 53 if err != nil { 54 return nil, err 55 } 56 if size > fa.sizeLimit { 57 return nil, ErrFileTooLarge 58 } 59 r := bytes.NewReader(fa.content) 60 return ioutil.ReadAll(r) 61 } 62 63 func (fa *FakeArtifact) ReadTail(n int64) ([]byte, error) { 64 size, err := fa.Size() 65 if err != nil { 66 return nil, err 67 } 68 buf := make([]byte, n) 69 _, err = fa.ReadAt(buf, size-n) 70 return buf, err 71 } 72 73 func (fa *FakeArtifact) UseContext(ctx context.Context) error { 74 return nil 75 } 76 77 func (fa *FakeArtifact) ReadAtMost(n int64) ([]byte, error) { 78 buf := make([]byte, n) 79 _, err := fa.ReadAt(buf, 0) 80 return buf, err 81 } 82 83 type dumpLens struct{} 84 85 func (dumpLens) Name() string { 86 return "dump" 87 } 88 89 func (dumpLens) Title() string { 90 return "Dump View" 91 } 92 93 func (dumpLens) Priority() int { 94 return 1 95 } 96 97 func (dumpLens) Header(artifacts []Artifact, resourceDir string) string { 98 return "" 99 } 100 101 func (dumpLens) Body(artifacts []Artifact, resourceDir, data string) string { 102 var view []byte 103 for _, a := range artifacts { 104 data, err := a.ReadAll() 105 if err != nil { 106 logrus.WithError(err).Error("Error reading artifact") 107 continue 108 } 109 view = append(view, data...) 110 } 111 return string(view) 112 } 113 114 func (dumpLens) Callback(artifacts []Artifact, resourceDir, data string) string { 115 return "" 116 } 117 118 // Tests getting a view from a viewer 119 func TestView(t *testing.T) { 120 err := RegisterLens(dumpLens{}) 121 if err != nil { 122 t.Fatal("Failed to register viewer for testing View") 123 } 124 fakeLog := &FakeArtifact{ 125 path: "log.txt", 126 content: []byte("Oh wow\nlogs\nthis is\ncrazy"), 127 sizeLimit: 500e6, 128 } 129 testCases := []struct { 130 name string 131 lensName string 132 artifacts []Artifact 133 raw string 134 expected string 135 err error 136 }{ 137 { 138 name: "simple view", 139 lensName: "dump", 140 artifacts: []Artifact{ 141 fakeLog, fakeLog, 142 }, 143 raw: "", 144 expected: `Oh wow 145 logs 146 this is 147 crazyOh wow 148 logs 149 this is 150 crazy`, 151 err: nil, 152 }, 153 { 154 name: "fail on unregistered view name", 155 lensName: "MicroverseBattery", 156 artifacts: []Artifact{}, 157 raw: "", 158 expected: "", 159 err: ErrInvalidLensName, 160 }, 161 } 162 for _, tc := range testCases { 163 lens, err := GetLens(tc.lensName) 164 if tc.err != err { 165 t.Errorf("%s expected error %v but got error %v", tc.name, tc.err, err) 166 continue 167 } 168 if tc.err == nil && lens == nil { 169 t.Fatalf("Expected lens %s but got nil.", tc.lensName) 170 } 171 if lens != nil && lens.Body(tc.artifacts, "", tc.raw) != tc.expected { 172 t.Errorf("%s expected view to be %s but got %s", tc.name, tc.expected, lens) 173 } 174 } 175 UnregisterLens("DumpView") 176 177 } 178 179 // Tests reading last N Lines from files in GCS 180 func TestLastNLines_GCS(t *testing.T) { 181 fakeGCSServerChunkSize := int64(3500) 182 var longLog string 183 for i := 0; i < 300; i++ { 184 longLog += "here a log\nthere a log\neverywhere a log log\n" 185 } 186 testCases := []struct { 187 name string 188 path string 189 contents []byte 190 n int64 191 a Artifact 192 expected []string 193 }{ 194 { 195 name: "Read last 2 lines of a 4-line file", 196 n: 2, 197 path: "log.txt", 198 contents: []byte("Oh wow\nlogs\nthis is\ncrazy"), 199 expected: []string{"this is", "crazy"}, 200 }, 201 { 202 name: "Read last 5 lines of a 4-line file", 203 n: 5, 204 path: "log.txt", 205 contents: []byte("Oh wow\nlogs\nthis is\ncrazy"), 206 expected: []string{"Oh wow", "logs", "this is", "crazy"}, 207 }, 208 { 209 name: "Read last 2 lines of a long log file", 210 n: 2, 211 path: "long-log.txt", 212 contents: []byte(longLog), 213 expected: []string{ 214 "there a log", 215 "everywhere a log log", 216 }, 217 }, 218 } 219 for _, tc := range testCases { 220 t.Run(tc.name, func(t *testing.T) { 221 artifact := &FakeArtifact{ 222 path: tc.path, 223 content: tc.contents, 224 sizeLimit: 500e6, 225 } 226 actual, err := LastNLinesChunked(artifact, tc.n, fakeGCSServerChunkSize) 227 if err != nil { 228 t.Fatalf("failed with error: %v", err) 229 } 230 if len(actual) != len(tc.expected) { 231 t.Fatalf("Expected length:\n%d\nActual length:\n%d", len(tc.expected), len(actual)) 232 } 233 for ix, line := range tc.expected { 234 if line != actual[ix] { 235 t.Errorf("Line %d expected:\n%s\nActual line %d:\n%s", ix, line, ix, actual[ix]) 236 break 237 } 238 } 239 for ix, line := range actual { 240 if line != tc.expected[ix] { 241 t.Errorf("Line %d expected:\n%s\nActual line %d:\n%s", ix, tc.expected[ix], ix, line) 242 break 243 } 244 } 245 }) 246 } 247 }