1 /*******************************************************************************
2 * Copyright (c) 2015 École Polytechnique de Montréal
4 * All rights reserved. This program and the accompanying materials are
5 * made 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 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.statesystem
.core
.tests
.backend
;
12 import static org
.junit
.Assert
.assertEquals
;
13 import static org
.junit
.Assert
.assertNotNull
;
14 import static org
.junit
.Assert
.fail
;
17 import java
.io
.IOException
;
18 import java
.nio
.channels
.ClosedChannelException
;
19 import java
.util
.List
;
21 import org
.eclipse
.jdt
.annotation
.NonNull
;
22 import org
.eclipse
.tracecompass
.internal
.statesystem
.core
.backend
.historytree
.HTConfig
;
23 import org
.eclipse
.tracecompass
.internal
.statesystem
.core
.backend
.historytree
.HTInterval
;
24 import org
.eclipse
.tracecompass
.internal
.statesystem
.core
.backend
.historytree
.HTNode
;
25 import org
.eclipse
.tracecompass
.internal
.statesystem
.core
.backend
.historytree
.HistoryTree
;
26 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
27 import org
.eclipse
.tracecompass
.statesystem
.core
.tests
.stubs
.backend
.HistoryTreeStub
;
28 import org
.junit
.After
;
29 import org
.junit
.Before
;
30 import org
.junit
.Test
;
33 * Tests the history tree
35 * @author Geneviève Bastien
37 public class HistoryTreeTest
{
39 /* Minimal allowed blocksize */
40 private static final int BLOCK_SIZE
= HistoryTree
.TREE_HEADER_SIZE
;
41 /* The extra size used by long and double values */
42 private static final int LONG_DOUBLE_SIZE
= 8;
43 /* The number of extra characters to store a string interval */
44 private static final int STRING_PADDING
= 2;
46 /* String with 23 characters, interval in file will be 25 bytes long */
47 private static final String TEST_STRING
= "abcdefghifklmnopqrstuvw";
48 private static final @NonNull TmfStateValue STRING_VALUE
= TmfStateValue
.newValueString(TEST_STRING
);
49 private static final @NonNull TmfStateValue LONG_VALUE
= TmfStateValue
.newValueLong(10L);
50 private static final @NonNull TmfStateValue INT_VALUE
= TmfStateValue
.newValueInt(1);
52 private File fTempFile
;
55 * Create the temporary file for this history tree
58 public void setupTest() {
60 fTempFile
= File
.createTempFile("tmpStateSystem", null);
61 } catch (IOException e
) {
67 * Delete the temporary history tree file after the test
70 public void cleanup() {
75 * Setup a history tree.
78 * The max number of children per node in the tree (tree config
81 private HistoryTreeStub
setupSmallTree(int maxChildren
) {
82 HistoryTreeStub ht
= null;
84 File newFile
= fTempFile
;
85 assertNotNull(newFile
);
86 HTConfig config
= new HTConfig(newFile
,
88 maxChildren
, /* Number of children */
89 1, /* Provider version */
91 ht
= new HistoryTreeStub(config
);
93 } catch (IOException e
) {
102 * Setup a history tree with config MAX_CHILDREN = 3.
104 private HistoryTreeStub
setupSmallTree() {
105 return setupSmallTree(3);
108 private static long fillValues(HistoryTree ht
, @NonNull TmfStateValue value
, int nbValues
, long start
) {
109 for (int i
= 0; i
< nbValues
; i
++) {
110 ht
.insertInterval(new HTInterval(start
+ i
, start
+ i
+ 1, 1, value
));
112 return start
+ nbValues
;
116 * Insert intervals in the tree to fill the current leaf node to capacity,
117 * without exceeding it.
119 * This guarantees that the following insertion will create new nodes.
122 * The history tree in which to insert
123 * @return Start time of the current leaf node. Future insertions should be
124 * greater than or equal to this to make sure the intervals go in
127 private static long fillNextLeafNode(HistoryTreeStub ht
, long leafNodeStart
) {
128 int prevCount
= ht
.getNodeCount();
129 int prevDepth
= ht
.getDepth();
131 /* Fill the following leaf node */
132 HTNode node
= ht
.getLatestLeaf();
133 int nodeFreeSpace
= node
.getNodeFreeSpace();
134 int nbIntervals
= nodeFreeSpace
/ (HTInterval
.DATA_ENTRY_SIZE
+ TEST_STRING
.length() + STRING_PADDING
);
135 long ret
= fillValues(ht
, STRING_VALUE
, nbIntervals
, leafNodeStart
);
137 /* Make sure we haven't changed the depth or node count */
138 assertEquals(prevCount
, ht
.getNodeCount());
139 assertEquals(prevDepth
, ht
.getDepth());
145 * Test that nodes are filled
147 * It fills nodes with sequential intervals from one attribute only, so that
148 * leafs should be filled.
151 public void testSequentialFill() {
152 HistoryTreeStub ht
= setupSmallTree();
154 HTNode node
= ht
.getLatestLeaf();
155 assertEquals(0, node
.getNodeUsagePercent());
157 /* Add null intervals up to ~10% */
158 int nodeFreeSpace
= node
.getNodeFreeSpace();
159 int nbIntervals
= nodeFreeSpace
/ 10 / HTInterval
.DATA_ENTRY_SIZE
;
160 long start
= fillValues(ht
, TmfStateValue
.nullValue(), nbIntervals
, 1);
161 assertEquals(nodeFreeSpace
- nbIntervals
* HTInterval
.DATA_ENTRY_SIZE
, node
.getNodeFreeSpace());
163 /* Add integer intervals up to ~20% */
164 nodeFreeSpace
= node
.getNodeFreeSpace();
165 nbIntervals
= nodeFreeSpace
/ 10 / HTInterval
.DATA_ENTRY_SIZE
;
166 start
= fillValues(ht
, INT_VALUE
, nbIntervals
, start
);
167 assertEquals(nodeFreeSpace
- nbIntervals
* HTInterval
.DATA_ENTRY_SIZE
, node
.getNodeFreeSpace());
169 /* Add long intervals up to ~30% */
170 nodeFreeSpace
= node
.getNodeFreeSpace();
171 nbIntervals
= nodeFreeSpace
/ 10 / (HTInterval
.DATA_ENTRY_SIZE
+ LONG_DOUBLE_SIZE
);
172 start
= fillValues(ht
, LONG_VALUE
, nbIntervals
, start
);
173 assertEquals(nodeFreeSpace
- nbIntervals
* (HTInterval
.DATA_ENTRY_SIZE
+ LONG_DOUBLE_SIZE
), node
.getNodeFreeSpace());
175 /* Add string intervals up to ~40% */
176 nodeFreeSpace
= node
.getNodeFreeSpace();
177 nbIntervals
= nodeFreeSpace
/ 10 / (HTInterval
.DATA_ENTRY_SIZE
+ TEST_STRING
.length() + STRING_PADDING
);
178 start
= fillValues(ht
, STRING_VALUE
, nbIntervals
, start
);
179 assertEquals(nodeFreeSpace
- nbIntervals
* (HTInterval
.DATA_ENTRY_SIZE
+ TEST_STRING
.length() + STRING_PADDING
), node
.getNodeFreeSpace());
184 * Test the addition of new nodes to the tree and make sure the tree is
185 * built with the right structure
188 public void testDepth() {
189 HistoryTreeStub ht
= setupSmallTree();
191 /* Fill a first node */
192 HTNode node
= ht
.getLatestLeaf();
193 int nodeFreeSpace
= node
.getNodeFreeSpace();
194 int nbIntervals
= nodeFreeSpace
/ (HTInterval
.DATA_ENTRY_SIZE
+ TEST_STRING
.length() + STRING_PADDING
);
195 long start
= fillValues(ht
, STRING_VALUE
, nbIntervals
, 1);
197 /* Add intervals that should add a sibling to the node */
198 assertEquals(1, ht
.getNodeCount());
199 assertEquals(1, ht
.getDepth());
200 start
= fillValues(ht
, STRING_VALUE
, 1, start
);
201 assertEquals(3, ht
.getNodeCount());
202 assertEquals(2, ht
.getDepth());
204 /* Fill the latest leaf node (2nd child) */
205 node
= ht
.getLatestLeaf();
206 nodeFreeSpace
= node
.getNodeFreeSpace();
207 nbIntervals
= nodeFreeSpace
/ (HTInterval
.DATA_ENTRY_SIZE
+ TEST_STRING
.length() + STRING_PADDING
);
208 start
= fillValues(ht
, STRING_VALUE
, nbIntervals
, start
);
211 * Add an interval that should add another sibling to the previous nodes
213 start
= fillValues(ht
, STRING_VALUE
, 1, start
);
214 assertEquals(4, ht
.getNodeCount());
215 assertEquals(2, ht
.getDepth());
217 /* Fill the latest leaf node (3rd and last child) */
218 node
= ht
.getLatestLeaf();
219 nodeFreeSpace
= node
.getNodeFreeSpace();
220 nbIntervals
= nodeFreeSpace
/ (HTInterval
.DATA_ENTRY_SIZE
+ TEST_STRING
.length() + STRING_PADDING
);
221 start
= fillValues(ht
, STRING_VALUE
, nbIntervals
, start
);
223 /* The new node created here should generate a new branch */
224 start
= fillValues(ht
, STRING_VALUE
, 1, start
);
225 assertEquals(7, ht
.getNodeCount());
226 assertEquals(3, ht
.getDepth());
230 * Make sure the node sequence numbers and parent pointers are set correctly
231 * when new nodes are created.
234 * We are building a tree whose node sequence numbers will look like this at
247 * However while building, the parent pointers may be different.
250 * @throws ClosedChannelException
254 public void testNodeSequenceNumbers() throws ClosedChannelException
{
255 /* Represents the start time of the current leaf node */
258 HistoryTreeStub ht
= setupSmallTree(2);
259 start
= fillNextLeafNode(ht
, start
);
261 List
<HTNode
> branch
= ht
.getLatestBranch();
262 assertEquals(1, branch
.size());
263 assertEquals( 0, branch
.get(0).getSequenceNumber());
264 assertEquals(-1, branch
.get(0).getParentSequenceNumber());
266 /* Create a new branch */
267 start
= fillValues(ht
, STRING_VALUE
, 1, start
);
268 start
= fillNextLeafNode(ht
, start
);
269 assertEquals(3, ht
.getNodeCount());
270 assertEquals(2, ht
.getDepth());
272 /* Make sure the first node's parent was updated */
273 HTNode node
= ht
.readNode(0);
274 assertEquals(0, node
.getSequenceNumber());
275 assertEquals(1, node
.getParentSequenceNumber());
277 /* Make sure the new branch is alright */
278 branch
= ht
.getLatestBranch();
279 assertEquals(2, branch
.size());
280 assertEquals( 1, branch
.get(0).getSequenceNumber());
281 assertEquals(-1, branch
.get(0).getParentSequenceNumber());
282 assertEquals( 2, branch
.get(1).getSequenceNumber());
283 assertEquals( 1, branch
.get(1).getParentSequenceNumber());
285 /* Create a third branch */
286 start
= fillValues(ht
, STRING_VALUE
, 1, start
);
287 start
= fillNextLeafNode(ht
, start
);
288 assertEquals(6, ht
.getNodeCount());
289 assertEquals(3, ht
.getDepth());
291 /* Make sure all previous nodes are still correct */
292 node
= ht
.readNode(0);
293 assertEquals(0, node
.getSequenceNumber());
294 assertEquals(1, node
.getParentSequenceNumber());
295 node
= ht
.readNode(1);
296 assertEquals(1, node
.getSequenceNumber());
297 assertEquals(3, node
.getParentSequenceNumber());
298 node
= ht
.readNode(2);
299 assertEquals(2, node
.getSequenceNumber());
300 assertEquals(1, node
.getParentSequenceNumber());
302 /* Verify the contents of the new latest branch */
303 branch
= ht
.getLatestBranch();
304 assertEquals(3, branch
.size());
305 assertEquals( 3, branch
.get(0).getSequenceNumber());
306 assertEquals(-1, branch
.get(0).getParentSequenceNumber());
307 assertEquals( 4, branch
.get(1).getSequenceNumber());
308 assertEquals( 3, branch
.get(1).getParentSequenceNumber());
309 assertEquals( 5, branch
.get(2).getSequenceNumber());
310 assertEquals( 4, branch
.get(2).getParentSequenceNumber());