go.fuchsia.dev/jiri@v0.0.0-20240502161911-b66513b29486/timing/timer_test.go (about)

     1  // Copyright 2015 The Vanadium Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package timing
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"math/rand"
    11  	"reflect"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  )
    16  
    17  func sec(d int) time.Duration {
    18  	return time.Second * time.Duration(d)
    19  }
    20  
    21  func tsec(d int) time.Time {
    22  	return time.Time{}.Add(sec(d))
    23  }
    24  
    25  // fakeNow is a simulated clock where now is set manually.
    26  type fakeNow struct{ now int }
    27  
    28  func (f *fakeNow) Now() time.Time { return tsec(f.now) }
    29  
    30  // stepNow is a simulated clock where now increments in 1 second steps.
    31  type stepNow struct{ now int }
    32  
    33  func (s *stepNow) Now() time.Time {
    34  	s.now++
    35  	return tsec(s.now)
    36  }
    37  
    38  type (
    39  	// op represents the operations that can be performed on a Timer, with a fake
    40  	// clock.  This makes it easy to construct test cases.
    41  	op interface {
    42  		run(f *fakeNow, t *Timer)
    43  	}
    44  	push struct {
    45  		now  int
    46  		name string
    47  	}
    48  	pop struct {
    49  		now int
    50  	}
    51  	finish struct {
    52  		now int
    53  	}
    54  )
    55  
    56  func (x push) run(f *fakeNow, t *Timer) {
    57  	f.now = x.now
    58  	t.Push(x.name)
    59  }
    60  func (x pop) run(f *fakeNow, t *Timer) {
    61  	f.now = x.now
    62  	t.Pop()
    63  }
    64  func (x finish) run(f *fakeNow, t *Timer) {
    65  	f.now = x.now
    66  	t.Finish()
    67  }
    68  
    69  // stripGaps strips out leading newlines, and also strips any line with an
    70  // asterisk (*).  Asterisks appear in lines with gaps, as shown here:
    71  //
    72  //	00:00:01.000 root   98.000s    00:01:39.000
    73  //	00:00:01.000    *       9.000s 00:00:10.000
    74  //	00:00:10.000    abc    89.000s 00:01:39.000
    75  func stripGaps(out string) string {
    76  	out = strings.TrimLeft(out, "\n")
    77  	var lines []string
    78  	for _, line := range strings.Split(out, "\n") {
    79  		if !strings.ContainsRune(line, '*') {
    80  			lines = append(lines, line)
    81  		}
    82  	}
    83  	return strings.Join(lines, "\n")
    84  }
    85  
    86  func TestTimer(t *testing.T) {
    87  	t.Parallel()
    88  	tests := []struct {
    89  		ops       []op
    90  		intervals []Interval
    91  		str       string
    92  	}{
    93  		{
    94  			nil,
    95  			[]Interval{{"root", 0, sec(0), InvalidDuration}},
    96  			`
    97  00:00:01.000 root 999.000s ---------now
    98  `,
    99  		},
   100  		{
   101  			[]op{pop{123}},
   102  			[]Interval{{"root", 0, sec(0), InvalidDuration}},
   103  			`
   104  00:00:01.000 root 999.000s ---------now
   105  `,
   106  		},
   107  		{
   108  			[]op{finish{99}},
   109  			[]Interval{{"root", 0, sec(0), sec(98)}},
   110  			`
   111  00:00:01.000 root 98.000s 00:01:39.000
   112  `,
   113  		},
   114  		{
   115  			[]op{finish{99}, pop{123}},
   116  			[]Interval{{"root", 0, sec(0), sec(98)}},
   117  			`
   118  00:00:01.000 root 98.000s 00:01:39.000
   119  `,
   120  		},
   121  		{
   122  			[]op{push{10, "abc"}},
   123  			[]Interval{
   124  				{"root", 0, sec(0), InvalidDuration},
   125  				{"abc", 1, sec(9), InvalidDuration},
   126  			},
   127  			`
   128  00:00:01.000 root   999.000s    ---------now
   129  00:00:01.000    *        9.000s 00:00:10.000
   130  00:00:10.000    abc    990.000s ---------now
   131  `,
   132  		},
   133  		{
   134  			[]op{push{10, "abc"}, finish{99}},
   135  			[]Interval{
   136  				{"root", 0, sec(0), sec(98)},
   137  				{"abc", 1, sec(9), sec(98)},
   138  			},
   139  			`
   140  00:00:01.000 root   98.000s    00:01:39.000
   141  00:00:01.000    *       9.000s 00:00:10.000
   142  00:00:10.000    abc    89.000s 00:01:39.000
   143  `,
   144  		},
   145  		{
   146  			[]op{push{10, "abc"}, pop{20}, finish{99}},
   147  			[]Interval{
   148  				{"root", 0, sec(0), sec(98)},
   149  				{"abc", 1, sec(9), sec(19)},
   150  			},
   151  			`
   152  00:00:01.000 root   98.000s    00:01:39.000
   153  00:00:01.000    *       9.000s 00:00:10.000
   154  00:00:10.000    abc    10.000s 00:00:20.000
   155  00:00:20.000    *      79.000s 00:01:39.000
   156  `,
   157  		},
   158  		{
   159  			[]op{push{10, "A1"}, push{20, "A1_1"}},
   160  			[]Interval{
   161  				{"root", 0, sec(0), InvalidDuration},
   162  				{"A1", 1, sec(9), InvalidDuration},
   163  				{"A1_1", 2, sec(19), InvalidDuration},
   164  			},
   165  			`
   166  00:00:01.000 root       999.000s       ---------now
   167  00:00:01.000    *            9.000s    00:00:10.000
   168  00:00:10.000    A1         990.000s    ---------now
   169  00:00:10.000       *           10.000s 00:00:20.000
   170  00:00:20.000       A1_1       980.000s ---------now
   171  `,
   172  		},
   173  		{
   174  			[]op{push{10, "A1"}, push{20, "A1_1"}, finish{99}},
   175  			[]Interval{
   176  				{"root", 0, sec(0), sec(98)},
   177  				{"A1", 1, sec(9), sec(98)},
   178  				{"A1_1", 2, sec(19), sec(98)},
   179  			},
   180  			`
   181  00:00:01.000 root       98.000s       00:01:39.000
   182  00:00:01.000    *           9.000s    00:00:10.000
   183  00:00:10.000    A1         89.000s    00:01:39.000
   184  00:00:10.000       *          10.000s 00:00:20.000
   185  00:00:20.000       A1_1       79.000s 00:01:39.000
   186  `,
   187  		},
   188  		{
   189  			[]op{push{10, "A1"}, push{20, "A1_1"}, pop{30}, finish{99}},
   190  			[]Interval{
   191  				{"root", 0, sec(0), sec(98)},
   192  				{"A1", 1, sec(9), sec(98)},
   193  				{"A1_1", 2, sec(19), sec(29)},
   194  			},
   195  			`
   196  00:00:01.000 root       98.000s       00:01:39.000
   197  00:00:01.000    *           9.000s    00:00:10.000
   198  00:00:10.000    A1         89.000s    00:01:39.000
   199  00:00:10.000       *          10.000s 00:00:20.000
   200  00:00:20.000       A1_1       10.000s 00:00:30.000
   201  00:00:30.000       *          69.000s 00:01:39.000
   202  `,
   203  		},
   204  		{
   205  			[]op{push{10, "A1"}, push{20, "A1_1"}, pop{30}, pop{40}, finish{99}},
   206  			[]Interval{
   207  				{"root", 0, sec(0), sec(98)},
   208  				{"A1", 1, sec(9), sec(39)},
   209  				{"A1_1", 2, sec(19), sec(29)},
   210  			},
   211  			`
   212  00:00:01.000 root       98.000s       00:01:39.000
   213  00:00:01.000    *           9.000s    00:00:10.000
   214  00:00:10.000    A1         30.000s    00:00:40.000
   215  00:00:10.000       *          10.000s 00:00:20.000
   216  00:00:20.000       A1_1       10.000s 00:00:30.000
   217  00:00:30.000       *          10.000s 00:00:40.000
   218  00:00:40.000    *          59.000s    00:01:39.000
   219  `,
   220  		},
   221  		{
   222  			[]op{push{10, "A1"}, push{20, "A1_1"}, push{30, "A1_1_1"}},
   223  			[]Interval{
   224  				{"root", 0, sec(0), InvalidDuration},
   225  				{"A1", 1, sec(9), InvalidDuration},
   226  				{"A1_1", 2, sec(19), InvalidDuration},
   227  				{"A1_1_1", 3, sec(29), InvalidDuration},
   228  			},
   229  			`
   230  00:00:01.000 root            999.000s          ---------now
   231  00:00:01.000    *                 9.000s       00:00:10.000
   232  00:00:10.000    A1              990.000s       ---------now
   233  00:00:10.000       *                10.000s    00:00:20.000
   234  00:00:20.000       A1_1            980.000s    ---------now
   235  00:00:20.000          *                10.000s 00:00:30.000
   236  00:00:30.000          A1_1_1          970.000s ---------now
   237  `,
   238  		},
   239  		{
   240  			[]op{push{10, "A1"}, push{20, "A1_1"}, push{30, "A1_1_1"}, finish{99}},
   241  			[]Interval{
   242  				{"root", 0, sec(0), sec(98)},
   243  				{"A1", 1, sec(9), sec(98)},
   244  				{"A1_1", 2, sec(19), sec(98)},
   245  				{"A1_1_1", 3, sec(29), sec(98)},
   246  			},
   247  			`
   248  00:00:01.000 root            98.000s          00:01:39.000
   249  00:00:01.000    *                9.000s       00:00:10.000
   250  00:00:10.000    A1              89.000s       00:01:39.000
   251  00:00:10.000       *               10.000s    00:00:20.000
   252  00:00:20.000       A1_1            79.000s    00:01:39.000
   253  00:00:20.000          *               10.000s 00:00:30.000
   254  00:00:30.000          A1_1_1          69.000s 00:01:39.000
   255  `,
   256  		},
   257  		{
   258  			[]op{push{10, "A1"}, push{20, "A1_1"}, push{30, "A1_1_1"}, pop{40}, finish{99}},
   259  			[]Interval{
   260  				{"root", 0, sec(0), sec(98)},
   261  				{"A1", 1, sec(9), sec(98)},
   262  				{"A1_1", 2, sec(19), sec(98)},
   263  				{"A1_1_1", 3, sec(29), sec(39)},
   264  			},
   265  			`
   266  00:00:01.000 root            98.000s          00:01:39.000
   267  00:00:01.000    *                9.000s       00:00:10.000
   268  00:00:10.000    A1              89.000s       00:01:39.000
   269  00:00:10.000       *               10.000s    00:00:20.000
   270  00:00:20.000       A1_1            79.000s    00:01:39.000
   271  00:00:20.000          *               10.000s 00:00:30.000
   272  00:00:30.000          A1_1_1          10.000s 00:00:40.000
   273  00:00:40.000          *               59.000s 00:01:39.000
   274  `,
   275  		},
   276  		{
   277  			[]op{push{10, "A1"}, push{20, "A1_1"}, push{30, "A1_1_1"}, pop{40}, pop{55}, finish{99}},
   278  			[]Interval{
   279  				{"root", 0, sec(0), sec(98)},
   280  				{"A1", 1, sec(9), sec(98)},
   281  				{"A1_1", 2, sec(19), sec(54)},
   282  				{"A1_1_1", 3, sec(29), sec(39)},
   283  			},
   284  			`
   285  00:00:01.000 root            98.000s          00:01:39.000
   286  00:00:01.000    *                9.000s       00:00:10.000
   287  00:00:10.000    A1              89.000s       00:01:39.000
   288  00:00:10.000       *               10.000s    00:00:20.000
   289  00:00:20.000       A1_1            35.000s    00:00:55.000
   290  00:00:20.000          *               10.000s 00:00:30.000
   291  00:00:30.000          A1_1_1          10.000s 00:00:40.000
   292  00:00:40.000          *               15.000s 00:00:55.000
   293  00:00:55.000       *               44.000s    00:01:39.000
   294  `,
   295  		},
   296  		{
   297  			[]op{push{10, "A1"}, push{20, "A1_1"}, push{30, "A1_1_1"}, pop{40}, pop{55}, pop{75}, finish{99}},
   298  			[]Interval{
   299  				{"root", 0, sec(0), sec(98)},
   300  				{"A1", 1, sec(9), sec(74)},
   301  				{"A1_1", 2, sec(19), sec(54)},
   302  				{"A1_1_1", 3, sec(29), sec(39)},
   303  			},
   304  			`
   305  00:00:01.000 root            98.000s          00:01:39.000
   306  00:00:01.000    *                9.000s       00:00:10.000
   307  00:00:10.000    A1              65.000s       00:01:15.000
   308  00:00:10.000       *               10.000s    00:00:20.000
   309  00:00:20.000       A1_1            35.000s    00:00:55.000
   310  00:00:20.000          *               10.000s 00:00:30.000
   311  00:00:30.000          A1_1_1          10.000s 00:00:40.000
   312  00:00:40.000          *               15.000s 00:00:55.000
   313  00:00:55.000       *               20.000s    00:01:15.000
   314  00:01:15.000    *               24.000s       00:01:39.000
   315  `,
   316  		},
   317  		{
   318  			[]op{push{10, "A1"}, push{20, "A1_1"}, finish{30}, push{40, "B1"}},
   319  			[]Interval{
   320  				{"root", 0, sec(0), InvalidDuration},
   321  				{"A1", 1, sec(9), sec(29)},
   322  				{"A1_1", 2, sec(19), sec(29)},
   323  				{"B1", 1, sec(39), InvalidDuration},
   324  			},
   325  			`
   326  00:00:01.000 root       999.000s       ---------now
   327  00:00:01.000    *            9.000s    00:00:10.000
   328  00:00:10.000    A1          20.000s    00:00:30.000
   329  00:00:10.000       *           10.000s 00:00:20.000
   330  00:00:20.000       A1_1        10.000s 00:00:30.000
   331  00:00:30.000    *           10.000s    00:00:40.000
   332  00:00:40.000    B1         960.000s    ---------now
   333  `,
   334  		},
   335  		{
   336  			[]op{push{10, "A1"}, push{20, "A1_1"}, finish{30}, push{40, "B1"}, finish{99}},
   337  			[]Interval{
   338  				{"root", 0, sec(0), sec(98)},
   339  				{"A1", 1, sec(9), sec(29)},
   340  				{"A1_1", 2, sec(19), sec(29)},
   341  				{"B1", 1, sec(39), sec(98)},
   342  			},
   343  			`
   344  00:00:01.000 root       98.000s       00:01:39.000
   345  00:00:01.000    *           9.000s    00:00:10.000
   346  00:00:10.000    A1         20.000s    00:00:30.000
   347  00:00:10.000       *          10.000s 00:00:20.000
   348  00:00:20.000       A1_1       10.000s 00:00:30.000
   349  00:00:30.000    *          10.000s    00:00:40.000
   350  00:00:40.000    B1         59.000s    00:01:39.000
   351  `,
   352  		},
   353  		{
   354  			[]op{push{10, "foo"}, push{15, "foo1"}, pop{37}, push{37, "foo2"}, pop{55}, pop{55}, push{55, "bar"}, pop{80}, push{80, "baz"}, pop{99}, finish{99}},
   355  			[]Interval{
   356  				{"root", 0, sec(0), sec(98)},
   357  				{"foo", 1, sec(9), sec(54)},
   358  				{"foo1", 2, sec(14), sec(36)},
   359  				{"foo2", 2, sec(36), sec(54)},
   360  				{"bar", 1, sec(54), sec(79)},
   361  				{"baz", 1, sec(79), sec(98)},
   362  			},
   363  			`
   364  00:00:01.000 root       98.000s       00:01:39.000
   365  00:00:01.000    *           9.000s    00:00:10.000
   366  00:00:10.000    foo        45.000s    00:00:55.000
   367  00:00:10.000       *           5.000s 00:00:15.000
   368  00:00:15.000       foo1       22.000s 00:00:37.000
   369  00:00:37.000       foo2       18.000s 00:00:55.000
   370  00:00:55.000    bar        25.000s    00:01:20.000
   371  00:01:20.000    baz        19.000s    00:01:39.000
   372  `,
   373  		},
   374  		{
   375  			[]op{push{10, "foo"}, push{15, "foo1"}, pop{30}, push{37, "foo2"}, pop{50}, pop{53}, push{55, "bar"}, pop{75}, push{80, "baz"}, pop{90}, finish{99}},
   376  			[]Interval{
   377  				{"root", 0, sec(0), sec(98)},
   378  				{"foo", 1, sec(9), sec(52)},
   379  				{"foo1", 2, sec(14), sec(29)},
   380  				{"foo2", 2, sec(36), sec(49)},
   381  				{"bar", 1, sec(54), sec(74)},
   382  				{"baz", 1, sec(79), sec(89)},
   383  			},
   384  			`
   385  00:00:01.000 root       98.000s       00:01:39.000
   386  00:00:01.000    *           9.000s    00:00:10.000
   387  00:00:10.000    foo        43.000s    00:00:53.000
   388  00:00:10.000       *           5.000s 00:00:15.000
   389  00:00:15.000       foo1       15.000s 00:00:30.000
   390  00:00:30.000       *           7.000s 00:00:37.000
   391  00:00:37.000       foo2       13.000s 00:00:50.000
   392  00:00:50.000       *           3.000s 00:00:53.000
   393  00:00:53.000    *           2.000s    00:00:55.000
   394  00:00:55.000    bar        20.000s    00:01:15.000
   395  00:01:15.000    *           5.000s    00:01:20.000
   396  00:01:20.000    baz        10.000s    00:01:30.000
   397  00:01:30.000    *           9.000s    00:01:39.000
   398  `,
   399  		},
   400  	}
   401  	for _, test := range tests {
   402  		// Run all ops.
   403  		now := &fakeNow{1}
   404  		nowFunc = now.Now
   405  		timer := NewTimer("root")
   406  		for _, op := range test.ops {
   407  			op.run(now, timer)
   408  		}
   409  		name := fmt.Sprintf("%#v", test.ops)
   410  		// Check all intervals.
   411  		if got, want := timer.Intervals, test.intervals; !reflect.DeepEqual(got, want) {
   412  			t.Errorf("%s got intervals %v, want %v", name, got, want)
   413  		}
   414  		// Check string output.
   415  		now.now = 1000
   416  		if got, want := timer.String(), strings.TrimLeft(test.str, "\n"); got != want {
   417  			t.Errorf("%s GOT STRING\n%sWANT\n%s", name, got, want)
   418  		}
   419  		// Check print output hiding all gaps.
   420  		var buf bytes.Buffer
   421  		printer := IntervalPrinter{Zero: timer.Zero, MinGap: time.Hour}
   422  		if err := printer.Print(&buf, timer.Intervals, nowFunc().Sub(timer.Zero)); err != nil {
   423  			t.Errorf("%s got printer error: %v", name, err)
   424  		}
   425  		if got, want := buf.String(), stripGaps(test.str); got != want {
   426  			t.Errorf("%s GOT PRINT\n%sWANT\n%s", name, got, want)
   427  		}
   428  	}
   429  	nowFunc = time.Now
   430  }
   431  
   432  // TestIntervalPrinterCornerCases tests corner cases for the printer.  These are
   433  // all cases where only a subset of the full Timer intervals is printed.
   434  func TestIntervalPrinterCornerCases(t *testing.T) {
   435  	t.Parallel()
   436  	tests := []struct {
   437  		intervals []Interval
   438  		str       string
   439  	}{
   440  		{
   441  			[]Interval{{"abc", 1, sec(9), InvalidDuration}},
   442  			`
   443  00:00:01.000 *     9.000s 00:00:10.000
   444  00:00:10.000 abc 990.000s ---------now
   445  `,
   446  		},
   447  		{
   448  			[]Interval{{"abc", 1, sec(9), sec(98)}},
   449  			`
   450  00:00:01.000 *    9.000s 00:00:10.000
   451  00:00:10.000 abc 89.000s 00:01:39.000
   452  `,
   453  		},
   454  		{
   455  			[]Interval{
   456  				{"A1", 1, sec(9), InvalidDuration},
   457  				{"A1_1", 2, sec(19), InvalidDuration},
   458  			},
   459  			`
   460  00:00:01.000 *         9.000s    00:00:10.000
   461  00:00:10.000 A1      990.000s    ---------now
   462  00:00:10.000    *        10.000s 00:00:20.000
   463  00:00:20.000    A1_1    980.000s ---------now
   464  `,
   465  		},
   466  		{
   467  			[]Interval{
   468  				{"A1", 1, sec(9), InvalidDuration},
   469  				{"A1_1", 2, sec(19), sec(49)},
   470  			},
   471  			`
   472  00:00:01.000 *         9.000s    00:00:10.000
   473  00:00:10.000 A1      990.000s    ---------now
   474  00:00:10.000    *        10.000s 00:00:20.000
   475  00:00:20.000    A1_1     30.000s 00:00:50.000
   476  00:00:50.000    *       950.000s ---------now
   477  `,
   478  		},
   479  		{
   480  			[]Interval{
   481  				{"A1", 1, sec(9), sec(98)},
   482  				{"A1_1", 2, sec(19), sec(49)},
   483  			},
   484  			`
   485  00:00:01.000 *        9.000s    00:00:10.000
   486  00:00:10.000 A1      89.000s    00:01:39.000
   487  00:00:10.000    *       10.000s 00:00:20.000
   488  00:00:20.000    A1_1    30.000s 00:00:50.000
   489  00:00:50.000    *       49.000s 00:01:39.000
   490  `,
   491  		},
   492  		{
   493  			[]Interval{
   494  				{"A1_1", 2, sec(9), sec(19)},
   495  				{"B1", 1, sec(39), InvalidDuration},
   496  			},
   497  			`
   498  00:00:01.000    *         9.000s 00:00:10.000
   499  00:00:10.000    A1_1     10.000s 00:00:20.000
   500  00:00:20.000    *        20.000s 00:00:40.000
   501  00:00:40.000 B1      960.000s    ---------now
   502  `,
   503  		},
   504  		{
   505  			[]Interval{
   506  				{"A1_1", 2, sec(9), sec(19)},
   507  				{"B1", 1, sec(39), sec(64)},
   508  			},
   509  			`
   510  00:00:01.000    *        9.000s 00:00:10.000
   511  00:00:10.000    A1_1    10.000s 00:00:20.000
   512  00:00:20.000    *       20.000s 00:00:40.000
   513  00:00:40.000 B1      25.000s    00:01:05.000
   514  `,
   515  		},
   516  		{
   517  			[]Interval{
   518  				{"A1_1", 2, sec(9), sec(19)},
   519  				{"B1", 1, sec(39), sec(64)},
   520  				{"C1", 1, sec(69), sec(84)},
   521  			},
   522  			`
   523  00:00:01.000    *        9.000s 00:00:10.000
   524  00:00:10.000    A1_1    10.000s 00:00:20.000
   525  00:00:20.000    *       20.000s 00:00:40.000
   526  00:00:40.000 B1      25.000s    00:01:05.000
   527  00:01:05.000 *        5.000s    00:01:10.000
   528  00:01:10.000 C1      15.000s    00:01:25.000
   529  `,
   530  		},
   531  		{
   532  			[]Interval{
   533  				{"A1_1", 2, sec(9), sec(19)},
   534  				{"B1", 1, sec(39), sec(84)},
   535  				{"B1_1", 2, sec(64), sec(69)},
   536  			},
   537  			`
   538  00:00:01.000    *        9.000s 00:00:10.000
   539  00:00:10.000    A1_1    10.000s 00:00:20.000
   540  00:00:20.000    *       20.000s 00:00:40.000
   541  00:00:40.000 B1      45.000s    00:01:25.000
   542  00:00:40.000    *       25.000s 00:01:05.000
   543  00:01:05.000    B1_1     5.000s 00:01:10.000
   544  00:01:10.000    *       15.000s 00:01:25.000
   545  `,
   546  		},
   547  		{
   548  			[]Interval{
   549  				{"A1_1", 2, sec(9), sec(19)},
   550  				{"B1", 1, sec(39), sec(89)},
   551  				{"B1_1", 2, sec(64), sec(69)},
   552  				{"B1_2", 2, sec(79), sec(87)},
   553  			},
   554  			`
   555  00:00:01.000    *        9.000s 00:00:10.000
   556  00:00:10.000    A1_1    10.000s 00:00:20.000
   557  00:00:20.000    *       20.000s 00:00:40.000
   558  00:00:40.000 B1      50.000s    00:01:30.000
   559  00:00:40.000    *       25.000s 00:01:05.000
   560  00:01:05.000    B1_1     5.000s 00:01:10.000
   561  00:01:10.000    *       10.000s 00:01:20.000
   562  00:01:20.000    B1_2     8.000s 00:01:28.000
   563  00:01:28.000    *        2.000s 00:01:30.000
   564  `,
   565  		},
   566  		{
   567  			[]Interval{
   568  				{"A1_1_1", 3, sec(9), sec(19)},
   569  				{"B1", 1, sec(39), InvalidDuration},
   570  			},
   571  			`
   572  00:00:01.000       *              9.000s 00:00:10.000
   573  00:00:10.000       A1_1_1        10.000s 00:00:20.000
   574  00:00:20.000       *             20.000s 00:00:40.000
   575  00:00:40.000 B1           960.000s       ---------now
   576  `,
   577  		},
   578  		{
   579  			[]Interval{
   580  				{"A1_1_1", 3, sec(9), sec(19)},
   581  				{"B1", 1, sec(39), sec(64)},
   582  			},
   583  			`
   584  00:00:01.000       *             9.000s 00:00:10.000
   585  00:00:10.000       A1_1_1       10.000s 00:00:20.000
   586  00:00:20.000       *            20.000s 00:00:40.000
   587  00:00:40.000 B1           25.000s       00:01:05.000
   588  `,
   589  		},
   590  		{
   591  			[]Interval{
   592  				{"A1_1_1", 3, sec(9), sec(19)},
   593  				{"B1", 1, sec(39), sec(64)},
   594  				{"C1", 1, sec(69), sec(84)},
   595  			},
   596  			`
   597  00:00:01.000       *             9.000s 00:00:10.000
   598  00:00:10.000       A1_1_1       10.000s 00:00:20.000
   599  00:00:20.000       *            20.000s 00:00:40.000
   600  00:00:40.000 B1           25.000s       00:01:05.000
   601  00:01:05.000 *             5.000s       00:01:10.000
   602  00:01:10.000 C1           15.000s       00:01:25.000
   603  `,
   604  		},
   605  		{
   606  			[]Interval{
   607  				{"A1_1_1", 3, sec(9), sec(19)},
   608  				{"B1", 1, sec(39), sec(84)},
   609  				{"B1_1", 2, sec(59), sec(69)},
   610  			},
   611  			`
   612  00:00:01.000       *             9.000s 00:00:10.000
   613  00:00:10.000       A1_1_1       10.000s 00:00:20.000
   614  00:00:20.000       *            20.000s 00:00:40.000
   615  00:00:40.000 B1           45.000s       00:01:25.000
   616  00:00:40.000    *            20.000s    00:01:00.000
   617  00:01:00.000    B1_1         10.000s    00:01:10.000
   618  00:01:10.000    *            15.000s    00:01:25.000
   619  `,
   620  		},
   621  		{
   622  			[]Interval{
   623  				{"A1_1", 2, sec(9), sec(84)},
   624  				{"A1_1_1", 3, sec(39), sec(79)},
   625  				{"A1_1_1_1", 4, sec(54), sec(69)},
   626  				{"B1", 1, sec(89), sec(99)},
   627  			},
   628  			`
   629  00:00:01.000    *                  9.000s       00:00:10.000
   630  00:00:10.000    A1_1              75.000s       00:01:25.000
   631  00:00:10.000       *                 30.000s    00:00:40.000
   632  00:00:40.000       A1_1_1            40.000s    00:01:20.000
   633  00:00:40.000          *                 15.000s 00:00:55.000
   634  00:00:55.000          A1_1_1_1          15.000s 00:01:10.000
   635  00:01:10.000          *                 10.000s 00:01:20.000
   636  00:01:20.000       *                  5.000s    00:01:25.000
   637  00:01:25.000    *                  5.000s       00:01:30.000
   638  00:01:30.000 B1                10.000s          00:01:40.000
   639  `,
   640  		},
   641  	}
   642  	for _, test := range tests {
   643  		name := fmt.Sprintf("%#v", test.intervals)
   644  		// Check print output with gaps.
   645  		var buf bytes.Buffer
   646  		printer := IntervalPrinter{Zero: tsec(1)}
   647  		if err := printer.Print(&buf, test.intervals, sec(999)); err != nil {
   648  			t.Errorf("%s got printer error: %v", name, err)
   649  		}
   650  		if got, want := buf.String(), strings.TrimLeft(test.str, "\n"); got != want {
   651  			t.Errorf("%s GOT STRING\n%sWANT\n%s", name, got, want)
   652  		}
   653  	}
   654  }
   655  
   656  func BenchmarkTimerPush(b *testing.B) {
   657  	t := NewTimer("root")
   658  	for i := 0; i < b.N; i++ {
   659  		t.Push("child")
   660  	}
   661  }
   662  
   663  func BenchmarkTimerPushPop(b *testing.B) {
   664  	t := NewTimer("root")
   665  	for i := 0; i < b.N; i++ {
   666  		timerPushPop(t)
   667  	}
   668  }
   669  func timerPushPop(t *Timer) {
   670  	t.Push("child1")
   671  	t.Pop()
   672  	t.Push("child2")
   673  	t.Push("child2_1")
   674  	t.Pop()
   675  	t.Push("child2_2")
   676  	t.Pop()
   677  	t.Pop()
   678  	t.Push("child3")
   679  	t.Pop()
   680  }
   681  
   682  var randSource = rand.NewSource(123)
   683  
   684  func BenchmarkTimerRandom(b *testing.B) {
   685  	t, rng := NewTimer("root"), rand.New(randSource)
   686  	for i := 0; i < b.N; i++ {
   687  		timerRandom(rng, t)
   688  	}
   689  }
   690  func timerRandom(rng *rand.Rand, t *Timer) {
   691  	switch pct := rng.Intn(100); {
   692  	case pct < 60:
   693  		timerPushPop(t)
   694  	case pct < 90:
   695  		t.Push("foo")
   696  	case pct < 99:
   697  		t.Pop()
   698  	default:
   699  		t.Finish()
   700  	}
   701  }
   702  
   703  func BenchmarkTimerString(b *testing.B) {
   704  	t, rng, now := NewTimer("root"), rand.New(randSource), &stepNow{0}
   705  	nowFunc = now.Now
   706  	for i := 0; i < 1000; i++ {
   707  		timerRandom(rng, t)
   708  	}
   709  	t.Finish() // Make sure all intervals are closed.
   710  	want := t.String()
   711  	b.ResetTimer()
   712  	for i := 0; i < b.N; i++ {
   713  		if got := t.String(); got != want {
   714  			b.Fatalf("GOT\n%sWANT\n%s\nTIMER\n%#v", got, want, t)
   715  		}
   716  	}
   717  	nowFunc = time.Now
   718  }