1 /******************************************************************************* 2 * Copyright (c) 2005, 2015 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 * Alexander Kurtakov <akurtako@redhat.com> - bug 458490 14 *******************************************************************************/ 15 package org.eclipse.core.tests.runtime.perf; 16 17 import java.io.*; 18 import java.net.MalformedURLException; 19 import java.net.URL; 20 import junit.framework.Test; 21 import junit.framework.TestSuite; 22 import org.eclipse.core.internal.content.*; 23 import org.eclipse.core.runtime.IPath; 24 import org.eclipse.core.runtime.Platform; 25 import org.eclipse.core.runtime.content.*; 26 import org.eclipse.core.runtime.preferences.InstanceScope; 27 import org.eclipse.core.tests.harness.*; 28 import org.eclipse.core.tests.runtime.RuntimeTest; 29 import org.eclipse.core.tests.runtime.RuntimeTestsPlugin; 30 import org.eclipse.core.tests.session.PerformanceSessionTestSuite; 31 import org.eclipse.core.tests.session.SessionTestSuite; 32 import org.osgi.framework.Bundle; 33 import org.osgi.framework.BundleException; 34 35 public class ContentTypePerformanceTest extends RuntimeTest { 36 37 private final static String CONTENT_TYPE_PREF_NODE = Platform.PI_RUNTIME + IPath.SEPARATOR + "content-types"; //$NON-NLS-1$ 38 private static final String DEFAULT_NAME = "file_" + ContentTypePerformanceTest.class.getName(); 39 private static final int ELEMENTS_PER_LEVEL = 4; 40 private static final int NUMBER_OF_LEVELS = 4; 41 private static final String TEST_DATA_ID = "org.eclipse.core.tests.runtime.contenttype.perf.testdata"; 42 private static final int TOTAL_NUMBER_OF_ELEMENTS = computeTotalTypes(NUMBER_OF_LEVELS, ELEMENTS_PER_LEVEL); 43 computeTotalTypes(int levels, int elementsPerLevel)44 private static int computeTotalTypes(int levels, int elementsPerLevel) { 45 double sum = 0; 46 for (int i = 0; i <= levels; i++) { 47 sum += Math.pow(elementsPerLevel, i); 48 } 49 return (int) sum; 50 } 51 createContentType(Writer writer, int number, String baseTypeId)52 private static String createContentType(Writer writer, int number, String baseTypeId) throws IOException { 53 String id = "performance" + number; 54 String definition = generateContentType(number, id, baseTypeId, new String[] {DEFAULT_NAME}, null); 55 writer.write(definition); 56 writer.write(System.lineSeparator()); 57 return id; 58 } 59 createContentTypes(Writer writer, String baseTypeId, int created, int numberOfLevels, int nodesPerLevel)60 public static int createContentTypes(Writer writer, String baseTypeId, int created, int numberOfLevels, int nodesPerLevel) throws IOException { 61 if (numberOfLevels == 0) { 62 return 0; 63 } 64 int local = nodesPerLevel; 65 for (int i = 0; i < nodesPerLevel; i++) { 66 String id = createContentType(writer, created + i, baseTypeId); 67 local += createContentTypes(writer, id, created + local, numberOfLevels - 1, nodesPerLevel); 68 } 69 return local; 70 } 71 generateContentType(int number, String id, String baseTypeId, String[] fileNames, String[] fileExtensions)72 private static String generateContentType(int number, String id, String baseTypeId, String[] fileNames, String[] fileExtensions) { 73 StringBuilder result = new StringBuilder(); 74 result.append("<content-type id=\""); 75 result.append(id); 76 result.append("\" name=\""); 77 result.append(id); 78 result.append("\" "); 79 if (baseTypeId != null) { 80 result.append("base-type=\""); 81 result.append(baseTypeId); 82 result.append("\" "); 83 } 84 String fileNameList = Util.toListString(fileNames); 85 if (fileNameList != null) { 86 result.append("file-names=\""); 87 result.append(fileNameList); 88 result.append("\" "); 89 } 90 String fileExtensionsList = Util.toListString(fileExtensions); 91 if (fileExtensions != null && fileExtensions.length > 0) { 92 result.append("file-extensions=\""); 93 result.append(fileExtensionsList); 94 result.append("\" "); 95 } 96 result.append("describer=\""); 97 result.append(BinarySignatureDescriber.class.getName()); 98 result.append(":"); 99 result.append(getSignatureString(number)); 100 result.append("\"/>"); 101 return result.toString(); 102 } 103 getContentTypeId(int i)104 private static String getContentTypeId(int i) { 105 return TEST_DATA_ID + ".performance" + i; 106 } 107 getSignature(int number)108 private static byte[] getSignature(int number) { 109 byte[] result = new byte[4]; 110 for (int i = 0; i < result.length; i++) { 111 result[i] = (byte) ((number >> (i * 8)) & 0xFFL); 112 } 113 return result; 114 } 115 getSignatureString(int number)116 private static String getSignatureString(int number) { 117 byte[] signature = getSignature(number); 118 StringBuilder result = new StringBuilder(signature.length * 3 - 1); 119 for (byte element : signature) { 120 result.append(Integer.toHexString(0xFF & element)); 121 result.append(' '); 122 } 123 result.deleteCharAt(result.length() - 1); 124 return result.toString(); 125 } 126 suite()127 public static Test suite() { 128 TestSuite suite = new TestSuite(ContentTypePerformanceTest.class.getName()); 129 130 // suite.addTest(new ContentTypePerformanceTest("testDoSetUp")); 131 // suite.addTest(new ContentTypePerformanceTest("testContentMatching")); 132 // suite.addTest(new ContentTypePerformanceTest("testContentTXTMatching")); 133 // suite.addTest(new ContentTypePerformanceTest("testContentXMLMatching")); 134 // suite.addTest(new ContentTypePerformanceTest("testDoTearDown")); 135 136 SessionTestSuite setUp = new SessionTestSuite(PI_RUNTIME_TESTS, "testDoSetUp"); 137 setUp.addTest(new ContentTypePerformanceTest("testDoSetUp")); 138 suite.addTest(setUp); 139 140 TestSuite singleRun = new PerformanceSessionTestSuite(PI_RUNTIME_TESTS, 1, "singleSessionTests"); 141 singleRun.addTest(new ContentTypePerformanceTest("testContentMatching")); 142 singleRun.addTest(new ContentTypePerformanceTest("testNameMatching")); 143 singleRun.addTest(new ContentTypePerformanceTest("testIsKindOf")); 144 suite.addTest(singleRun); 145 146 TestSuite loadCatalog = new PerformanceSessionTestSuite(PI_RUNTIME_TESTS, 10, "multipleSessionTests"); 147 loadCatalog.addTest(new ContentTypePerformanceTest("testLoadCatalog")); 148 suite.addTest(loadCatalog); 149 150 TestSuite tearDown = new SessionTestSuite(PI_RUNTIME_TESTS, "testDoTearDown"); 151 tearDown.addTest(new ContentTypePerformanceTest("testDoTearDown")); 152 suite.addTest(tearDown); 153 return suite; 154 } 155 ContentTypePerformanceTest(String name)156 public ContentTypePerformanceTest(String name) { 157 super(name); 158 } 159 countTestContentTypes(IContentType[] all)160 private int countTestContentTypes(IContentType[] all) { 161 String namespace = TEST_DATA_ID + '.'; 162 int count = 0; 163 for (IContentType element : all) { 164 if (element.getId().startsWith(namespace)) { 165 count++; 166 } 167 } 168 return count; 169 } 170 getExtraPluginLocation()171 public IPath getExtraPluginLocation() { 172 return getTempDir().append(TEST_DATA_ID); 173 } 174 installContentTypes(String tag, int numberOfLevels, int nodesPerLevel)175 private Bundle installContentTypes(String tag, int numberOfLevels, int nodesPerLevel) { 176 TestRegistryChangeListener listener = new TestRegistryChangeListener(Platform.PI_RUNTIME, ContentTypeBuilder.PT_CONTENTTYPES, null, null); 177 Bundle installed = null; 178 listener.register(); 179 try { 180 IPath pluginLocation = getExtraPluginLocation(); 181 assertTrue(pluginLocation.toFile().mkdirs()); 182 assertTrue(pluginLocation.append("META-INF").toFile().mkdirs()); 183 URL installURL = null; 184 try { 185 installURL = pluginLocation.toFile().toURI().toURL(); 186 } catch (MalformedURLException e) { 187 fail(tag + ".0.5", e); 188 } 189 String eol = System.lineSeparator(); 190 try (Writer writer = new BufferedWriter(new FileWriter(pluginLocation.append("plugin.xml").toFile()), 191 0x10000)) { 192 writer.write("<plugin>"); 193 writer.write(eol); 194 writer.write("<extension point=\"org.eclipse.core.runtime.contentTypes\">"); 195 writer.write(eol); 196 String root = createContentType(writer, 0, null); 197 createContentTypes(writer, root, 1, numberOfLevels, nodesPerLevel); 198 writer.write("</extension></plugin>"); 199 } catch (IOException e) { 200 fail(tag + ".1.0", e); 201 } 202 try (Writer writer = new BufferedWriter( 203 new FileWriter(pluginLocation.append("META-INF").append("MANIFEST.MF").toFile()), 204 0x10000)) { 205 writer.write("Manifest-Version: 1.0"); 206 writer.write(eol); 207 writer.write("Bundle-ManifestVersion: 2"); 208 writer.write(eol); 209 writer.write("Bundle-Name: Content Type Performance Test Data"); 210 writer.write(eol); 211 writer.write("Bundle-SymbolicName: " + TEST_DATA_ID + "; singleton:=true"); 212 writer.write(eol); 213 writer.write("Bundle-Version: 1.0\n"); 214 writer.write("Require-Bundle: " + PI_RUNTIME_TESTS); 215 writer.write(eol); 216 } catch (IOException e) { 217 fail(tag + ".2.0", e); 218 } 219 try { 220 installed = RuntimeTestsPlugin.getContext().installBundle(installURL.toExternalForm()); 221 } catch (BundleException e) { 222 fail(tag + ".3.0", e); 223 } 224 BundleTestingHelper.refreshPackages(RuntimeTestsPlugin.getContext(), new Bundle[] {installed}); 225 assertTrue(tag + ".4.0", listener.eventReceived(10000)); 226 } finally { 227 listener.unregister(); 228 } 229 return installed; 230 } 231 232 /** 233 * Warms up the content type registry. 234 */ loadChildren()235 private void loadChildren() { 236 final IContentTypeManager manager = Platform.getContentTypeManager(); 237 IContentType[] allTypes = manager.getAllContentTypes(); 238 for (IContentType allType : allTypes) { 239 String[] fileNames = allType.getFileSpecs(IContentType.IGNORE_USER_DEFINED | IContentType.FILE_NAME_SPEC); 240 for (String fileName : fileNames) { 241 manager.findContentTypeFor(fileName); 242 } 243 String[] fileExtensions = allType.getFileSpecs(IContentType.IGNORE_USER_DEFINED | IContentType.FILE_EXTENSION_SPEC); 244 for (String fileExtension : fileExtensions) { 245 manager.findContentTypeFor("anyname." + fileExtension); 246 } 247 } 248 } 249 250 /** 251 * Returns a loaded content type manager. Except for load time tests, this method should 252 * be called outside the scope of performance monitoring. 253 */ loadContentTypeManager()254 private IContentTypeManager loadContentTypeManager() { 255 // any cheap interaction that causes the catalog to be built 256 Platform.getContentTypeManager().getContentType(IContentTypeManager.CT_TEXT); 257 return Platform.getContentTypeManager(); 258 } 259 260 /** Forces all describers to be loaded.*/ loadDescribers()261 private void loadDescribers() { 262 final IContentTypeManager manager = Platform.getContentTypeManager(); 263 IContentType[] allTypes = manager.getAllContentTypes(); 264 for (IContentType allType : allTypes) { 265 ((ContentTypeHandler) allType).getTarget().getDescriber(); 266 } 267 } 268 loadPreferences()269 private void loadPreferences() { 270 InstanceScope.INSTANCE.getNode(CONTENT_TYPE_PREF_NODE); 271 } 272 273 /** Tests how much the size of the catalog affects the performance of content type matching by content analysis */ testContentMatching()274 public void testContentMatching() { 275 loadPreferences(); 276 // warm up content type registry 277 final IContentTypeManager manager = loadContentTypeManager(); 278 loadDescribers(); 279 loadChildren(); 280 new PerformanceTestRunner() { 281 @Override 282 protected void test() { 283 try { 284 for (int i = 0; i < TOTAL_NUMBER_OF_ELEMENTS; i++) { 285 String id = getContentTypeId(i); 286 IContentType[] result = manager.findContentTypesFor(new ByteArrayInputStream(getSignature(i)), DEFAULT_NAME); 287 assertEquals("1.0." + i, 1, result.length); 288 assertEquals("1.1." + i, id, result[0].getId()); 289 } 290 } catch (IOException e) { 291 fail("2.0", e); 292 } 293 } 294 }.run(this, 10, 2); 295 } 296 297 @Override setUp()298 protected void setUp() throws Exception { 299 super.setUp(); 300 if (getName().equals("testDoSetUp") || getName().equals("testDoTearDown")) { 301 return; 302 } 303 Bundle installed = null; 304 try { 305 installed = RuntimeTestsPlugin.getContext() 306 .installBundle(getExtraPluginLocation().toFile().toURI().toURL().toExternalForm()); 307 } catch (BundleException e) { 308 fail("1.0", e); 309 } catch (MalformedURLException e) { 310 fail("2.0", e); 311 } 312 BundleTestingHelper.refreshPackages(RuntimeTestsPlugin.getContext(), new Bundle[] { installed }); 313 } 314 testDoSetUp()315 public void testDoSetUp() { 316 installContentTypes("1.0", NUMBER_OF_LEVELS, ELEMENTS_PER_LEVEL); 317 } 318 testDoTearDown()319 public void testDoTearDown() { 320 ensureDoesNotExistInFileSystem(getExtraPluginLocation().toFile()); 321 } 322 testIsKindOf()323 public void testIsKindOf() { 324 // warm up preference service 325 loadPreferences(); 326 // warm up content type registry 327 final IContentTypeManager manager = loadContentTypeManager(); 328 loadChildren(); 329 final IContentType root = manager.getContentType(getContentTypeId(0)); 330 assertNotNull("2.0", root); 331 new PerformanceTestRunner() { 332 @Override 333 protected void test() { 334 for (int i = 0; i < TOTAL_NUMBER_OF_ELEMENTS; i++) { 335 IContentType type = manager.getContentType(getContentTypeId(i)); 336 assertNotNull("3.0." + i, type); 337 assertTrue("3.1." + i, type.isKindOf(root)); 338 } 339 } 340 }.run(this, 10, 500); 341 } 342 343 /** 344 * This test is intended for running as a session test. 345 */ testLoadCatalog()346 public void testLoadCatalog() { 347 // warm up preference service 348 loadPreferences(); 349 PerformanceTestRunner runner = new PerformanceTestRunner() { 350 @Override 351 protected void test() { 352 // any interation that will cause the registry to be loaded 353 Platform.getContentTypeManager().getContentType(IContentTypeManager.CT_TEXT); 354 } 355 }; 356 runner.run(this, 1, /* must run only once - the suite controls how many sessions are run */1); 357 // sanity check to make sure we are running with good data 358 assertEquals("missing content types", TOTAL_NUMBER_OF_ELEMENTS, countTestContentTypes(Platform.getContentTypeManager().getAllContentTypes())); 359 } 360 361 /** Tests how much the size of the catalog affects the performance of content type matching by name */ testNameMatching()362 public void testNameMatching() { 363 // warm up preference service 364 loadPreferences(); 365 // warm up content type registry 366 final IContentTypeManager manager = loadContentTypeManager(); 367 loadDescribers(); 368 loadChildren(); 369 new PerformanceTestRunner() { 370 @Override 371 protected void test() { 372 IContentType[] associated = manager.findContentTypesFor("foo.txt"); 373 // we know at least the etxt content type should be here 374 assertTrue("2.0", associated.length >= 1); 375 // and it is supposed to be the first one (since it is at the root) 376 assertEquals("2.1", IContentTypeManager.CT_TEXT, associated[0].getId()); 377 } 378 }.run(this, 10, 200000); 379 } 380 }