1 /******************************************************************************* 2 * Copyright (c) 2008, 2017 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 15 package org.eclipse.ua.tests.help.other; 16 17 import static org.junit.Assert.assertEquals; 18 import static org.junit.Assert.fail; 19 20 import org.eclipse.help.ITopic; 21 import org.eclipse.help.internal.toc.Toc; 22 import org.eclipse.ua.tests.help.util.DocumentCreator; 23 import org.junit.Test; 24 import org.w3c.dom.Document; 25 import org.w3c.dom.Element; 26 27 public class ConcurrentTocAccess { 28 29 private boolean checkAttributes = true; 30 31 // Set enableTimeout to false for debugging 32 private boolean enableTimeout = true; 33 34 private class TocGenerator { 35 private int[] dimensions; 36 private StringBuilder result; 37 generateToc(int dimensions[])38 public String generateToc(int dimensions[]) { 39 this.dimensions = dimensions; 40 result = new StringBuilder(); 41 result.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); 42 result.append("<?NLS TYPE=\"org.eclipse.help.toc\"?>\n"); 43 result.append("<toc label=\"Test Toc\" >\n"); 44 generateTopics(0); 45 result.append("</toc>"); 46 return result.toString(); 47 } 48 generateTopics(int depth)49 private void generateTopics(int depth) { 50 if (depth >= dimensions.length) { 51 return; 52 } 53 int numChildren = dimensions[depth]; 54 for (int i = 0; i < numChildren; i++) { 55 result.append("<topic label=\"topicLabel" + i + "\" href=\"page" + i + ".html\">\n"); 56 generateTopics(depth + 1); 57 result.append("</topic>\n"); 58 } 59 } 60 } 61 62 /* 63 * Class which visits every topic in a TOC 64 */ 65 private class TocVisitor extends Thread { 66 private final Toc toc; 67 private int leafCount = -2; 68 public Exception exception; 69 TocVisitor(Toc toc)70 TocVisitor(Toc toc) { 71 this.toc = toc; 72 } 73 74 @Override run()75 public void run() { 76 try { 77 int result = traverseToc(toc); 78 setLeafCount(result); 79 } catch (Exception e) { 80 setLeafCount(-1); 81 this.exception = e; 82 } 83 } 84 setLeafCount(int leafCount)85 synchronized public void setLeafCount(int leafCount) { 86 this.leafCount = leafCount; 87 } 88 getLeafCount()89 synchronized public int getLeafCount() { 90 return leafCount; 91 } 92 } 93 94 private class BadHrefException extends RuntimeException { 95 private static final long serialVersionUID = 410319402417607912L; 96 } 97 private class BadLabelException extends RuntimeException { 98 private static final long serialVersionUID = -4581518572807575035L; 99 } 100 accessInParallel(int[] dimensions, int numberOfThreads)101 private void accessInParallel(int[] dimensions, int numberOfThreads) throws Exception { 102 Toc toc = createToc(dimensions); 103 int expectedLeafCount = computeNumberOfLeafTopics(dimensions); 104 TocVisitor[] visitors = new TocVisitor[numberOfThreads]; 105 for (int i = 0; i < numberOfThreads; i++) { 106 visitors[i] = new TocVisitor(toc); 107 } 108 for (int i = 0; i < numberOfThreads; i++) { 109 visitors[i].start(); 110 } 111 // Now wait for the threads to complete 112 boolean complete = false; 113 int iterations = 0; 114 do { 115 complete = true; 116 iterations++; 117 if (enableTimeout && iterations > 100) { 118 fail("Test did not complete within 10 seconds"); 119 } 120 for (int i = 0; i < numberOfThreads; i++) { 121 if (visitors[i].isAlive()) { 122 complete = false; 123 try { 124 Thread.sleep(100); 125 } catch (InterruptedException e) { 126 fail("Interrupted Exception"); 127 } 128 } 129 } 130 } while (!complete); 131 for (int i = 0; i < numberOfThreads; i++) { 132 if (visitors[i].exception != null) { 133 throw visitors[i].exception; 134 } 135 assertEquals(expectedLeafCount, visitors[i].getLeafCount()); 136 } 137 } 138 139 // Visit every child of a TOC and count the number of leaf topics traverseToc(Toc toc)140 private int traverseToc(Toc toc) { 141 int leafNodes = 0; 142 ITopic[] children = toc.getTopics(); 143 for (int i = 0; i < children.length; i++) { 144 leafNodes += traverseTopic(children[i], i); 145 } 146 return leafNodes; 147 } 148 computeNumberOfLeafTopics(int[] dimensions)149 private int computeNumberOfLeafTopics(int[] dimensions) { 150 int expectedLeaves = 1; 151 for (int dimension : dimensions) { 152 expectedLeaves = expectedLeaves * dimension; 153 } 154 return expectedLeaves; 155 } 156 traverseTopic(ITopic topic, int index)157 private int traverseTopic(ITopic topic, int index) { 158 if (checkAttributes) { 159 String expectedLabel = "topicLabel" + index; 160 String expectedHref = "page" + index + ".html"; 161 String label = topic.getLabel(); 162 String href = topic.getHref(); 163 if (!label.equals(expectedLabel)) { 164 throw new BadLabelException(); 165 } 166 if (!href.equals(expectedHref)) { 167 throw new BadHrefException(); 168 } 169 } 170 ITopic[] children = topic.getSubtopics(); 171 if (children.length == 0) { 172 return 1; 173 } 174 int leafNodes = 0; 175 for (int i = 0; i < children.length; i++) { 176 leafNodes += traverseTopic(children[i], i); 177 } 178 return leafNodes; 179 } 180 createToc(int[] dimensions)181 private Toc createToc(int[] dimensions) { 182 String tocSource = new TocGenerator().generateToc(dimensions); 183 Toc toc; 184 Document doc; 185 try { 186 doc = DocumentCreator.createDocument(tocSource); 187 } catch (Exception e) { 188 fail("Exception creating TOC"); 189 doc = null; 190 } 191 Element tocElement = (Element) doc.getElementsByTagName("toc").item(0); 192 toc = new Toc(tocElement); 193 return toc; 194 } 195 196 @Test testFlatTocSize5()197 public void testFlatTocSize5() throws Exception { 198 int[] dimensions = {5}; 199 accessInParallel(dimensions, 2); 200 } 201 202 @Test testFlatTocSize1000()203 public void testFlatTocSize1000() throws Exception { 204 int[] dimensions = {1000}; 205 accessInParallel(dimensions, 2); 206 } 207 208 @Test testFlatTocSize10000()209 public void testFlatTocSize10000() throws Exception { 210 int[] dimensions = {10000}; 211 accessInParallel(dimensions, 2); 212 } 213 214 @Test testTwoLevelToc()215 public void testTwoLevelToc() throws Exception { 216 int[] dimensions = {50, 50}; 217 accessInParallel(dimensions, 2); 218 } 219 220 @Test testDeepToc()221 public void testDeepToc() throws Exception { 222 int[] dimensions = {2,2,2,2,2,2,2,2,2,2,2}; 223 accessInParallel(dimensions, 2); 224 } 225 226 @Test testFlatTocManyThreads()227 public void testFlatTocManyThreads() throws Exception { 228 int[] dimensions = {100}; 229 accessInParallel(dimensions, 100); 230 } 231 232 } 233