1 /*******************************************************************************
2 * Copyright (c) 2016 Ericsson
4 * All rights reserved. This program and the accompanying materials are made
5 * available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
9 package org
.eclipse
.tracecompass
.analysis
.timing
.ui
.swtbot
.tests
.table
;
11 import static org
.junit
.Assert
.assertEquals
;
12 import static org
.junit
.Assert
.assertNotNull
;
13 import static org
.junit
.Assert
.assertNull
;
14 import static org
.junit
.Assert
.assertTrue
;
16 import java
.io
.BufferedReader
;
17 import java
.io
.ByteArrayOutputStream
;
19 import java
.io
.FileReader
;
20 import java
.io
.IOException
;
21 import java
.lang
.reflect
.InvocationTargetException
;
22 import java
.lang
.reflect
.Method
;
23 import java
.util
.ArrayList
;
24 import java
.util
.Arrays
;
25 import java
.util
.Collections
;
26 import java
.util
.List
;
27 import java
.util
.Random
;
28 import java
.util
.stream
.Collectors
;
30 import org
.apache
.log4j
.ConsoleAppender
;
31 import org
.apache
.log4j
.Logger
;
32 import org
.apache
.log4j
.SimpleLayout
;
33 import org
.eclipse
.jdt
.annotation
.NonNull
;
34 import org
.eclipse
.jdt
.annotation
.Nullable
;
35 import org
.eclipse
.jface
.viewers
.TableViewer
;
36 import org
.eclipse
.swtbot
.eclipse
.finder
.SWTWorkbenchBot
;
37 import org
.eclipse
.swtbot
.eclipse
.finder
.widgets
.SWTBotView
;
38 import org
.eclipse
.swtbot
.swt
.finder
.SWTBot
;
39 import org
.eclipse
.swtbot
.swt
.finder
.finders
.UIThreadRunnable
;
40 import org
.eclipse
.swtbot
.swt
.finder
.results
.Result
;
41 import org
.eclipse
.swtbot
.swt
.finder
.utils
.SWTBotPreferences
;
42 import org
.eclipse
.swtbot
.swt
.finder
.widgets
.SWTBotMenu
;
43 import org
.eclipse
.swtbot
.swt
.finder
.widgets
.SWTBotTable
;
44 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.IAnalysisProgressListener
;
45 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.ISegmentStoreProvider
;
46 import org
.eclipse
.tracecompass
.analysis
.timing
.ui
.views
.segmentstore
.table
.AbstractSegmentStoreTableView
;
47 import org
.eclipse
.tracecompass
.analysis
.timing
.ui
.views
.segmentstore
.table
.AbstractSegmentStoreTableViewer
;
48 import org
.eclipse
.tracecompass
.segmentstore
.core
.BasicSegment
;
49 import org
.eclipse
.tracecompass
.segmentstore
.core
.ISegment
;
50 import org
.eclipse
.tracecompass
.segmentstore
.core
.ISegmentStore
;
51 import org
.eclipse
.tracecompass
.segmentstore
.core
.SegmentStoreFactory
;
52 import org
.eclipse
.tracecompass
.segmentstore
.core
.SegmentStoreFactory
.SegmentStoreType
;
53 import org
.eclipse
.tracecompass
.tmf
.core
.segment
.ISegmentAspect
;
54 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
55 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
56 import org
.eclipse
.tracecompass
.tmf
.ui
.dialog
.TmfFileDialogFactory
;
57 import org
.eclipse
.tracecompass
.tmf
.ui
.swtbot
.tests
.shared
.ConditionHelpers
;
58 import org
.eclipse
.tracecompass
.tmf
.ui
.swtbot
.tests
.shared
.SWTBotUtils
;
59 import org
.eclipse
.tracecompass
.tmf
.ui
.tests
.shared
.WaitUtils
;
60 import org
.eclipse
.ui
.IViewPart
;
61 import org
.eclipse
.ui
.IWorkbench
;
62 import org
.eclipse
.ui
.PartInitException
;
63 import org
.eclipse
.ui
.PlatformUI
;
64 import org
.junit
.After
;
65 import org
.junit
.Before
;
66 import org
.junit
.BeforeClass
;
67 import org
.junit
.Test
;
70 * Tests of the latency table to extend it to custom tables, 4 steps are needed.
72 * <li>Override {@link #createSegment(long, long)}</li>
73 * <li>Override {@link #openTable()} to open the desired table view</li>
74 * <li>Override {@link #getSegStoreProvider()} to retrieve the segment store
75 * provider with the desirable aspects</li>
76 * <li>Override {@link #testTsv(String[])} to test the content of the output to
80 * Feel free to override any test and add additional tests but remember to call
81 * <code>super.test()</code> before.
83 * @author Matthew Khouzam
85 public class SegmentTableTest
{
90 * @author Matthew Khouzam
92 public static final class TestSegmentStoreTableView
extends AbstractSegmentStoreTableView
{
96 public static final String ID
= "org.eclipse.tracecompass.analysis.timing.ui.swtbot.tests.table.TestSegmentStoreTableView"; //$NON-NLS-1$
101 public TestSegmentStoreTableView() {
104 SegmentTableTest fTest
;
107 * Set the parent test
112 public void setTest(SegmentTableTest test
) {
117 protected @NonNull AbstractSegmentStoreTableViewer
createSegmentStoreViewer(@NonNull TableViewer tableViewer
) {
118 return new AbstractSegmentStoreTableViewer(tableViewer
) {
121 protected @Nullable ISegmentStoreProvider
getSegmentStoreProvider(@NonNull ITmfTrace trace
) {
122 return fTest
.getSegStoreProvider();
128 private final class SimpleSegmentStoreProvider
implements ISegmentStoreProvider
{
130 public void removeListener(@NonNull IAnalysisProgressListener listener
) {
135 public @Nullable ISegmentStore
<@NonNull ISegment
> getSegmentStore() {
140 public @NonNull Iterable
<@NonNull ISegmentAspect
> getSegmentAspects() {
141 return Collections
.emptyList();
145 public void addListener(@NonNull IAnalysisProgressListener listener
) {
150 private AbstractSegmentStoreTableView fTableView
;
151 private AbstractSegmentStoreTableViewer fTable
;
152 private ISegmentStoreProvider fSsp
;
153 private final ISegmentStore
<@NonNull ISegment
> fSs
= SegmentStoreFactory
.createSegmentStore(SegmentStoreType
.Fast
);
155 /** The Log4j logger instance. */
156 private static final Logger fLogger
= Logger
.getRootLogger();
159 * Before class, call by all subclassed
162 public static void beforeClass() {
164 SWTBotUtils
.initialize();
165 Thread
.currentThread().setName("SWTBotTest");
166 /* set up for swtbot */
167 SWTBotPreferences
.TIMEOUT
= 20000; /* 20 second timeout */
168 SWTBotPreferences
.KEYBOARD_LAYOUT
= "EN_US";
169 fLogger
.removeAllAppenders();
170 fLogger
.addAppender(new ConsoleAppender(new SimpleLayout(), ConsoleAppender
.SYSTEM_OUT
));
171 SWTWorkbenchBot bot
= new SWTWorkbenchBot();
172 final List
<SWTBotView
> openViews
= bot
.views();
173 for (SWTBotView view
: openViews
) {
174 if (view
.getTitle().equals("Welcome")) {
176 bot
.waitUntil(ConditionHelpers
.ViewIsClosed(view
));
179 /* Switch perspectives */
180 SWTBotUtils
.switchToTracingPerspective();
181 /* Finish waiting for eclipse to load */
182 WaitUtils
.waitForJobs();
186 * Opens a latency table
190 setTableView(openTable());
191 assertNotNull(getTableView());
192 setTable(getTableView().getSegmentStoreViewer());
193 assertNotNull(getTable());
194 ISegmentStoreProvider segStoreProvider
= getSegStoreProvider();
195 assertNotNull(segStoreProvider
);
196 UIThreadRunnable
.syncExec(() -> getTable().setSegmentProvider(segStoreProvider
));
203 public void finish() {
204 new SWTWorkbenchBot().viewById(getTableView().getSite().getId()).close();
208 * Create the table viewer to test
210 * @return the table viewer bot
212 protected AbstractSegmentStoreTableView
openTable() {
213 AbstractSegmentStoreTableView tableView
= getTableView();
214 if (tableView
!= null) {
218 final IWorkbench workbench
= PlatformUI
.getWorkbench();
219 vp
= UIThreadRunnable
.syncExec((Result
<IViewPart
>) () -> {
221 return workbench
.getActiveWorkbenchWindow().getActivePage().showView(TestSegmentStoreTableView
.ID
);
222 } catch (PartInitException e
) {
227 assertTrue(vp
instanceof TestSegmentStoreTableView
);
228 TestSegmentStoreTableView testSegmentStoreTableView
= (TestSegmentStoreTableView
) vp
;
229 testSegmentStoreTableView
.setTest(this);
230 fTableView
= testSegmentStoreTableView
;
236 * Create a segment of the type supported by the table under test, with the
237 * requested start and end time
243 * @return the segment
245 protected @NonNull ISegment
createSegment(long start
, long end
) {
246 return new BasicSegment(start
, end
);
250 * Test a climbing data structure.
252 * Create segments that are progressively larger and start later. Test that
253 * the "duration" column sorts well
256 public void climbTest() {
257 List
<@NonNull ISegment
> fixture
= new ArrayList
<>();
258 for (int i
= 0; i
< 100; i
++) {
259 fixture
.add(createSegment(i
, 2 * i
));
262 assertNotNull(getTable());
263 getTable().updateModel(fixture
);
264 SWTBotTable tableBot
= new SWTBotTable(getTable().getTableViewer().getTable());
265 SWTBot bot
= new SWTBot();
266 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "0", 0, 2));
267 tableBot
.header("Duration").click();
268 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "0", 0, 2));
269 tableBot
.header("Duration").click();
270 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "99", 0, 2));
274 * Test a decrementing data structure.
276 * Create segments that are progressively shorter and start sooner,
277 * effectively the inverse sorted {@link #climbTest()} datastructure. Test
278 * that the "duration" column sorts well
281 public void decrementingTest() {
282 List
<@NonNull ISegment
> fixture
= new ArrayList
<>();
283 for (int i
= 100; i
>= 0; i
--) {
284 fixture
.add(createSegment(i
, 2 * i
));
286 assertNotNull(getTable());
287 getTable().updateModel(fixture
);
288 SWTBotTable tableBot
= new SWTBotTable(getTable().getTableViewer().getTable());
289 SWTBot bot
= new SWTBot();
290 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "100", 0, 2));
291 tableBot
.header("Duration").click();
292 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "0", 0, 2));
293 tableBot
.header("Duration").click();
294 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "100", 0, 2));
300 * Test table with 2 segments. Duration sort is tested.
303 public void smallTest() {
304 List
<@NonNull ISegment
> fixture
= new ArrayList
<>();
305 for (int i
= 1; i
>= 0; i
--) {
306 fixture
.add(createSegment(i
, 2 * i
));
308 assertNotNull(getTable());
309 getTable().updateModel(fixture
);
310 SWTBotTable tableBot
= new SWTBotTable(getTable().getTableViewer().getTable());
311 SWTBot bot
= new SWTBot();
312 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "1", 0, 2));
313 tableBot
.header("Duration").click();
314 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "0", 0, 2));
315 tableBot
.header("Duration").click();
316 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "1", 0, 2));
322 * Test table with over 9000 segments. Duration sort is tested.
325 public void largeTest() {
326 final int size
= 1000000;
327 ISegment
[] fixture
= new ISegment
[size
];
328 for (int i
= 0; i
< size
; i
++) {
329 fixture
[i
] = createSegment(i
, 2 * i
);
331 assertNotNull(getTable());
332 getTable().updateModel(fixture
);
333 SWTBotTable tableBot
= new SWTBotTable(getTable().getTableViewer().getTable());
334 SWTBot bot
= new SWTBot();
335 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "0", 0, 2));
336 tableBot
.header("Duration").click();
337 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "0", 0, 2));
338 tableBot
.header("Duration").click();
339 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "999,999", 0, 2));
343 * Test table with segments that have durations spread into a random (white
344 * noise) distribution
346 * Test table with a random distribution of segments. Duration sort is
350 public void noiseTest() {
351 Random rnd
= new Random();
353 final int size
= 1000000;
354 ISegment
[] fixture
= new ISegment
[size
];
355 for (int i
= 0; i
< size
; i
++) {
356 int start
= Math
.abs(rnd
.nextInt(100000000));
357 int end
= start
+ Math
.abs(rnd
.nextInt(1000000));
358 fixture
[i
] = (createSegment(start
, end
));
360 assertNotNull(getTable());
361 getTable().updateModel(fixture
);
362 SWTBotTable tableBot
= new SWTBotTable(getTable().getTableViewer().getTable());
363 SWTBot bot
= new SWTBot();
364 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "894,633", 0, 2));
365 tableBot
.header("Duration").click();
366 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "0", 0, 2));
367 tableBot
.header("Duration").click();
368 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "999,999", 0, 2));
372 * Test table with segments that have durations spread into a gaussian
373 * (normal) distribution
375 * Test table with a gaussian distribution of segments. Duration sort is
379 public void gaussianNoiseTest() {
380 Random rnd
= new Random();
382 List
<@NonNull ISegment
> fixture
= new ArrayList
<>();
383 for (int i
= 1; i
<= 1000000; i
++) {
384 int start
= Math
.abs(rnd
.nextInt(100000000));
385 final int delta
= Math
.abs(rnd
.nextInt(1000));
386 int end
= start
+ delta
* delta
;
387 fixture
.add(createSegment(start
, end
));
389 assertNotNull(getTable());
390 getTable().updateModel(fixture
);
391 SWTBotTable tableBot
= new SWTBotTable(getTable().getTableViewer().getTable());
392 SWTBot bot
= new SWTBot();
393 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "400,689", 0, 2));
394 tableBot
.header("Duration").click();
395 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "0", 0, 2));
396 tableBot
.header("Duration").click();
397 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "998,001", 0, 2));
401 * Test creating a tsv
403 * @throws NoSuchMethodException
404 * Error creating the tsv
405 * @throws IOException
406 * no such file or the file is locked.
409 public void testWriteToTsv() throws NoSuchMethodException
, IOException
{
411 List
<@NonNull ISegment
> fixture
= new ArrayList
<>();
412 for (int i
= 1; i
<= 20; i
++) {
415 int end
= start
+ delta
* delta
;
416 fixture
.add(createSegment(start
, end
));
418 assertNotNull(getTable());
419 getTable().updateModel(fixture
);
420 SWTBotTable tableBot
= new SWTBotTable(getTable().getTableViewer().getTable());
421 SWTBot bot
= new SWTBot();
422 bot
.waitUntil(ConditionHelpers
.isTableCellFilled(tableBot
, "1", 0, 2));
423 SWTWorkbenchBot swtWorkbenchBot
= new SWTWorkbenchBot();
424 SWTBotView viewBot
= swtWorkbenchBot
.viewById(getTableView().getSite().getId());
425 String
[] lines
= extractTsv(viewBot
);
427 List
<String
> actionResult
= Arrays
.asList(lines
);
428 String absolutePath
= TmfTraceManager
.getTemporaryDirPath() + File
.separator
+ "syscallLatencyTest.testWriteToTsv.tsv";
429 TmfFileDialogFactory
.setOverrideFiles(absolutePath
);
430 SWTBotMenu menuBot
= viewBot
.viewMenu().menu("Export to TSV");
432 assertTrue(menuBot
.isEnabled());
433 assertTrue(menuBot
.isVisible());
436 try (BufferedReader br
= new BufferedReader(new FileReader(absolutePath
))) {
437 List
<String
> actual
= br
.lines().collect(Collectors
.toList());
438 assertEquals("Both reads", actionResult
, actual
);
441 new File(absolutePath
).delete();
446 private String
[] extractTsv(SWTBotView viewBot
) throws NoSuchMethodException
, SecurityException
{
447 ByteArrayOutputStream os
= new ByteArrayOutputStream();
449 Class
<@NonNull AbstractSegmentStoreTableView
> clazz
= AbstractSegmentStoreTableView
.class;
450 Method method
= clazz
.getDeclaredMethod("exportToTsv", java
.io
.OutputStream
.class);
451 method
.setAccessible(true);
452 final Exception
[] except
= new Exception
[1];
453 UIThreadRunnable
.syncExec(() -> {
455 method
.invoke(getTableView(), os
);
456 } catch (IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e
) {
460 assertNull(except
[0]);
461 @SuppressWarnings("null")
462 String
[] lines
= String
.valueOf(os
).split(System
.getProperty("line.separator"));
467 * Test the TSV generated. For each line, including the header, it should be
468 * asserted that it is equal to the expected line
471 * every entry, starting with the header
473 protected void testTsv(String
[] lines
) {
474 assertNotNull(lines
);
475 assertEquals("number of lines", 21, lines
.length
);
476 assertEquals("header", "Start Time\tEnd Time\tDuration", lines
[0]);
477 // not a straight up string compare due to time zones. Kathmandu and
478 // Eucla have 15 minute time zones.
479 assertTrue("line 1", lines
[1].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s001\\t\\d\\d:\\d\\d:00.000 000 002\\t1"));
480 assertTrue("line 2", lines
[2].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s002\\t\\d\\d:\\d\\d:00.000 000 006\\t4"));
481 assertTrue("line 3", lines
[3].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s003\\t\\d\\d:\\d\\d:00.000 000 012\\t9"));
482 assertTrue("line 4", lines
[4].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s004\\t\\d\\d:\\d\\d:00.000 000 020\\t16"));
483 assertTrue("line 5", lines
[5].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s005\\t\\d\\d:\\d\\d:00.000 000 030\\t25"));
484 assertTrue("line 6", lines
[6].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s006\\t\\d\\d:\\d\\d:00.000 000 042\\t36"));
485 assertTrue("line 7", lines
[7].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s007\\t\\d\\d:\\d\\d:00.000 000 056\\t49"));
486 assertTrue("line 8", lines
[8].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s008\\t\\d\\d:\\d\\d:00.000 000 072\\t64"));
487 assertTrue("line 9", lines
[9].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s009\\t\\d\\d:\\d\\d:00.000 000 090\\t81"));
488 assertTrue("line 10", lines
[10].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s010\\t\\d\\d:\\d\\d:00.000 000 110\\t100"));
489 assertTrue("line 11", lines
[11].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s011\\t\\d\\d:\\d\\d:00.000 000 132\\t121"));
490 assertTrue("line 12", lines
[12].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s012\\t\\d\\d:\\d\\d:00.000 000 156\\t144"));
491 assertTrue("line 13", lines
[13].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s013\\t\\d\\d:\\d\\d:00.000 000 182\\t169"));
492 assertTrue("line 14", lines
[14].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s014\\t\\d\\d:\\d\\d:00.000 000 210\\t196"));
493 assertTrue("line 15", lines
[15].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s015\\t\\d\\d:\\d\\d:00.000 000 240\\t225"));
494 assertTrue("line 16", lines
[16].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s016\\t\\d\\d:\\d\\d:00.000 000 272\\t256"));
495 assertTrue("line 17", lines
[17].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s017\\t\\d\\d:\\d\\d:00.000 000 306\\t289"));
496 assertTrue("line 18", lines
[18].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s018\\t\\d\\d:\\d\\d:00.000 000 342\\t324"));
497 assertTrue("line 19", lines
[19].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s019\\t\\d\\d:\\d\\d:00.000 000 380\\t361"));
498 assertTrue("line 20", lines
[20].matches("\\d\\d:\\d\\d:00\\.000\\s000\\s020\\t\\d\\d:\\d\\d:00.000 000 420\\t400"));
502 * Gets the table view
504 * @return the table view
506 protected AbstractSegmentStoreTableView
getTableView() {
511 * Sets the table view
516 protected void setTableView(AbstractSegmentStoreTableView tableView
) {
517 fTableView
= tableView
;
521 * Gets the table viewer
523 * @return the table viewer
525 protected AbstractSegmentStoreTableViewer
getTable() {
530 * Set the table viewer
535 protected void setTable(AbstractSegmentStoreTableViewer table
) {
540 * get the segment store provider
542 * @return the segment store provider
544 protected ISegmentStoreProvider
getSegStoreProvider() {
545 ISegmentStoreProvider ssp
= fSsp
;
547 ssp
= new SimpleSegmentStoreProvider();