1// Copyright 2019 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package status
16
17import (
18	"reflect"
19	"testing"
20	"time"
21)
22
23type testCriticalPath struct {
24	*criticalPath
25	Counts
26
27	actions map[int]*Action
28}
29
30type testClock time.Time
31
32func (t testClock) Now() time.Time { return time.Time(t) }
33
34func (t *testCriticalPath) start(id int, startTime time.Duration, outputs, inputs []string) {
35	t.clock = testClock(time.Unix(0, 0).Add(startTime))
36	action := &Action{
37		Description: outputs[0],
38		Outputs:     outputs,
39		Inputs:      inputs,
40	}
41
42	t.actions[id] = action
43	t.StartAction(action, t.Counts)
44}
45
46func (t *testCriticalPath) finish(id int, endTime time.Duration) {
47	t.clock = testClock(time.Unix(0, 0).Add(endTime))
48	t.FinishAction(ActionResult{
49		Action: t.actions[id],
50	}, t.Counts)
51}
52
53func TestCriticalPath(t *testing.T) {
54	tests := []struct {
55		name     string
56		msgs     func(*testCriticalPath)
57		want     []string
58		wantTime time.Duration
59	}{
60		{
61			name: "empty",
62			msgs: func(cp *testCriticalPath) {},
63		},
64		{
65			name: "duplicate",
66			msgs: func(cp *testCriticalPath) {
67				cp.start(0, 0, []string{"a"}, nil)
68				cp.start(1, 0, []string{"a"}, nil)
69				cp.finish(0, 1000)
70				cp.finish(0, 2000)
71			},
72			want:     []string{"a"},
73			wantTime: 1000,
74		},
75		{
76			name: "linear",
77			//  a
78			//  |
79			//  b
80			//  |
81			//  c
82			msgs: func(cp *testCriticalPath) {
83				cp.start(0, 0, []string{"a"}, nil)
84				cp.finish(0, 1000)
85				cp.start(1, 1000, []string{"b"}, []string{"a"})
86				cp.finish(1, 2000)
87				cp.start(2, 3000, []string{"c"}, []string{"b"})
88				cp.finish(2, 4000)
89			},
90			want:     []string{"c", "b", "a"},
91			wantTime: 3000,
92		},
93		{
94			name: "diamond",
95			//  a
96			//  |\
97			//  b c
98			//  |/
99			//  d
100			msgs: func(cp *testCriticalPath) {
101				cp.start(0, 0, []string{"a"}, nil)
102				cp.finish(0, 1000)
103				cp.start(1, 1000, []string{"b"}, []string{"a"})
104				cp.start(2, 1000, []string{"c"}, []string{"a"})
105				cp.finish(1, 2000)
106				cp.finish(2, 3000)
107				cp.start(3, 3000, []string{"d"}, []string{"b", "c"})
108				cp.finish(3, 4000)
109			},
110			want:     []string{"d", "c", "a"},
111			wantTime: 4000,
112		},
113		{
114			name: "multiple",
115			//  a d
116			//  | |
117			//  b e
118			//  |
119			//  c
120			msgs: func(cp *testCriticalPath) {
121				cp.start(0, 0, []string{"a"}, nil)
122				cp.start(3, 0, []string{"d"}, nil)
123				cp.finish(0, 1000)
124				cp.finish(3, 1000)
125				cp.start(1, 1000, []string{"b"}, []string{"a"})
126				cp.start(4, 1000, []string{"e"}, []string{"d"})
127				cp.finish(1, 2000)
128				cp.start(2, 2000, []string{"c"}, []string{"b"})
129				cp.finish(2, 3000)
130				cp.finish(4, 4000)
131
132			},
133			want:     []string{"e", "d"},
134			wantTime: 4000,
135		},
136	}
137	for _, tt := range tests {
138		t.Run(tt.name, func(t *testing.T) {
139			cp := &testCriticalPath{
140				criticalPath: NewCriticalPath(nil).(*criticalPath),
141				actions:      make(map[int]*Action),
142			}
143
144			tt.msgs(cp)
145
146			criticalPath := cp.criticalPath.criticalPath()
147
148			var descs []string
149			for _, x := range criticalPath {
150				descs = append(descs, x.action.Description)
151			}
152
153			if !reflect.DeepEqual(descs, tt.want) {
154				t.Errorf("criticalPath.criticalPath() = %v, want %v", descs, tt.want)
155			}
156
157			var gotTime time.Duration
158			if len(criticalPath) > 0 {
159				gotTime = criticalPath[0].cumulativeDuration
160			}
161			if gotTime != tt.wantTime {
162				t.Errorf("cumulativeDuration[0].cumulativeDuration = %v, want %v", gotTime, tt.wantTime)
163			}
164		})
165	}
166}
167