diff --git a/platform/platform-impl/src/com/intellij/internal/IconsLoadTime.java b/platform/platform-impl/src/com/intellij/internal/IconsLoadTime.java index b1d8ac000f27..d7bbeda2a7e8 100644 --- a/platform/platform-impl/src/com/intellij/internal/IconsLoadTime.java +++ b/platform/platform-impl/src/com/intellij/internal/IconsLoadTime.java @@ -14,6 +14,7 @@ import com.intellij.util.ImageLoader; import com.intellij.util.ImageLoader.ImageDesc.Type; import com.intellij.util.ImageLoader.LoadFunction; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.awt.*; import java.io.IOException; @@ -46,6 +47,36 @@ public class IconsLoadTime extends DumbAwareAction { } } + public static class StatData { + public final Type type; + public final boolean startup; + public final int count; + + // millis + public final float totalTime; + public final float averageTime; + public final float medianTime; + + private StatData(Type type, boolean startup, int totalTime, int averageTime, int medianTime, int count) { + this.type = type; + this.startup = startup; + this.count = count; + + this.totalTime = totalTime / 1000000f; + this.averageTime = averageTime / 1000000f; + this.medianTime = medianTime / 1000000f; + } + + @Override + public String toString() { + return type + " load time: " + + (startup ? "ide_startup=" : "total=") + String.format("%.02fms", totalTime) + + ", average=" + String.format("%.02fms", averageTime) + + ", median=" + String.format("%.02fms", medianTime) + + "; number of icons: " + count; + } + } + @Override public void actionPerformed(AnActionEvent e) { log(false); @@ -57,21 +88,20 @@ public class IconsLoadTime extends DumbAwareAction { } private static void log(boolean measureStartupLoad, Type type) { + StatData data = getStatData(measureStartupLoad, type); + if (data != null) LOG.info(data.toString()); + } + + public static @Nullable StatData getStatData(boolean measureStartupLoad, Type type) { List stats = getStats(type); - if (stats == null || stats.isEmpty()) return; + if (stats == null || stats.isEmpty()) return null; int size = stats.size(); - long sum = stats.stream().mapToInt(Integer::intValue).sum(); - long average = sum / size; - long median = (size % 2 == 0) ? stats.get(size / 2 - 1) + stats.get(size / 2) : stats.get(size / 2); + int sum = stats.stream().mapToInt(Integer::intValue).sum(); + int average = sum / size; + int median = (size % 2 == 0) ? stats.get(size / 2 - 1) + stats.get(size / 2) : stats.get(size / 2); - Function ms = (nano) -> String.format("%.02fms", nano / 1000000f); - - LOG.info(type + " load time: " + - (measureStartupLoad ? "ide_startup=" : "total=") + ms.apply(sum) + - ", average=" + ms.apply(average) + - ", median=" + ms.apply(median) + - "; number of icons: " + size); + return new StatData(type, measureStartupLoad, sum, average, median, size); } private static Image measure(LoadFunction func, Type type) throws IOException { diff --git a/platform/platform-tests/testData/icons/icons_list.txt b/platform/platform-tests/testData/icons/icons_list.txt new file mode 100644 index 000000000000..dcb295fc445c --- /dev/null +++ b/platform/platform-tests/testData/icons/icons_list.txt @@ -0,0 +1,91 @@ +platform/icons/src/debugger/threads.png +platform/icons/src/fileTypes/any_type.png +platform/icons/src/fileTypes/archive.png +platform/icons/src/fileTypes/as.png +platform/icons/src/fileTypes/aspectj.png +platform/icons/src/fileTypes/config.png +platform/icons/src/fileTypes/css.png +platform/icons/src/fileTypes/custom.png +platform/icons/src/fileTypes/diagram.png +platform/icons/src/fileTypes/dtd.png +platform/icons/src/fileTypes/facelets.png +platform/icons/src/fileTypes/facesConfig.png +platform/icons/src/fileTypes/htaccess.png +platform/icons/src/fileTypes/html.png +platform/icons/src/fileTypes/idl.png +platform/icons/src/fileTypes/java.png +platform/icons/src/fileTypes/javaClass.png +platform/icons/src/fileTypes/javaOutsideSource.png +platform/icons/src/fileTypes/javaScript.png +platform/icons/src/fileTypes/json.png +platform/icons/src/fileTypes/jsp.png +platform/icons/src/fileTypes/jspx.png +platform/icons/src/fileTypes/manifest.png +platform/icons/src/fileTypes/properties.png +platform/icons/src/fileTypes/text.png +platform/icons/src/fileTypes/typeScript.png +platform/icons/src/fileTypes/uiForm.png +platform/icons/src/fileTypes/unknown.png +platform/icons/src/fileTypes/wsdlFile.png +platform/icons/src/fileTypes/xhtml.png +platform/icons/src/fileTypes/xml.png +platform/icons/src/fileTypes/xsdFile.png +platform/icons/src/modules/editFolder.png +platform/icons/src/modules/excludedGeneratedRoot.png +platform/icons/src/modules/excludeRoot.png +platform/icons/src/modules/generatedFolder.png +platform/icons/src/modules/generatedSourceRoot.png +platform/icons/src/modules/generatedTestRoot.png +platform/icons/src/modules/library.png +platform/icons/src/modules/modulesNode.png +platform/icons/src/modules/output.png +platform/icons/src/modules/ResourcesRoot.png +platform/icons/src/modules/sourceFolder.png +platform/icons/src/modules/sourceRoot.png +platform/icons/src/modules/sources.png +platform/icons/src/modules/testResourcesRoot.png +platform/icons/src/modules/testRoot.png +platform/icons/src/modules/testSourceFolder.png +platform/icons/src/modules/unmarkWebroot.png +platform/icons/src/modules/webRoot.png +platform/icons/src/nodes/abstractClass.png +platform/icons/src/nodes/abstractException.png +platform/icons/src/nodes/abstractMethod.png +platform/icons/src/nodes/annotationtype.png +platform/icons/src/nodes/anonymousClass.png +platform/icons/src/nodes/class.png +platform/icons/src/nodes/classInitializer.png +platform/icons/src/nodes/enum.png +platform/icons/src/nodes/exceptionClass.png +platform/icons/src/nodes/field.png +platform/icons/src/nodes/folder.png +platform/icons/src/nodes/function.png +platform/icons/src/nodes/ideaModule.png +platform/icons/src/nodes/ideaProject.png +platform/icons/src/nodes/interface.png +platform/icons/src/nodes/method.png +platform/icons/src/nodes/methodReference.png +platform/icons/src/nodes/Module.png +platform/icons/src/nodes/moduleGroup.png +platform/icons/src/nodes/newFolder.png +platform/icons/src/nodes/package.png +platform/icons/src/nodes/parameter.png +platform/icons/src/nodes/pointcut.png +platform/icons/src/nodes/ppJar.png +platform/icons/src/nodes/ppLib.png +platform/icons/src/nodes/ppLibFolder.png +platform/icons/src/nodes/property.png +platform/icons/src/nodes/propertyRead.png +platform/icons/src/nodes/propertyReadStatic.png +platform/icons/src/nodes/propertyReadWrite.png +platform/icons/src/nodes/propertyReadWriteStatic.png +platform/icons/src/nodes/propertyWrite.png +platform/icons/src/nodes/propertyWriteStatic.png +platform/icons/src/nodes/resourceBundle.png +platform/icons/src/nodes/sourceFolder.png +platform/icons/src/nodes/static.png +platform/icons/src/nodes/TreeClosed.png +platform/icons/src/nodes/unknownJdk.png +platform/icons/src/nodes/upFolder.png +platform/icons/src/nodes/variable.png +platform/icons/src/nodes/webFolder.png diff --git a/platform/platform-tests/testSrc/com/intellij/util/IconsLoadTimeTest.java b/platform/platform-tests/testSrc/com/intellij/util/IconsLoadTimeTest.java new file mode 100644 index 000000000000..a6d34630d062 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/util/IconsLoadTimeTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ +package com.intellij.util; + +import com.intellij.internal.IconsLoadTime; +import com.intellij.internal.IconsLoadTime.StatData; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.testFramework.PlatformTestUtil; +import org.junit.Test; + +import java.io.*; + +import static com.intellij.util.ImageLoader.ImageDesc.Type; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; + +/** + * Estimates SVG icon average load time. + * + * @author tav + */ +public class IconsLoadTimeTest { + private static final int SVG_ICON_AVERAGE_LOAD_TIME_ESTIMATE = 10; // ms + private static final int SVG_ICON_QUORUM_COUNT = 50; + + // a list of icons for which we have SVG versions + private static final String ICONS_LIST_PATH = PlatformTestUtil.getPlatformTestDataPath() + "icons/icons_list.txt"; + + @Test + public void loadIcons() throws ClassNotFoundException, IOException { + System.setProperty("idea.is.internal", "true"); + Registry.get("ide.svg.icon").setValue(true); + + assertNotNull(Class.forName(IconsLoadTime.class.getName())); // force static init + + try (BufferedReader br = new BufferedReader(new FileReader(new File(ICONS_LIST_PATH)))) { + String iconPath; + while ((iconPath = br.readLine()) != null) { + ImageLoader.loadFromUrl(new File(PlatformTestUtil.getCommunityPath() + "/" + iconPath).toURI().toURL()); + } + } + StatData svgData = IconsLoadTime.getStatData(false, Type.SVG); + assertNotNull("failed to gather SVG load statistics", svgData); + System.out.println(svgData); + + assertEquals("too few icons loaded: " + svgData.count + "; expecting > " + SVG_ICON_QUORUM_COUNT, + true, svgData.count >= SVG_ICON_QUORUM_COUNT); + + assertEquals("SVG icon load time raised to " + String.format("%.02fms", svgData.averageTime) + + ", exceeding max " + SVG_ICON_AVERAGE_LOAD_TIME_ESTIMATE + "ms", + true, svgData.averageTime <= SVG_ICON_AVERAGE_LOAD_TIME_ESTIMATE); + } +}