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  }