1// Copyright (C) 2018 The Android Open Source Project
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
15import {produce} from 'immer';
16
17import {StateActions} from './actions';
18import {
19  createEmptyState,
20  SCROLLING_TRACK_GROUP,
21  State,
22  TraceUrlSource,
23  TrackState,
24} from './state';
25
26function fakeTrack(state: State, id: string): TrackState {
27  const track: TrackState = {
28    id,
29    engineId: '1',
30    kind: 'SOME_TRACK_KIND',
31    name: 'A track',
32    trackGroup: SCROLLING_TRACK_GROUP,
33    config: {},
34  };
35  state.tracks[id] = track;
36  return track;
37}
38
39test('navigate', () => {
40  const after = produce(createEmptyState(), draft => {
41    StateActions.navigate(draft, {route: '/foo'});
42  });
43  expect(after.route).toBe('/foo');
44});
45
46test('add scrolling tracks', () => {
47  const once = produce(createEmptyState(), draft => {
48    StateActions.addTrack(draft, {
49      engineId: '1',
50      kind: 'cpu',
51      name: 'Cpu 1',
52      trackGroup: SCROLLING_TRACK_GROUP,
53      config: {},
54    });
55  });
56  const twice = produce(once, draft => {
57    StateActions.addTrack(draft, {
58      engineId: '2',
59      kind: 'cpu',
60      name: 'Cpu 2',
61      trackGroup: SCROLLING_TRACK_GROUP,
62      config: {},
63    });
64  });
65
66  expect(Object.values(twice.tracks).length).toBe(2);
67  expect(twice.scrollingTracks.length).toBe(2);
68});
69
70test('add track to track group', () => {
71  const state = createEmptyState();
72  fakeTrack(state, 's');
73
74  const afterGroup = produce(state, draft => {
75    StateActions.addTrackGroup(draft, {
76      engineId: '1',
77      name: 'A track group',
78      id: '123-123-123',
79      summaryTrackId: 's',
80      collapsed: false,
81    });
82  });
83
84  const afterTrackAdd = produce(afterGroup, draft => {
85    StateActions.addTrack(draft, {
86      id: '1',
87      engineId: '1',
88      kind: 'slices',
89      name: 'renderer 1',
90      trackGroup: '123-123-123',
91      config: {},
92    });
93  });
94
95  expect(afterTrackAdd.trackGroups['123-123-123'].tracks[0]).toBe('1');
96});
97
98test('reorder tracks', () => {
99  const once = produce(createEmptyState(), draft => {
100    StateActions.addTrack(draft, {
101      engineId: '1',
102      kind: 'cpu',
103      name: 'Cpu 1',
104      config: {},
105    });
106    StateActions.addTrack(draft, {
107      engineId: '2',
108      kind: 'cpu',
109      name: 'Cpu 2',
110      config: {},
111    });
112  });
113
114  const firstTrackId = once.scrollingTracks[0];
115  const secondTrackId = once.scrollingTracks[1];
116
117  const twice = produce(once, draft => {
118    StateActions.moveTrack(draft, {
119      srcId: `${firstTrackId}`,
120      op: 'after',
121      dstId: `${secondTrackId}`,
122    });
123  });
124
125  expect(twice.scrollingTracks[0]).toBe(secondTrackId);
126  expect(twice.scrollingTracks[1]).toBe(firstTrackId);
127});
128
129test('reorder pinned to scrolling', () => {
130  const state = createEmptyState();
131  fakeTrack(state, 'a');
132  fakeTrack(state, 'b');
133  fakeTrack(state, 'c');
134  state.pinnedTracks = ['a', 'b'];
135  state.scrollingTracks = ['c'];
136
137  const after = produce(state, draft => {
138    StateActions.moveTrack(draft, {
139      srcId: 'b',
140      op: 'before',
141      dstId: 'c',
142    });
143  });
144
145  expect(after.pinnedTracks).toEqual(['a']);
146  expect(after.scrollingTracks).toEqual(['b', 'c']);
147});
148
149test('reorder scrolling to pinned', () => {
150  const state = createEmptyState();
151  fakeTrack(state, 'a');
152  fakeTrack(state, 'b');
153  fakeTrack(state, 'c');
154  state.pinnedTracks = ['a'];
155  state.scrollingTracks = ['b', 'c'];
156
157  const after = produce(state, draft => {
158    StateActions.moveTrack(draft, {
159      srcId: 'b',
160      op: 'after',
161      dstId: 'a',
162    });
163  });
164
165  expect(after.pinnedTracks).toEqual(['a', 'b']);
166  expect(after.scrollingTracks).toEqual(['c']);
167});
168
169test('reorder clamp bottom', () => {
170  const state = createEmptyState();
171  fakeTrack(state, 'a');
172  fakeTrack(state, 'b');
173  fakeTrack(state, 'c');
174  state.pinnedTracks = ['a', 'b'];
175  state.scrollingTracks = ['c'];
176
177  const after = produce(state, draft => {
178    StateActions.moveTrack(draft, {
179      srcId: 'a',
180      op: 'before',
181      dstId: 'a',
182    });
183  });
184  expect(after).toEqual(state);
185});
186
187test('reorder clamp top', () => {
188  const state = createEmptyState();
189  fakeTrack(state, 'a');
190  fakeTrack(state, 'b');
191  fakeTrack(state, 'c');
192  state.pinnedTracks = ['a'];
193  state.scrollingTracks = ['b', 'c'];
194
195  const after = produce(state, draft => {
196    StateActions.moveTrack(draft, {
197      srcId: 'c',
198      op: 'after',
199      dstId: 'c',
200    });
201  });
202  expect(after).toEqual(state);
203});
204
205test('pin', () => {
206  const state = createEmptyState();
207  fakeTrack(state, 'a');
208  fakeTrack(state, 'b');
209  fakeTrack(state, 'c');
210  state.pinnedTracks = ['a'];
211  state.scrollingTracks = ['b', 'c'];
212
213  const after = produce(state, draft => {
214    StateActions.toggleTrackPinned(draft, {
215      trackId: 'c',
216    });
217  });
218  expect(after.pinnedTracks).toEqual(['a', 'c']);
219  expect(after.scrollingTracks).toEqual(['b']);
220});
221
222test('unpin', () => {
223  const state = createEmptyState();
224  fakeTrack(state, 'a');
225  fakeTrack(state, 'b');
226  fakeTrack(state, 'c');
227  state.pinnedTracks = ['a', 'b'];
228  state.scrollingTracks = ['c'];
229
230  const after = produce(state, draft => {
231    StateActions.toggleTrackPinned(draft, {
232      trackId: 'a',
233    });
234  });
235  expect(after.pinnedTracks).toEqual(['b']);
236  expect(after.scrollingTracks).toEqual(['a', 'c']);
237});
238
239test('open trace', () => {
240  const state = createEmptyState();
241  state.nextId = 100;
242  const recordConfig = state.recordConfig;
243  const after = produce(state, draft => {
244    StateActions.openTraceFromUrl(draft, {
245      url: 'https://example.com/bar',
246    });
247  });
248
249  const engineKeys = Object.keys(after.engines);
250  expect(after.nextId).toBe(101);
251  expect(engineKeys.length).toBe(1);
252  expect((after.engines[engineKeys[0]].source as TraceUrlSource).url)
253      .toBe('https://example.com/bar');
254  expect(after.route).toBe('/viewer');
255  expect(after.recordConfig).toBe(recordConfig);
256});
257
258test('open second trace from file', () => {
259  const once = produce(createEmptyState(), draft => {
260    StateActions.openTraceFromUrl(draft, {
261      url: 'https://example.com/bar',
262    });
263  });
264
265  const twice = produce(once, draft => {
266    StateActions.addTrack(draft, {
267      engineId: '1',
268      kind: 'cpu',
269      name: 'Cpu 1',
270      config: {},
271    });
272  });
273
274  const thrice = produce(twice, draft => {
275    StateActions.openTraceFromUrl(draft, {
276      url: 'https://example.com/foo',
277    });
278  });
279
280  const engineKeys = Object.keys(thrice.engines);
281  expect(engineKeys.length).toBe(1);
282  expect((thrice.engines[engineKeys[0]].source as TraceUrlSource).url)
283      .toBe('https://example.com/foo');
284  expect(thrice.pinnedTracks.length).toBe(0);
285  expect(thrice.scrollingTracks.length).toBe(0);
286  expect(thrice.route).toBe('/viewer');
287});
288