github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/pkg/model/logstore/logstore_test.go (about)

     1  package logstore
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"runtime"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  
    13  	"github.com/tilt-dev/tilt/pkg/logger"
    14  	"github.com/tilt-dev/tilt/pkg/model"
    15  )
    16  
    17  // NOTE(dmiller): set at runtime with:
    18  // go test -ldflags="-X 'github.com/tilt-dev/tilt/pkg/model/logstore.LogstoreWriteGoldenMaster=1'" ./pkg/model/logstore
    19  var LogstoreWriteGoldenMaster = "0"
    20  
    21  func TestLog_AppendUnderLimit(t *testing.T) {
    22  	l := NewLogStore()
    23  	l.Append(newGlobalTestLogEvent("foo"), nil)
    24  	l.Append(newGlobalTestLogEvent("bar"), nil)
    25  	assert.Equal(t, "foobar", l.String())
    26  }
    27  
    28  func TestAppendDifferentLevels(t *testing.T) {
    29  	l := NewLogStore()
    30  	l.Append(newGlobalLevelTestLogEvent("foo", logger.InfoLvl), nil)
    31  	l.Append(newGlobalLevelTestLogEvent("bar", logger.DebugLvl), nil)
    32  	l.Append(newGlobalLevelTestLogEvent("baz", logger.InfoLvl), nil)
    33  	assert.Equal(t, "foo\nbar\nbaz", l.String())
    34  }
    35  
    36  func TestAppendDifferentLevelsMultiLines(t *testing.T) {
    37  	l := NewLogStore()
    38  	l.Append(newGlobalTestLogEvent("hello ... "), nil)
    39  	l.Append(newGlobalLevelTestLogEvent("foobar", logger.DebugLvl), nil)
    40  	l.Append(newGlobalTestLogEvent("world\nnext line of global log"), nil)
    41  	assert.Equal(t, "hello ... \nfoobar\nworld\nnext line of global log", l.String())
    42  
    43  	l.recomputeDerivedValues()
    44  	assert.Equal(t, "hello ... \nfoobar\nworld\nnext line of global log", l.String())
    45  }
    46  
    47  func TestLog_AppendOverLimit(t *testing.T) {
    48  	l := NewLogStore()
    49  	l.maxLogLengthInBytes = 32
    50  
    51  	l.Append(newGlobalTestLogEvent("hello\n"), nil)
    52  	sb := strings.Builder{}
    53  	for i := 0; i < l.maxLogLengthInBytes/2; i++ {
    54  		_, err := sb.WriteString("x\n")
    55  		if err != nil {
    56  			t.Fatalf("error in %T.WriteString: %+v", sb, err)
    57  		}
    58  	}
    59  
    60  	s := sb.String()
    61  	l.Append(newGlobalTestLogEvent(s), nil)
    62  	assert.Equal(t, "x\nx\nx\nx\nx\nx\nx\nx\n", l.String())
    63  }
    64  
    65  func TestLog_TruncateChattySpansFirst(t *testing.T) {
    66  	l := NewLogStore()
    67  	l.maxLogLengthInBytes = 100
    68  
    69  	l.Append(newTestLogEvent("(tiltfile)", time.Now(), "Tiltfile Success"), nil)
    70  	for i := 0; i < 40; i++ {
    71  		l.Append(newTestLogEvent("noisy", time.Now(), "Noisy Log\n"), nil)
    72  	}
    73  
    74  	assert.Contains(t, l.String(), "Tiltfile Success")
    75  }
    76  
    77  func TestLog_ChattyServicesDoNotTruncateTests(t *testing.T) {
    78  	if runtime.GOOS == "windows" {
    79  		t.Skip() // Windows has slightly different line-endings which effect this test
    80  	}
    81  	l := NewLogStore()
    82  	l.maxLogLengthInBytes = 500
    83  
    84  	l.Append(newTestLogEvent("(tiltfile)", time.Now(), "Tiltfile Success"), nil)
    85  	for i := 0; i < 100; i++ {
    86  		l.Append(newTestLogEvent(model.ManifestName(fmt.Sprintf("noisy%d", i%5)), time.Now(), "Noisy Log\n"), nil)
    87  	}
    88  
    89  	for i := 0; i < 5; i++ {
    90  		l.Append(newTestLogEvent("test", time.Now(), fmt.Sprintf("test #%d success\n", i)), nil)
    91  	}
    92  
    93  	for i := 0; i < 20; i++ {
    94  		l.Append(newTestLogEvent(model.ManifestName(fmt.Sprintf("noisy%d", i%5)), time.Now(), "Noisy Log\n"), nil)
    95  	}
    96  
    97  	assert.Contains(t, l.String(), "Tiltfile Success")
    98  	assert.Contains(t, l.String(), "test #0 success")
    99  }
   100  
   101  func TestLogPrefix(t *testing.T) {
   102  	l := NewLogStore()
   103  	l.Append(newGlobalTestLogEvent("hello\n"), nil)
   104  	l.Append(newTestLogEvent("prefix", time.Now(), "bar\nbaz\n"), nil)
   105  
   106  	assertSnapshot(t, l.String())
   107  }
   108  
   109  // Assert that when logs come from two different sources, they get interleaved correctly.
   110  func TestLogInterleaving(t *testing.T) {
   111  	l := NewLogStore()
   112  	l.Append(newGlobalTestLogEvent("hello ... "), nil)
   113  	l.Append(newTestLogEvent("prefix", time.Now(), "START LONG MESSAGE\ngoodbye ... "), nil)
   114  	l.Append(newGlobalTestLogEvent("world\nnext line of global log"), nil)
   115  	l.Append(newTestLogEvent("prefix", time.Now(), "world\nEND LONG MESSAGE"), nil)
   116  
   117  	assertSnapshot(t, l.String())
   118  }
   119  
   120  func TestScrubSecret(t *testing.T) {
   121  	l := NewLogStore()
   122  	secretSet := model.SecretSet{}
   123  	secretSet.AddSecret("my-secret", "client-id", []byte("secret"))
   124  	l.Append(newGlobalTestLogEvent("hello\nsecret-time!\nc2VjcmV0-time!\ngoodbye"), secretSet)
   125  	assert.Equal(t, `hello
   126  [redacted secret my-secret:client-id]-time!
   127  [redacted secret my-secret:client-id]-time!
   128  goodbye`, l.String())
   129  }
   130  
   131  func TestLogTail(t *testing.T) {
   132  	l := NewLogStore()
   133  	l.Append(newGlobalTestLogEvent("1\n2\n3\n4\n5\n"), nil)
   134  	assert.Equal(t, "", l.Tail(0))
   135  	assert.Equal(t, "5\n", l.Tail(1))
   136  	assert.Equal(t, "4\n5\n", l.Tail(2))
   137  	assert.Equal(t, "3\n4\n5\n", l.Tail(3))
   138  	assert.Equal(t, "2\n3\n4\n5\n", l.Tail(4))
   139  	assert.Equal(t, "1\n2\n3\n4\n5\n", l.Tail(5))
   140  	assert.Equal(t, "1\n2\n3\n4\n5\n", l.Tail(6))
   141  }
   142  
   143  func TestLogTailPrefixes(t *testing.T) {
   144  	l := NewLogStore()
   145  	l.Append(newGlobalTestLogEvent("1\n2\n"), nil)
   146  	l.Append(newTestLogEvent("fe", time.Now(), "3\n4\n"), nil)
   147  	l.Append(newGlobalTestLogEvent("5\n"), nil)
   148  	assert.Equal(t, "", l.Tail(0))
   149  	assert.Equal(t, "5\n", l.Tail(1))
   150  	assert.Equal(t, "           fe │ 4\n5\n", l.Tail(2))
   151  	assert.Equal(t, "           fe │ 3\n           fe │ 4\n5\n", l.Tail(3))
   152  	assert.Equal(t, "2\n           fe │ 3\n           fe │ 4\n5\n", l.Tail(4))
   153  	assert.Equal(t, "1\n2\n           fe │ 3\n           fe │ 4\n5\n", l.Tail(5))
   154  	assert.Equal(t, "1\n2\n           fe │ 3\n           fe │ 4\n5\n", l.Tail(6))
   155  }
   156  
   157  func TestLogTailSpan(t *testing.T) {
   158  	l := NewLogStore()
   159  	l.Append(newGlobalTestLogEvent("1\n2\n"), nil)
   160  	l.Append(newTestLogEvent("fe", time.Now(), "3\n4\n"), nil)
   161  	l.Append(newGlobalTestLogEvent("5\n"), nil)
   162  	assert.Equal(t, "5\n", l.TailSpan(1, ""))
   163  	assert.Equal(t, "2\n5\n", l.TailSpan(2, ""))
   164  	assert.Equal(t, "1\n2\n5\n", l.TailSpan(3, ""))
   165  	assert.Equal(t, "4\n", l.TailSpan(1, "fe"))
   166  	assert.Equal(t, "3\n4\n", l.TailSpan(2, "fe"))
   167  	assert.Equal(t, "3\n4\n", l.TailSpan(3, "fe"))
   168  	assert.Equal(t, "3\n4\n", l.TailSpan(30, "fe"))
   169  }
   170  
   171  func TestLogTailParts(t *testing.T) {
   172  	l := NewLogStore()
   173  	l.Append(newGlobalTestLogEvent("a"), nil)
   174  	l.Append(newTestLogEvent("fe", time.Now(), "xy"), nil)
   175  	l.Append(newGlobalTestLogEvent("bc\n"), nil)
   176  	l.Append(newTestLogEvent("fe", time.Now(), "z\n"), nil)
   177  	assert.Equal(t, "           fe │ xyz\n", l.Tail(1))
   178  	assert.Equal(t, "abc\n           fe │ xyz\n", l.Tail(2))
   179  }
   180  
   181  func TestContinuingString(t *testing.T) {
   182  	l := NewLogStore()
   183  
   184  	c1 := l.Checkpoint()
   185  	assert.Equal(t, "", l.ContinuingString(c1))
   186  
   187  	l.Append(newGlobalTestLogEvent("foo"), nil)
   188  	c2 := l.Checkpoint()
   189  	assert.Equal(t, "foo", l.ContinuingString(c1))
   190  
   191  	l.Append(newGlobalTestLogEvent("bar\n"), nil)
   192  	assert.Equal(t, "foobar\n", l.ContinuingString(c1))
   193  	assert.Equal(t, "bar\n", l.ContinuingString(c2))
   194  }
   195  
   196  func TestContinuingStringOneSource(t *testing.T) {
   197  	l := NewLogStore()
   198  
   199  	c1 := l.Checkpoint()
   200  	assert.Equal(t, "", l.ContinuingString(c1))
   201  
   202  	l.Append(newTestLogEvent("fe", time.Now(), "foo"), nil)
   203  	c2 := l.Checkpoint()
   204  	assert.Equal(t, "           fe │ foo", l.ContinuingString(c1))
   205  
   206  	l.Append(newTestLogEvent("fe", time.Now(), "bar\n"), nil)
   207  	assert.Equal(t, "           fe │ foobar\n", l.ContinuingString(c1))
   208  	assert.Equal(t, "bar\n", l.ContinuingString(c2))
   209  }
   210  
   211  func TestContinuingStringTwoSources(t *testing.T) {
   212  	l := NewLogStore()
   213  
   214  	c1 := l.Checkpoint()
   215  
   216  	l.Append(newGlobalTestLogEvent("a"), nil)
   217  	c2 := l.Checkpoint()
   218  	assert.Equal(t, "a", l.ContinuingString(c1))
   219  
   220  	l.Append(newTestLogEvent("fe", time.Now(), "xy"), nil)
   221  	c3 := l.Checkpoint()
   222  	assert.Equal(t, "a\n           fe │ xy", l.ContinuingString(c1))
   223  	assert.Equal(t, "\n           fe │ xy", l.ContinuingString(c2))
   224  
   225  	l.Append(newGlobalTestLogEvent("bc\n"), nil)
   226  	c4 := l.Checkpoint()
   227  	assert.Equal(t, "abc\n           fe │ xy", l.ContinuingString(c1))
   228  	assert.Equal(t, "\n           fe │ xy\nbc\n", l.ContinuingString(c2))
   229  	assert.Equal(t, "\nbc\n", l.ContinuingString(c3))
   230  
   231  	l.Append(newTestLogEvent("fe", time.Now(), "z\n"), nil)
   232  	assert.Equal(t, "abc\n           fe │ xyz\n", l.ContinuingString(c1))
   233  	assert.Equal(t, "\n           fe │ xyz\nbc\n", l.ContinuingString(c2))
   234  	assert.Equal(t, "\nbc\n           fe │ z\n", l.ContinuingString(c3))
   235  	assert.Equal(t, "           fe │ z\n", l.ContinuingString(c4))
   236  }
   237  
   238  func TestContinuingStringNextSpanFiltered(t *testing.T) {
   239  	l := NewLogStore()
   240  	opts := lineOptionsWithManifests("foo")
   241  
   242  	c1 := l.Checkpoint()
   243  	l.Append(newTestLogEvent("foo", time.Now(), "hello "), nil)
   244  	assert.Equal(t, "          foo │ hello ", l.ContinuingStringWithOptions(c1, opts))
   245  
   246  	c2 := l.Checkpoint()
   247  	l.Append(newTestLogEvent("bar", time.Now(), "INTERRUPTING COW!\n"), nil)
   248  	l.Append(newTestLogEvent("foo", time.Now(), "world\n"), nil)
   249  	assert.Equal(t, "          foo │ hello world\n", l.ContinuingStringWithOptions(c1, opts))
   250  	assert.Equal(t, "world\n", l.ContinuingStringWithOptions(c2, opts))
   251  }
   252  
   253  func TestContinuingStringProceedingSpanFiltered(t *testing.T) {
   254  	l := NewLogStore()
   255  	opts := lineOptionsWithManifests("foo")
   256  
   257  	c1 := l.Checkpoint()
   258  	l.Append(newTestLogEvent("foo", time.Now(), "hello "), nil)
   259  	l.Append(newTestLogEvent("bar", time.Now(), "INTERRUPTING COW!\n"), nil)
   260  	assert.Equal(t, "          foo │ hello ", l.ContinuingStringWithOptions(c1, opts))
   261  
   262  	c2 := l.Checkpoint()
   263  	l.Append(newTestLogEvent("foo", time.Now(), "world\n"), nil)
   264  	assert.Equal(t, "          foo │ hello world\n", l.ContinuingStringWithOptions(c1, opts))
   265  	assert.Equal(t, "world\n", l.ContinuingStringWithOptions(c2, opts))
   266  }
   267  
   268  func TestContinuingStringProceedingAndNextSpanFiltered(t *testing.T) {
   269  	l := NewLogStore()
   270  	opts := lineOptionsWithManifests("foo")
   271  
   272  	c1 := l.Checkpoint()
   273  	l.Append(newTestLogEvent("foo", time.Now(), "hello "), nil)
   274  	l.Append(newTestLogEvent("bar", time.Now(), "INTERRUPTING COW!\n"), nil)
   275  	assert.Equal(t, "          foo │ hello ", l.ContinuingStringWithOptions(c1, opts))
   276  
   277  	c2 := l.Checkpoint()
   278  	l.Append(newTestLogEvent("bar", time.Now(), "INTERRUPTING COW!\n"), nil)
   279  	l.Append(newTestLogEvent("foo", time.Now(), "world\n"), nil)
   280  	assert.Equal(t, "          foo │ hello world\n", l.ContinuingStringWithOptions(c1, opts))
   281  	assert.Equal(t, "world\n", l.ContinuingStringWithOptions(c2, opts))
   282  }
   283  
   284  func TestContinuingStringAfterLimit(t *testing.T) {
   285  	l := NewLogStore()
   286  	l.maxLogLengthInBytes = 20
   287  
   288  	c1 := l.Checkpoint()
   289  	assert.Equal(t, "", l.ContinuingString(c1))
   290  
   291  	l.Append(newGlobalTestLogEvent("123456789\n"), nil)
   292  	c2 := l.Checkpoint()
   293  	assert.Equal(t, "123456789\n", l.String())
   294  	assert.Equal(t, "123456789\n", l.ContinuingString(c1))
   295  
   296  	l.Append(newGlobalTestLogEvent("abcdefghi\n"), nil)
   297  	c3 := l.Checkpoint()
   298  	assert.Equal(t, "123456789\nabcdefghi\n", l.String())
   299  	assert.Equal(t, "123456789\nabcdefghi\n", l.ContinuingString(c1))
   300  	assert.Equal(t, "abcdefghi\n", l.ContinuingString(c2))
   301  
   302  	l.Append(newGlobalTestLogEvent("jklmnopqr\n"), nil)
   303  	assert.Equal(t, "jklmnopqr\n", l.String())
   304  	assert.Equal(t, "jklmnopqr\n", l.ContinuingString(c1))
   305  	assert.Equal(t, "jklmnopqr\n", l.ContinuingString(c2))
   306  	assert.Equal(t, "jklmnopqr\n", l.ContinuingString(c3))
   307  }
   308  
   309  func TestManifestLog(t *testing.T) {
   310  	l := NewLogStore()
   311  	l.Append(newGlobalTestLogEvent("1\n2\n"), nil)
   312  	l.Append(newTestLogEvent("fe", time.Now(), "3\n4\n"), nil)
   313  	l.Append(newGlobalTestLogEvent("5\n6\n"), nil)
   314  	l.Append(newTestLogEvent("fe", time.Now(), "7\n8\n"), nil)
   315  	l.Append(newTestLogEvent("back", time.Now(), "a\nb\n"), nil)
   316  	l.Append(newGlobalTestLogEvent("5\n6\n"), nil)
   317  	assert.Equal(t, "3\n4\n7\n8\n", l.ManifestLog("fe"))
   318  	assert.Equal(t, "a\nb\n", l.ManifestLog("back"))
   319  }
   320  
   321  func TestManifestLogContinuation(t *testing.T) {
   322  	l := NewLogStore()
   323  	l.Append(newGlobalTestLogEvent("1\n2\n"), nil)
   324  	l.Append(newTestLogEvent("fe", time.Now(), "34"), nil)
   325  	l.Append(newGlobalTestLogEvent("5\n6\n"), nil)
   326  	l.Append(newTestLogEvent("fe", time.Now(), "78"), nil)
   327  	l.Append(newTestLogEvent("back", time.Now(), "ab"), nil)
   328  	l.Append(newGlobalTestLogEvent("5\n6\n"), nil)
   329  	assert.Equal(t, "3478", l.ManifestLog("fe"))
   330  	assert.Equal(t, "ab", l.ManifestLog("back"))
   331  	assert.Equal(t, "1\n2\n           fe │ 3478\n5\n6\n         back │ ab\n5\n6\n", l.String())
   332  }
   333  
   334  func TestLogIncremental(t *testing.T) {
   335  	l := NewLogStore()
   336  	l.Append(newGlobalTestLogEvent("line1\n"), nil)
   337  	l.Append(newGlobalTestLogEvent("line2\n"), nil)
   338  	l.Append(newGlobalTestLogEvent("line3\n"), nil)
   339  
   340  	list, err := l.ToLogList(0)
   341  	assert.NoError(t, err)
   342  	assert.Equal(t, 3, len(list.Segments))
   343  	assert.Equal(t, int32(0), list.FromCheckpoint)
   344  	assert.Equal(t, int32(3), list.ToCheckpoint)
   345  
   346  	list, err = l.ToLogList(1)
   347  	assert.NoError(t, err)
   348  	assert.Equal(t, 2, len(list.Segments))
   349  	assert.Equal(t, int32(1), list.FromCheckpoint)
   350  	assert.Equal(t, int32(3), list.ToCheckpoint)
   351  
   352  	list, err = l.ToLogList(3)
   353  	assert.NoError(t, err)
   354  	assert.Equal(t, 0, len(list.Segments))
   355  	assert.Equal(t, int32(-1), list.FromCheckpoint)
   356  	assert.Equal(t, int32(-1), list.ToCheckpoint)
   357  
   358  	list, err = l.ToLogList(10)
   359  	assert.NoError(t, err)
   360  	assert.Equal(t, 0, len(list.Segments))
   361  	assert.Equal(t, int32(-1), list.FromCheckpoint)
   362  	assert.Equal(t, int32(-1), list.ToCheckpoint)
   363  }
   364  
   365  func TestWarnings(t *testing.T) {
   366  	l := NewLogStore()
   367  	l.Append(testLogEvent{
   368  		name:    "fe",
   369  		level:   logger.WarnLvl,
   370  		message: "Warning 1 line 1\nWarning 1 line 2\nWarning 1 line 3\n",
   371  	}, nil)
   372  	l.Append(testLogEvent{
   373  		name:    "fe",
   374  		level:   logger.WarnLvl,
   375  		message: "Warning 2 line 1\nWarning 2 line 2\n",
   376  	}, nil)
   377  	l.Append(testLogEvent{
   378  		name:    "fe",
   379  		level:   logger.WarnLvl,
   380  		message: "Warning 3 line 1\n",
   381  	}, nil)
   382  	l.Append(testLogEvent{
   383  		name:    "non-fe",
   384  		level:   logger.WarnLvl,
   385  		message: "non-fe warning\n",
   386  	}, nil)
   387  
   388  	warnings := l.Warnings("fe")
   389  	assert.Equal(t, warnings, []string{
   390  		"Warning 1 line 1\nWarning 1 line 2\nWarning 1 line 3\n",
   391  		"Warning 2 line 1\nWarning 2 line 2\n",
   392  		"Warning 3 line 1\n",
   393  	})
   394  
   395  	assertSnapshot(t, l.String())
   396  }
   397  
   398  func TestErrors(t *testing.T) {
   399  	l := NewLogStore()
   400  	l.Append(testLogEvent{
   401  		name:    "fe",
   402  		level:   logger.ErrorLvl,
   403  		message: "Error 1 line 1\nError 1 line 2\nError 1 line 3\n",
   404  	}, nil)
   405  	l.Append(testLogEvent{
   406  		name:    "fe",
   407  		level:   logger.ErrorLvl,
   408  		message: "Error 2 line 1\nError 2 line 2\n",
   409  	}, nil)
   410  	l.Append(testLogEvent{
   411  		name:    "fe",
   412  		level:   logger.ErrorLvl,
   413  		message: "Error 3 line 1\n",
   414  	}, nil)
   415  	l.Append(testLogEvent{
   416  		name:    "non-fe",
   417  		level:   logger.ErrorLvl,
   418  		message: "non-fe warning\n",
   419  	}, nil)
   420  
   421  	assertSnapshot(t, l.String())
   422  }
   423  
   424  func TestContinuingLines(t *testing.T) {
   425  	l := NewLogStore()
   426  	c1 := l.Checkpoint()
   427  
   428  	now := time.Now()
   429  	l.Append(testLogEvent{
   430  		name:    "fe",
   431  		message: "layer 1: pending\n",
   432  		ts:      now,
   433  		fields:  map[string]string{logger.FieldNameProgressID: "layer 1"},
   434  	}, nil)
   435  	l.Append(testLogEvent{
   436  		name:    "fe",
   437  		message: "layer 2: pending\n",
   438  		ts:      now,
   439  		fields:  map[string]string{logger.FieldNameProgressID: "layer 2"},
   440  	}, nil)
   441  	l.Append(testLogEvent{
   442  		name:    "be",
   443  		message: "layer 1: pending\n",
   444  		ts:      now,
   445  		fields:  map[string]string{logger.FieldNameProgressID: "layer 1"},
   446  	}, nil)
   447  
   448  	assert.Equal(t, `           fe │ layer 1: pending
   449             fe │ layer 2: pending
   450             be │ layer 1: pending
   451  `, l.ContinuingString(c1))
   452  
   453  	c2 := l.Checkpoint()
   454  	assert.Equal(t, []LogLine{
   455  		LogLine{Text: "           fe │ layer 1: pending\n", SpanID: "fe", ProgressID: "layer 1", Time: now},
   456  		LogLine{Text: "           fe │ layer 2: pending\n", SpanID: "fe", ProgressID: "layer 2", Time: now},
   457  		LogLine{Text: "           be │ layer 1: pending\n", SpanID: "be", ProgressID: "layer 1", Time: now},
   458  	}, l.ContinuingLines(c1))
   459  
   460  	l.Append(testLogEvent{
   461  		name:    "fe",
   462  		message: "layer 1: done\n",
   463  		ts:      now,
   464  		fields: map[string]string{
   465  			logger.FieldNameProgressID:        "layer 1",
   466  			logger.FieldNameProgressMustPrint: "1",
   467  		},
   468  	}, nil)
   469  
   470  	assert.Equal(t, []LogLine{
   471  		LogLine{
   472  			Text:              "           fe │ layer 1: done\n",
   473  			SpanID:            "fe",
   474  			ProgressID:        "layer 1",
   475  			ProgressMustPrint: true,
   476  			Time:              now,
   477  		},
   478  	}, l.ContinuingLines(c2))
   479  }
   480  
   481  func TestContinuingLinesWithOptionsSuppressPrefix(t *testing.T) {
   482  	l := NewLogStore()
   483  	c1 := l.Checkpoint()
   484  
   485  	now := time.Now()
   486  	l.Append(testLogEvent{
   487  		name:    "fe",
   488  		message: "layer 1: pending\n",
   489  		ts:      now,
   490  		fields:  map[string]string{logger.FieldNameProgressID: "layer 1"},
   491  	}, nil)
   492  	l.Append(testLogEvent{
   493  		name:    "fe",
   494  		message: "layer 2: pending\n",
   495  		ts:      now,
   496  		fields:  map[string]string{logger.FieldNameProgressID: "layer 2"},
   497  	}, nil)
   498  
   499  	assert.Equal(t, []LogLine{
   500  		LogLine{Text: "layer 1: pending\n", SpanID: "fe", ProgressID: "layer 1", Time: now},
   501  		LogLine{Text: "layer 2: pending\n", SpanID: "fe", ProgressID: "layer 2", Time: now},
   502  	}, l.ContinuingLinesWithOptions(c1, LineOptions{SuppressPrefix: true}))
   503  }
   504  
   505  func TestContinuingLinesWithOptionsSpans(t *testing.T) {
   506  	l := NewLogStore()
   507  	c1 := l.Checkpoint()
   508  
   509  	now := time.Now()
   510  	l.Append(testLogEvent{
   511  		name:    "foo",
   512  		message: "layer 1: pending\n",
   513  		ts:      now,
   514  		fields:  map[string]string{logger.FieldNameProgressID: "layer 1"},
   515  	}, nil)
   516  	l.Append(testLogEvent{
   517  		name:    "foo",
   518  		message: "layer 2: pending\n",
   519  		ts:      now,
   520  		fields:  map[string]string{logger.FieldNameProgressID: "layer 2"},
   521  	}, nil)
   522  	l.Append(testLogEvent{
   523  		name:    "bar",
   524  		message: "layer 1: pending\n",
   525  		ts:      now,
   526  		fields:  map[string]string{logger.FieldNameProgressID: "layer 1"},
   527  	}, nil)
   528  
   529  	assert.Equal(t, []LogLine{
   530  		LogLine{Text: "          foo │ layer 1: pending\n", SpanID: "foo", ProgressID: "layer 1", Time: now},
   531  		LogLine{Text: "          foo │ layer 2: pending\n", SpanID: "foo", ProgressID: "layer 2", Time: now},
   532  	}, l.ContinuingLinesWithOptions(c1, lineOptionsWithManifests("foo")))
   533  }
   534  
   535  func TestBuildEventInit(t *testing.T) {
   536  	l := NewLogStore()
   537  
   538  	now := time.Now()
   539  	l.Append(testLogEvent{
   540  		name:    "",
   541  		message: "starting tilt\n",
   542  		ts:      now,
   543  	}, nil)
   544  	l.Append(testLogEvent{
   545  		name:    "fe",
   546  		message: "init fe build\n",
   547  		ts:      now,
   548  		fields:  map[string]string{logger.FieldNameBuildEvent: "init"},
   549  	}, nil)
   550  	l.Append(testLogEvent{
   551  		name:    "db",
   552  		message: "init db build\n",
   553  		ts:      now,
   554  		fields:  map[string]string{logger.FieldNameBuildEvent: "init"},
   555  	}, nil)
   556  
   557  	assert.Equal(t, 5, len(l.toLogLines(logOptions{spans: l.spans})))
   558  
   559  	assertSnapshot(t, l.String())
   560  }
   561  
   562  func assertSnapshot(t *testing.T, output string) {
   563  	d1 := []byte(output)
   564  	gmPath := fmt.Sprintf("testdata/%s_master", t.Name())
   565  	if LogstoreWriteGoldenMaster == "1" {
   566  		err := os.WriteFile(gmPath, d1, 0644)
   567  		if err != nil {
   568  			t.Fatal(err)
   569  		}
   570  	}
   571  	expected, err := os.ReadFile(gmPath)
   572  	if err != nil {
   573  		t.Fatal(err)
   574  	}
   575  
   576  	assert.Equal(t, string(expected), output)
   577  }
   578  
   579  func lineOptionsWithManifests(mns ...model.ManifestName) LineOptions {
   580  	mnSet := make(model.ManifestNameSet)
   581  	for _, mn := range mns {
   582  		mnSet[mn] = true
   583  	}
   584  	return LineOptions{ManifestNames: mnSet}
   585  }