Merge Android Studio EE Canary 9
authorAndrei Kuznetsov <andrei.kuznetsov@jetbrains.com>
Fri, 5 Aug 2022 16:44:55 +0000 (19:44 +0300)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Fri, 12 Aug 2022 16:32:07 +0000 (16:32 +0000)
GitOrigin-RevId: 0050a742aebbf8ecbcd3a76198a8a8e7059c6604

73 files changed:
1  2 
.gitignore
.idea/libraries/aia_proto.xml
.idea/libraries/android_test_plugin_host_device_info_proto.xml
.idea/libraries/baksmali.xml
.idea/libraries/dexlib2.xml
.idea/libraries/emulator_proto.xml
.idea/libraries/ffmpeg.xml
.idea/libraries/ffmpeg_platform.xml
.idea/libraries/layout_inspector_compose_java_proto.xml
.idea/libraries/layout_inspector_snapshot_java_proto.xml
.idea/libraries/layout_inspector_view_java_proto.xml
.idea/libraries/layoutinspector_skia_proto.xml
.idea/libraries/layoutlib.xml
.idea/libraries/libam_instrumentation_data_proto.xml
.idea/libraries/libapp_processes_proto.xml
.idea/libraries/network_inspector_java_proto.xml
.idea/libraries/perfetto_proto.xml
.idea/libraries/sqlite_inspector_proto.xml
.idea/libraries/studio_analytics_proto.xml
.idea/libraries/studio_grpc.xml
.idea/libraries/studio_proto.xml
.idea/libraries/transport_proto.xml
.idea/modules.xml
tools/ideTestingFramework/intellij.tools.ide.starter/README.md
tools/ideTestingFramework/intellij.tools.ide.starter/intellij.tools.ide.starter.iml
tools/ideTestingFramework/intellij.tools.ide.starter/jvmti/Makefile
tools/ideTestingFramework/intellij.tools.ide.starter/jvmti/vmtrace.cpp
tools/ideTestingFramework/intellij.tools.ide.starter/resources/ide.general.xml
tools/ideTestingFramework/intellij.tools.ide.starter/resources/libvmtrace-aarch64.dylib
tools/ideTestingFramework/intellij.tools.ide.starter/resources/libvmtrace.dylib
tools/ideTestingFramework/intellij.tools.ide.starter/resources/libvmtrace.so
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/android.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/bus/EventsReceiver.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/bus/Extensions.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/bus/LICENSE
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/community/ProductInfoRequestParameters.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/community/PublicIdeDownloader.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/coroutine/CommonScope.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/di/diContainer.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/ide/IDETestContext.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/ide/IdeArchiveExtractor.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/ide/IdeInstallator.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/ide/IdeProductProvider.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/ide/LinuxIdeDistribution.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/models/IdeProduct.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/models/IdeProductImp.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/models/TestCase.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/models/VMOptions.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/models/VMTrace.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/path/GlobalPaths.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/plugins/PluginConfigurator.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/process/exec/ExecOutputRedirect.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/process/exec/ExecTimeoutException.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/process/exec/ProcessExecutor.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/process/exec/execUtil.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/process/processUtils.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/report/ErrorReporter.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/report/publisher/impl/QodanaTestResultPublisher.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/report/sarif/SarifBuilder.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/report/sarif/TestContextToQodanaSarifMapper.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/runner/CurrentTestMethod.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/runner/IDERunContext.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/runner/TestContainer.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/runner/TestContainerImpl.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/runner/TestContextInitializedEvent.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/runner/TestWatcherActions.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/sdk/JdkDownloaderFacade.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/system/SystemInfo.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/utils/FileSystem.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/utils/Git.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/utils/testNameExtension.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/utils/utils.kt
tools/ideTestingFramework/intellij.tools.ide.starter/src/com/intellij/ide/starter/wsl/WslDistributionNotFoundException.kt

diff --cc .gitignore
index 7a81c1e3c8def3bad4ea3ca08807f8fd4f9f8f94,a492e8c3b116c24bbef02370bd80fbe2c651713b..5e640a6eff50cf0cc85b56fb9e08f981d28e30b3
@@@ -15,7 -17,4 +15,6 @@@ build/dependencies/.gradl
  build/dependencies/build
  edu/dependencies/.gradle
  edu/dependencies/build
 +native/**/build/
  stale_outputs_checked
- /android
- /tools/ideTestingFramework/intellij.tools.ide.starter
++/android
index 9fc3bbfdf3f3ee29d96515b76518f1293ff25ffe,0000000000000000000000000000000000000000..63ec506d6533619bd2c42e2ef11a1d3e758652f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="aia-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/proto/30.2.0.0/proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/proto/221.0.9.0/proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 6e4a6cc564e1a7bd3152563bd5bb814d3dfa1ac3,0000000000000000000000000000000000000000..04d2e8f84d802aa4a6c0c49c454e4951e7705405
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:libstudio.android-test-plugin-host-device-info-proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="android-test-plugin-host-device-info-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/libstudio.android-test-plugin-host-device-info-proto/30.2.0.0/libstudio.android-test-plugin-host-device-info-proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:libstudio.android-test-plugin-host-device-info-proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/libstudio.android-test-plugin-host-device-info-proto/221.0.9.0/libstudio.android-test-plugin-host-device-info-proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index adae6d1501dfad8f0b8e9912947b3c9c61866995,85627127198e70bf9fabdcf99bf184faa8b052da..f69131f662e6b548a5f6539889bb3b5ee1d1a770
@@@ -1,8 -1,8 +1,8 @@@
  <component name="libraryTable">
 -  <library name="baksmali">
 +  <library name="baksmali" type="repository">
-     <properties include-transitive-deps="false" maven-id="org.smali:baksmali:2.2.4" />
++    <properties include-transitive-deps="false" maven-id="org.smali:baksmali:2.5.2" />
      <CLASSES>
-       <root url="jar://$MAVEN_REPOSITORY$/org/smali/baksmali/2.2.4/baksmali-2.2.4.jar!/" />
 -      <root url="jar://$PROJECT_DIR$/android/android/lib/baksmali-2.2.1.jar!/" />
 -      <root url="jar://$PROJECT_DIR$/android/android/lib/util-2.2.1.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/smali/baksmali/2.5.2/baksmali-2.5.2.jar!/" />
      </CLASSES>
      <JAVADOC />
      <SOURCES />
index d8320eb4e4d59cc6d05f95d85b98346a95e55e6b,e8044243b570b329dd90da97b7cf02e32c7ef0b1..fb3a02bd0bb75d09bea66949413abe912476f1b1
@@@ -1,15 -1,7 +1,8 @@@
  <component name="libraryTable">
 -  <library name="dexlib2">
 +  <library name="dexlib2" type="repository">
-     <properties maven-id="org.smali:dexlib2:2.2.4">
-       <exclude>
-         <dependency maven-id="com.beust:jcommander" />
-         <dependency maven-id="com.google.code.findbugs:jsr305" />
-         <dependency maven-id="com.google.guava:guava" />
-       </exclude>
-     </properties>
++    <properties include-transitive-deps="false" maven-id="org.smali:dexlib2:2.5.2" />
      <CLASSES>
-       <root url="jar://$MAVEN_REPOSITORY$/org/smali/dexlib2/2.2.4/dexlib2-2.2.4.jar!/" />
-       <root url="jar://$MAVEN_REPOSITORY$/org/smali/util/2.2.4/util-2.2.4.jar!/" />
 -      <root url="jar://$PROJECT_DIR$/android/android/lib/dexlib2-2.2.1.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/smali/dexlib2/2.5.2/dexlib2-2.5.2.jar!/" />
      </CLASSES>
      <JAVADOC />
      <SOURCES />
index 40347b3b0b479f2e6076b23ac3fcf11b9df89727,0000000000000000000000000000000000000000..2011517ec059a34709ad2ed185b0ebf455477825
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:emulator_java_proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="emulator-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/emulator_java_proto/30.2.0.0/emulator_java_proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:emulator_java_proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/emulator_java_proto/221.0.9.0/emulator_java_proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..18697b01d56063e33710603f81c1002ad7adb3d9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++<component name="libraryTable">
++  <library name="ffmpeg" type="repository">
++    <properties maven-id="org.bytedeco:ffmpeg:5.0-1.5.7" />
++    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7.jar!/" />
++    </CLASSES>
++    <JAVADOC />
++    <SOURCES />
++  </library>
++</component>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..382da63c0311fe608aab6a6218a85e2ffa9695ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++<component name="libraryTable">
++  <library name="ffmpeg-platform" type="repository">
++    <properties maven-id="org.bytedeco:ffmpeg-platform:5.0-1.5.7" />
++    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg-platform/5.0-1.5.7/ffmpeg-platform-5.0-1.5.7.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp-platform/1.5.7/javacpp-platform-1.5.7.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-android-arm.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-android-arm64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-android-x86.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-android-x86_64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-ios-arm64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-ios-x86_64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-linux-armhf.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-linux-arm64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-linux-ppc64le.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-linux-x86.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-linux-x86_64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-macosx-arm64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-macosx-x86_64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-windows-x86.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/javacpp/1.5.7/javacpp-1.5.7-windows-x86_64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-android-arm.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-android-arm64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-android-x86.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-android-x86_64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-linux-x86.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-linux-x86_64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-linux-armhf.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-linux-arm64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-linux-ppc64le.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-macosx-arm64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-macosx-x86_64.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-windows-x86.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/bytedeco/ffmpeg/5.0-1.5.7/ffmpeg-5.0-1.5.7-windows-x86_64.jar!/" />
++    </CLASSES>
++    <JAVADOC />
++    <SOURCES />
++  </library>
++</component>
index f2658c2e929210dc2edefa597dce10d89321faff,0000000000000000000000000000000000000000..c442d4a0dfe4c25b74054a8e39f1b944ffd498ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:layout_inspector_compose_java_proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="layout_inspector_compose_java_proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/layout_inspector_compose_java_proto/30.2.0.0/layout_inspector_compose_java_proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:layout_inspector_compose_java_proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/layout_inspector_compose_java_proto/221.0.9.0/layout_inspector_compose_java_proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 374e85feb2c5bc199d3fdb945abb4333fa3a90c4,0000000000000000000000000000000000000000..b1b449ad44927f98af6e579b18e65e0c87f687ad
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:layout_inspector_snapshot_java_proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="layout_inspector_snapshot_java_proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/layout_inspector_snapshot_java_proto/30.2.0.0/layout_inspector_snapshot_java_proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:layout_inspector_snapshot_java_proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/layout_inspector_snapshot_java_proto/221.0.9.0/layout_inspector_snapshot_java_proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 0626fdc11e4230198570588614b7e708d6fafb9d,0000000000000000000000000000000000000000..b5f4f0520cedea7c364dd4826243a49fe6da7ecd
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:layout_inspector_view_java_proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="layout_inspector_view_java_proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/layout_inspector_view_java_proto/30.2.0.0/layout_inspector_view_java_proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:layout_inspector_view_java_proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/layout_inspector_view_java_proto/221.0.9.0/layout_inspector_view_java_proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 1d945ddd1b03a3ec23a3e34bb07895d79a40e1ce,0000000000000000000000000000000000000000..e0ddeba1f91dfc4598a336ff5ddc4833a4dc446b
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:layout_inspector_skia_java_proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="layoutinspector-skia-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/layout_inspector_skia_java_proto/30.2.0.0/layout_inspector_skia_java_proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:layout_inspector_skia_java_proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/layout_inspector_skia_java_proto/221.0.9.0/layout_inspector_skia_java_proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 594cb4c2aa766c80a5fdf6bc5d6c6872499c63ad,76fd67ac89cd4f4481748e0754d57cdc10e0c748..fb25de424cffadd471e0dd29bd47babdb75f9dc5
@@@ -1,8 -1,7 +1,8 @@@
  <component name="libraryTable">
 -  <library name="layoutlib">
 +  <library name="layoutlib" type="repository">
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:jb-layoutlib-jdk11:30.2.0.0" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:jb-layoutlib-jdk11:221.0.9.0" />
      <CLASSES>
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/jb-layoutlib-jdk11/30.2.0.0/jb-layoutlib-jdk11-30.2.0.0.jar!/" />
 -      <root url="jar://$PROJECT_DIR$/android/android/lib/jdk8/layoutlib.jar!/" />
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/jb-layoutlib-jdk11/221.0.9.0/jb-layoutlib-jdk11-221.0.9.0.jar!/" />
      </CLASSES>
      <JAVADOC />
      <SOURCES />
index 32d969ff7b749da9ce1d8de76d04db710100cb06,0000000000000000000000000000000000000000..2c59846a8d39546db07f6155ccba8c96d3448910
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:libam-instrumentation-data-proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="libam-instrumentation-data-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/libam-instrumentation-data-proto/30.2.0.0/libam-instrumentation-data-proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:libam-instrumentation-data-proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/libam-instrumentation-data-proto/221.0.9.0/libam-instrumentation-data-proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index c6c32367f8b378088d98525f21b833f536521adc,0000000000000000000000000000000000000000..7da13f078518db7b18484c12aea3a160962b395e
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:libapp-processes-proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="libapp-processes-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/libapp-processes-proto/30.2.0.0/libapp-processes-proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:libapp-processes-proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/libapp-processes-proto/221.0.9.0/libapp-processes-proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index a33da4dd87cb03296f9f2d866b61e2a930b9f8eb,0000000000000000000000000000000000000000..461a3261548297b2940214295d710443b658627d
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:network_inspector_java_proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="network_inspector_java_proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/network_inspector_java_proto/30.2.0.0/network_inspector_java_proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:network_inspector_java_proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/network_inspector_java_proto/221.0.9.0/network_inspector_java_proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index bdb7e66049fe7ee4dc7126dc32dac014fb7d14d8,0000000000000000000000000000000000000000..40aaba0c58a155fb283f1228537c5132da784cb7
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:perfetto-protos:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="perfetto-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/perfetto-protos/30.2.0.0/perfetto-protos-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:perfetto-protos:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/perfetto-protos/221.0.9.0/perfetto-protos-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index e2df5d1c3f4b9c40b846e80b5cd269ac199770f3,0000000000000000000000000000000000000000..ddf8d789d9ec7a797999e6273fbedf820bad5fa7
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:sqlite_inspector_proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="sqlite-inspector-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/sqlite_inspector_proto/30.2.0.0/sqlite_inspector_proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:sqlite_inspector_proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/sqlite_inspector_proto/221.0.9.0/sqlite_inspector_proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 7d5e8e299bb6ec050b1c06e676771ccc7d37300e,0000000000000000000000000000000000000000..0676477f74c7be6ff3bdff43e9b9d16555e94f6f
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:libstudio.proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="studio-analytics-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/libstudio.proto/30.2.0.0/libstudio.proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:libstudio.proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/libstudio.proto/221.0.9.0/libstudio.proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 32ae201bdec4d1741a9c36c4bc3a55b36e91a359,0000000000000000000000000000000000000000..a161e4067db727c748b7860ce75b5f8d8a839437
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:studio-grpc:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="studio-grpc" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/studio-grpc/30.2.0.0/studio-grpc-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:studio-grpc:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/studio-grpc/221.0.9.0/studio-grpc-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 4819708d646dd0e61f831412f3556bd1a3aa012c,0000000000000000000000000000000000000000..58375af4aa9a1e9c8659ccbfbd896a69378aa27e
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:studio-proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="studio-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/studio-proto/30.2.0.0/studio-proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:studio-proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/studio-proto/221.0.9.0/studio-proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 3b61b9d93284476cbd161c39623356328cad7880,0000000000000000000000000000000000000000..1b0016db88e871d2bd30e38b9e0207fcdeccb9d7
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-     <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:transport_java_proto:30.2.0.0" />
 +<component name="libraryTable">
 +  <library name="transport-proto" type="repository">
-       <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/transport_java_proto/30.2.0.0/transport_java_proto-30.2.0.0.jar!/" />
++    <properties include-transitive-deps="false" maven-id="org.jetbrains.intellij.deps.android.tools.base:transport_java_proto:221.0.9.0" />
 +    <CLASSES>
++      <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/intellij/deps/android/tools/base/transport_java_proto/221.0.9.0/transport_java_proto-221.0.9.0.jar!/" />
 +    </CLASSES>
 +    <JAVADOC />
 +    <SOURCES />
 +  </library>
 +</component>
index 6cb542914381e1381d1c92e1731a80463c82ae6e,d8dc53e0b33b8d78c150cb0bdc56b2232d5d3b2c..1eeeb2d60c8c179f56ecfab66f4dded5d5fb17f5
    </component>
    <component name="ProjectModuleManager">
      <modules>
 -      <module fileurl="file://$PROJECT_DIR$/android/tools-base/flags/android.sdktools.flags.iml" filepath="$PROJECT_DIR$/android/tools-base/flags/android.sdktools.flags.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/tools-base/instant-run/instant-run-annotations/android.sdktools.instant-run-annotations.iml" filepath="$PROJECT_DIR$/android/tools-base/instant-run/instant-run-annotations/android.sdktools.instant-run-annotations.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/tools-base/instant-run/instant-run-client/android.sdktools.instant-run-client.iml" filepath="$PROJECT_DIR$/android/tools-base/instant-run/instant-run-client/android.sdktools.instant-run-client.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/tools-base/instant-run/instant-run-common/android.sdktools.instant-run-common.iml" filepath="$PROJECT_DIR$/android/tools-base/instant-run/instant-run-common/android.sdktools.instant-run-common.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/tools-base/android.sdktools.layoutlib-resources.iml" filepath="$PROJECT_DIR$/android/tools-base/android.sdktools.layoutlib-resources.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/tools-base/sdk-common/android.sdktools.sdk-common.iml" filepath="$PROJECT_DIR$/android/tools-base/sdk-common/android.sdktools.sdk-common.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/tools-base/sdklib/android.sdktools.sdklib.iml" filepath="$PROJECT_DIR$/android/tools-base/sdklib/android.sdktools.sdklib.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/analytics/analytics.iml" filepath="$PROJECT_DIR$/android/analytics/analytics.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/analytics-crash.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/analytics-crash.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/analytics-shared.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/analytics-shared.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/analytics-tracker.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/analytics-tracker.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.adblib.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.adblib.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analytics-crash.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analytics-crash.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analytics-shared.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analytics-shared.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.adblib.ddmlibcompatibility.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.adblib.ddmlibcompatibility.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.adblib.tools.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.adblib.tools.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analytics-testing.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analytics-testing.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analytics-tracker.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analytics-tracker.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analyzer.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.analyzer.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.android-annotations.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.android-annotations.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.binary-resources.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.binary-resources.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.builder-model.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.builder-model.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.chunkio.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.chunkio.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.common.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.common.testfixtures.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.common.testfixtures.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.db-baseLibrary.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.db-baseLibrary.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.db-baseLibrarySupport.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.db-baseLibrarySupport.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.db-compiler.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.db-compiler.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.db-compilerCommon.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.db-compilerCommon.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.ddmlib.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.ddmlib.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.deployer.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.deployer.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.draw9patch.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.draw9patch.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.dvlib.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.dvlib.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.dynamic-layout-inspector.common.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.dynamic-layout-inspector.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.fakeadbserver.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.fakeadbserver.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.flags.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.flags.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.layoutinspector.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.layoutinspector.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.layoutlib-api.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.layoutlib-api.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.lint-api.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.lint-api.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.lint-checks.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.lint-checks.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.lint-model.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.lint-model.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.manifest-merger.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.manifest-merger.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.manifest-parser.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.manifest-parser.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.mlkit-common.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.mlkit-common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.ninepatch.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.ninepatch.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.perf-logger.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.perf-logger.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.perflib.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.perflib.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.pixelprobe.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.pixelprobe.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.repository.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.repository.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.resource-repository.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.resource-repository.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.sdk-common.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.sdk-common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.sdklib.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.sdklib.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.testutils.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.testutils.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.threading-agent-callback.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.threading-agent-callback.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.tracer.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.tracer.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.usb-devices.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.usb-devices.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.wizardTemplate.impl.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.wizardTemplate.impl.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.wizardTemplate.plugin.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.wizardTemplate.plugin.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.zipflinger.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/android.sdktools.zipflinger.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/api/app-inspection.api.iml" filepath="$PROJECT_DIR$/android/app-inspection/api/app-inspection.api.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/ide/app-inspection.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/ide/app-inspection.ide.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/ide/app-inspection.ide.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/ide/app-inspection.ide.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspector/api/app-inspection.inspector.api.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspector/api/app-inspection.inspector.api.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspector/ide/app-inspection.inspector.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspector/ide/app-inspection.inspector.ide.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/ide/app-inspection.inspectors.backgroundtask.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/ide/app-inspection.inspectors.backgroundtask.ide.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/model/app-inspection.inspectors.backgroundtask.model.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/model/app-inspection.inspectors.backgroundtask.model.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/view/app-inspection.inspectors.backgroundtask.view.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/view/app-inspection.inspectors.backgroundtask.view.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/view/app-inspection.inspectors.backgroundtask.view.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/view/app-inspection.inspectors.backgroundtask.view.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/database/app-inspection.inspectors.database.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/database/app-inspection.inspectors.database.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/database/app-inspection.inspectors.database.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/database/app-inspection.inspectors.database.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/ide/app-inspection.inspectors.network.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/ide/app-inspection.inspectors.network.ide.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/ide/app-inspection.inspectors.network.ide.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/ide/app-inspection.inspectors.network.ide.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/model/app-inspection.inspectors.network.model.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/model/app-inspection.inspectors.network.model.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/view/app-inspection.inspectors.network.view.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/view/app-inspection.inspectors.network.view.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/view/app-inspection.inspectors.network.view.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/view/app-inspection.inspectors.network.view.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/ide/app-inspection.inspectors.workmanager.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/ide/app-inspection.inspectors.workmanager.ide.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/model/app-inspection.inspectors.workmanager.model.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/model/app-inspection.inspectors.workmanager.model.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/view/app-inspection.inspectors.workmanager.view.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/view/app-inspection.inspectors.workmanager.view.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/view/app-inspection.inspectors.workmanager.view.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/view/app-inspection.inspectors.workmanager.view.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/as-driver/agent/as-driver.agent.iml" filepath="$PROJECT_DIR$/android/as-driver/agent/as-driver.agent.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/as-driver/utils/as-driver.utils.iml" filepath="$PROJECT_DIR$/android/as-driver/utils/as-driver.utils.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/assistant/assistant.iml" filepath="$PROJECT_DIR$/android/assistant/assistant.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/assistant/assistant-test.iml" filepath="$PROJECT_DIR$/android/assistant/assistant-test.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/codenavigation/codenavigation.iml" filepath="$PROJECT_DIR$/android/codenavigation/codenavigation.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/connection-assistant/connection-assistant.iml" filepath="$PROJECT_DIR$/android/connection-assistant/connection-assistant.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/connection-assistant/connection-assistant-test.iml" filepath="$PROJECT_DIR$/android/connection-assistant/connection-assistant-test.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/db-baseLibrary.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/db-baseLibrary.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/db-baseLibrarySupport.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/db-baseLibrarySupport.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/db-compiler.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/db-compiler.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/db-compilerCommon.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/db-compilerCommon.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/dynamic-layout-inspector.common.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/dynamic-layout-inspector.common.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/android-adb/intellij.android.adb.iml" filepath="$PROJECT_DIR$/android/android-adb/intellij.android.adb.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/android-adb/intellij.android.adb.tests.iml" filepath="$PROJECT_DIR$/android/android-adb/intellij.android.adb.tests.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/adt-branding/intellij.android.adt.branding.iml" filepath="$PROJECT_DIR$/android/adt-branding/intellij.android.adt.branding.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android-adb-ui/intellij.android.adb.ui.iml" filepath="$PROJECT_DIR$/android/android-adb-ui/intellij.android.adb.ui.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android-adb-ui/intellij.android.adb.ui.tests.iml" filepath="$PROJECT_DIR$/android/android-adb-ui/intellij.android.adb.ui.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/swingp/intellij.android.adt.swingp.iml" filepath="$PROJECT_DIR$/android/swingp/intellij.android.adt.swingp.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/adt-testutils/intellij.android.adt.testutils.iml" filepath="$PROJECT_DIR$/android/adt-testutils/intellij.android.adt.testutils.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/adt-ui/intellij.android.adt.ui.iml" filepath="$PROJECT_DIR$/android/adt-ui/intellij.android.adt.ui.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/adt-ui-model/intellij.android.adt.ui.model.iml" filepath="$PROJECT_DIR$/android/adt-ui-model/intellij.android.adt.ui.model.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android-layout-inspector/intellij.android.android-layout-inspector.iml" filepath="$PROJECT_DIR$/android/android-layout-inspector/intellij.android.android-layout-inspector.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/android-layout-inspector/intellij.android.android-layout-inspector-test.iml" filepath="$PROJECT_DIR$/android/android-layout-inspector/intellij.android.android-layout-inspector-test.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/androidx-integration-tests/intellij.android.androidx-integration-tests.iml" filepath="$PROJECT_DIR$/android/androidx-integration-tests/intellij.android.androidx-integration-tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/apkanalyzer/intellij.android.apkanalyzer.iml" filepath="$PROJECT_DIR$/android/apkanalyzer/intellij.android.apkanalyzer.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/api/intellij.android.app-inspection.api.iml" filepath="$PROJECT_DIR$/android/app-inspection/api/intellij.android.app-inspection.api.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/ide/intellij.android.app-inspection.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/ide/intellij.android.app-inspection.ide.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/ide/intellij.android.app-inspection.ide.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/ide/intellij.android.app-inspection.ide.tests.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspector/api/intellij.android.app-inspection.inspector.api.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspector/api/intellij.android.app-inspection.inspector.api.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspector/ide/intellij.android.app-inspection.inspector.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspector/ide/intellij.android.app-inspection.inspector.ide.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/ide/intellij.android.app-inspection.inspectors.backgroundtask.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/ide/intellij.android.app-inspection.inspectors.backgroundtask.ide.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/model/intellij.android.app-inspection.inspectors.backgroundtask.model.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/model/intellij.android.app-inspection.inspectors.backgroundtask.model.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/view/intellij.android.app-inspection.inspectors.backgroundtask.view.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/view/intellij.android.app-inspection.inspectors.backgroundtask.view.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/view/intellij.android.app-inspection.inspectors.backgroundtask.view.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/backgroundtask/view/intellij.android.app-inspection.inspectors.backgroundtask.view.tests.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/database/intellij.android.app-inspection.inspectors.database.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/database/intellij.android.app-inspection.inspectors.database.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/database/intellij.android.app-inspection.inspectors.database.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/database/intellij.android.app-inspection.inspectors.database.tests.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/ide/intellij.android.app-inspection.inspectors.network.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/ide/intellij.android.app-inspection.inspectors.network.ide.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/ide/intellij.android.app-inspection.inspectors.network.ide.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/ide/intellij.android.app-inspection.inspectors.network.ide.tests.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/model/intellij.android.app-inspection.inspectors.network.model.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/model/intellij.android.app-inspection.inspectors.network.model.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/view/intellij.android.app-inspection.inspectors.network.view.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/view/intellij.android.app-inspection.inspectors.network.view.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/network/view/intellij.android.app-inspection.inspectors.network.view.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/network/view/intellij.android.app-inspection.inspectors.network.view.tests.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/ide/intellij.android.app-inspection.inspectors.workmanager.ide.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/ide/intellij.android.app-inspection.inspectors.workmanager.ide.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/model/intellij.android.app-inspection.inspectors.workmanager.model.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/model/intellij.android.app-inspection.inspectors.workmanager.model.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/view/intellij.android.app-inspection.inspectors.workmanager.view.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/view/intellij.android.app-inspection.inspectors.workmanager.view.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/view/intellij.android.app-inspection.inspectors.workmanager.view.tests.iml" filepath="$PROJECT_DIR$/android/app-inspection/inspectors/workmanager/view/intellij.android.app-inspection.inspectors.workmanager.view.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/artwork/intellij.android.artwork.iml" filepath="$PROJECT_DIR$/android/artwork/intellij.android.artwork.iml" />
--      <module fileurl="file://$PROJECT_DIR$/android/assistant/intellij.android.assistant.iml" filepath="$PROJECT_DIR$/android/assistant/intellij.android.assistant.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/assistant/intellij.android.assistant-test.iml" filepath="$PROJECT_DIR$/android/assistant/intellij.android.assistant-test.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/bleak/intellij.android.bleak.iml" filepath="$PROJECT_DIR$/android/bleak/intellij.android.bleak.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/build-attribution/intellij.android.build-attribution.iml" filepath="$PROJECT_DIR$/android/build-attribution/intellij.android.build-attribution.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/build-attribution/intellij.android.build-attribution.tests.iml" filepath="$PROJECT_DIR$/android/build-attribution/intellij.android.build-attribution.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/build-common/intellij.android.buildCommon.iml" filepath="$PROJECT_DIR$/android/build-common/intellij.android.buildCommon.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/codenavigation/intellij.android.codenavigation.iml" filepath="$PROJECT_DIR$/android/codenavigation/intellij.android.codenavigation.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/android-common/intellij.android.common.iml" filepath="$PROJECT_DIR$/android/android-common/intellij.android.common.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/connection-assistant/intellij.android.connectionAssistant.iml" filepath="$PROJECT_DIR$/android/connection-assistant/intellij.android.connectionAssistant.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/intellij.android.compose-common/intellij.android.compose-common.iml" filepath="$PROJECT_DIR$/android/intellij.android.compose-common/intellij.android.compose-common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/compose-designer/intellij.android.compose-designer.iml" filepath="$PROJECT_DIR$/android/compose-designer/intellij.android.compose-designer.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/compose-designer/intellij.android.compose-designer.integration.iml" filepath="$PROJECT_DIR$/android/compose-designer/intellij.android.compose-designer.integration.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/compose-designer/intellij.android.compose-designer.tests.iml" filepath="$PROJECT_DIR$/android/compose-designer/intellij.android.compose-designer.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/compose-ide-plugin/intellij.android.compose-ide-plugin.iml" filepath="$PROJECT_DIR$/android/compose-ide-plugin/intellij.android.compose-ide-plugin.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/compose-ide-plugin/intellij.android.compose-ide-plugin.tests.iml" filepath="$PROJECT_DIR$/android/compose-ide-plugin/intellij.android.compose-ide-plugin.tests.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/connection-assistant/intellij.android.connection-assistant.iml" filepath="$PROJECT_DIR$/android/connection-assistant/intellij.android.connection-assistant.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/connection-assistant/intellij.android.connection-assistant-test.iml" filepath="$PROJECT_DIR$/android/connection-assistant/intellij.android.connection-assistant-test.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/android/intellij.android.core.iml" filepath="$PROJECT_DIR$/android/android/intellij.android.core.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android/intellij.android.core.agp-integration-tests.iml" filepath="$PROJECT_DIR$/android/android/intellij.android.core.agp-integration-tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/android/intellij.android.core.tests.iml" filepath="$PROJECT_DIR$/android/android/intellij.android.core.tests.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/android-debuggers/intellij.android.debuggers.iml" filepath="$PROJECT_DIR$/android/android-debuggers/intellij.android.debuggers.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/android-debuggers/intellij.android.debuggers.tests.iml" filepath="$PROJECT_DIR$/android/android-debuggers/intellij.android.debuggers.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/dagger/intellij.android.dagger.iml" filepath="$PROJECT_DIR$/android/dagger/intellij.android.dagger.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/dagger/intellij.android.dagger.tests.iml" filepath="$PROJECT_DIR$/android/dagger/intellij.android.dagger.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/databinding/intellij.android.databinding.iml" filepath="$PROJECT_DIR$/android/databinding/intellij.android.databinding.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/databinding/intellij.android.databinding.tests.iml" filepath="$PROJECT_DIR$/android/databinding/intellij.android.databinding.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/debuggers/intellij.android.debuggers.iml" filepath="$PROJECT_DIR$/android/debuggers/intellij.android.debuggers.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/debuggers/intellij.android.debuggers.tests.iml" filepath="$PROJECT_DIR$/android/debuggers/intellij.android.debuggers.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/deploy/intellij.android.deploy.iml" filepath="$PROJECT_DIR$/android/deploy/intellij.android.deploy.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/design-plugin/intellij.android.design-plugin.iml" filepath="$PROJECT_DIR$/android/design-plugin/intellij.android.design-plugin.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/designer/intellij.android.designer.iml" filepath="$PROJECT_DIR$/android/designer/intellij.android.designer.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/designer-perf-tests/intellij.android.designer-perf-tests.iml" filepath="$PROJECT_DIR$/android/designer-perf-tests/intellij.android.designer-perf-tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/designer/customview/intellij.android.designer.customview.iml" filepath="$PROJECT_DIR$/android/designer/customview/intellij.android.designer.customview.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/designer/customview/intellij.android.designer.customview.tests.iml" filepath="$PROJECT_DIR$/android/designer/customview/intellij.android.designer.customview.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/designer/intellij.android.designer.tests.iml" filepath="$PROJECT_DIR$/android/designer/intellij.android.designer.tests.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/uitest-framework/intellij.android.guiTestFramework.iml" filepath="$PROJECT_DIR$/android/uitest-framework/intellij.android.guiTestFramework.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/jps-plugin/intellij.android.jps.iml" filepath="$PROJECT_DIR$/android/jps-plugin/intellij.android.jps.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/device-explorer/intellij.android.device-explorer.iml" filepath="$PROJECT_DIR$/android/device-explorer/intellij.android.device-explorer.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/device-explorer/intellij.android.device-explorer.tests.iml" filepath="$PROJECT_DIR$/android/device-explorer/intellij.android.device-explorer.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/device-manager/intellij.android.device-manager.iml" filepath="$PROJECT_DIR$/android/device-manager/intellij.android.device-manager.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/device-manager/intellij.android.device-manager.tests.iml" filepath="$PROJECT_DIR$/android/device-manager/intellij.android.device-manager.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/device-monitor/intellij.android.device-monitor.iml" filepath="$PROJECT_DIR$/android/device-monitor/intellij.android.device-monitor.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/device-monitor/intellij.android.device-monitor.tests.iml" filepath="$PROJECT_DIR$/android/device-monitor/intellij.android.device-monitor.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/emulator/intellij.android.emulator.iml" filepath="$PROJECT_DIR$/android/emulator/intellij.android.emulator.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/emulator/intellij.android.emulator.tests.iml" filepath="$PROJECT_DIR$/android/emulator/intellij.android.emulator.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/gradle-tooling/studio-gradle-tooling-api/intellij.android.gradle-tooling.api.iml" filepath="$PROJECT_DIR$/android/gradle-tooling/studio-gradle-tooling-api/intellij.android.gradle-tooling.api.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/gradle-tooling/studio-gradle-tooling-impl/intellij.android.gradle-tooling.impl.iml" filepath="$PROJECT_DIR$/android/gradle-tooling/studio-gradle-tooling-impl/intellij.android.gradle-tooling.impl.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/gradle-dsl/intellij.android.gradle.dsl.iml" filepath="$PROJECT_DIR$/android/gradle-dsl/intellij.android.gradle.dsl.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/gradle-dsl-kotlin/intellij.android.gradle.dsl.kotlin.iml" filepath="$PROJECT_DIR$/android/gradle-dsl-kotlin/intellij.android.gradle.dsl.kotlin.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/gradle-dsl/intellij.android.gradle.dsl.tests.iml" filepath="$PROJECT_DIR$/android/gradle-dsl/intellij.android.gradle.dsl.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/gradle-dsl/intellij.android.gradle.dsl.testutils.iml" filepath="$PROJECT_DIR$/android/gradle-dsl/intellij.android.gradle.dsl.testutils.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/gradle-dsl-toml/intellij.android.gradle.dsl.toml.iml" filepath="$PROJECT_DIR$/android/gradle-dsl-toml/intellij.android.gradle.dsl.toml.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/ide-perf-tests/intellij.android.ide-perf-tests.iml" filepath="$PROJECT_DIR$/android/ide-perf-tests/intellij.android.ide-perf-tests.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/idea-integration-tests/intellij.android.ideaIntegrationTests.iml" filepath="$PROJECT_DIR$/android/idea-integration-tests/intellij.android.ideaIntegrationTests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/inspectors-common/api/intellij.android.inspectors-common.api.iml" filepath="$PROJECT_DIR$/android/inspectors-common/api/intellij.android.inspectors-common.api.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/inspectors-common/api-ide/intellij.android.inspectors-common.api-ide.iml" filepath="$PROJECT_DIR$/android/inspectors-common/api-ide/intellij.android.inspectors-common.api-ide.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/inspectors-common/ui/intellij.android.inspectors-common.ui.iml" filepath="$PROJECT_DIR$/android/inspectors-common/ui/intellij.android.inspectors-common.ui.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/kotlin-integration/intellij.android.kotlin-integration-tests.iml" filepath="$PROJECT_DIR$/android/kotlin-integration/intellij.android.kotlin-integration-tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/jps-model/intellij.android.jps.model.iml" filepath="$PROJECT_DIR$/android/jps-model/intellij.android.jps.model.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-kotlin/android-extensions-idea/intellij.android.kotlin.extensions.iml" filepath="$PROJECT_DIR$/android/android-kotlin/android-extensions-idea/intellij.android.kotlin.extensions.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-kotlin/android-extensions-idea-common/intellij.android.kotlin.extensions.common.iml" filepath="$PROJECT_DIR$/android/android-kotlin/android-extensions-idea-common/intellij.android.kotlin.extensions.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-kotlin/idea-android/intellij.android.kotlin.idea.iml" filepath="$PROJECT_DIR$/android/android-kotlin/idea-android/intellij.android.kotlin.idea.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-kotlin/idea-android/idea-android-output-parser/intellij.android.kotlin.output.parser.iml" filepath="$PROJECT_DIR$/android/android-kotlin/idea-android/idea-android-output-parser/intellij.android.kotlin.output.parser.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-kotlin/intellij.android.kotlin.tests.iml" filepath="$PROJECT_DIR$/android/android-kotlin/intellij.android.kotlin.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/android-lang/intellij.android.lang.iml" filepath="$PROJECT_DIR$/android/android-lang/intellij.android.lang.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-lang-databinding/intellij.android.lang-databinding.iml" filepath="$PROJECT_DIR$/android/android-lang-databinding/intellij.android.lang-databinding.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-lang-databinding/intellij.android.lang-databinding.tests.iml" filepath="$PROJECT_DIR$/android/android-lang-databinding/intellij.android.lang-databinding.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/android-lang/intellij.android.lang.tests.iml" filepath="$PROJECT_DIR$/android/android-lang/intellij.android.lang.tests.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/android-layout-inspector/intellij.android.layoutInspector.iml" filepath="$PROJECT_DIR$/android/android-layout-inspector/intellij.android.layoutInspector.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/android-layout-inspector/intellij.android.layoutInspector.test.iml" filepath="$PROJECT_DIR$/android/android-layout-inspector/intellij.android.layoutInspector.test.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/layout-inspector/intellij.android.layout-inspector.iml" filepath="$PROJECT_DIR$/android/layout-inspector/intellij.android.layout-inspector.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/layout-inspector/intellij.android.layout-inspector.tests.iml" filepath="$PROJECT_DIR$/android/layout-inspector/intellij.android.layout-inspector.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/layout-ui/intellij.android.layout-ui.iml" filepath="$PROJECT_DIR$/android/layout-ui/intellij.android.layout-ui.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/layoutlib/intellij.android.layoutlib.iml" filepath="$PROJECT_DIR$/android/layoutlib/intellij.android.layoutlib.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/native-symbolizer/intellij.android.nativeSymbolizer.iml" filepath="$PROJECT_DIR$/android/native-symbolizer/intellij.android.nativeSymbolizer.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/layoutlib-loader/intellij.android.layoutlib-loader.iml" filepath="$PROJECT_DIR$/android/layoutlib-loader/intellij.android.layoutlib-loader.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/layoutlib/intellij.android.layoutlib.tests.iml" filepath="$PROJECT_DIR$/android/layoutlib/intellij.android.layoutlib.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-lint/intellij.android.lint.iml" filepath="$PROJECT_DIR$/android/android-lint/intellij.android.lint.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/lint/intellij.android.lint.common.iml" filepath="$PROJECT_DIR$/android/lint/intellij.android.lint.common.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/lint/tests/intellij.android.lint.common.tests.iml" filepath="$PROJECT_DIR$/android/lint/tests/intellij.android.lint.common.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-lint/intellij.android.lint.tests.iml" filepath="$PROJECT_DIR$/android/android-lint/intellij.android.lint.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/logcat/intellij.android.logcat.iml" filepath="$PROJECT_DIR$/android/logcat/intellij.android.logcat.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/logcat/intellij.android.logcat.tests.iml" filepath="$PROJECT_DIR$/android/logcat/intellij.android.logcat.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/mlkit/intellij.android.mlkit.iml" filepath="$PROJECT_DIR$/android/mlkit/intellij.android.mlkit.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/mlkit/intellij.android.mlkit.tests.iml" filepath="$PROJECT_DIR$/android/mlkit/intellij.android.mlkit.tests.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/native-symbolizer/intellij.android.native-symbolizer.iml" filepath="$PROJECT_DIR$/android/native-symbolizer/intellij.android.native-symbolizer.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/nav/editor/intellij.android.nav.editor.iml" filepath="$PROJECT_DIR$/android/nav/editor/intellij.android.nav.editor.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/nav/editor/intellij.android.nav.editor.tests.iml" filepath="$PROJECT_DIR$/android/nav/editor/intellij.android.nav.editor.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/nav/safeargs/intellij.android.nav.safeargs.iml" filepath="$PROJECT_DIR$/android/nav/safeargs/intellij.android.nav.safeargs.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/nav/safeargs/intellij.android.nav.safeargs.tests.iml" filepath="$PROJECT_DIR$/android/nav/safeargs/intellij.android.nav.safeargs.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-navigator/intellij.android.navigator.iml" filepath="$PROJECT_DIR$/android/android-navigator/intellij.android.navigator.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-navigator/intellij.android.navigator.testutils.iml" filepath="$PROJECT_DIR$/android/android-navigator/intellij.android.navigator.testutils.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-npw/intellij.android.newProjectWizard.iml" filepath="$PROJECT_DIR$/android/android-npw/intellij.android.newProjectWizard.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-npw/intellij.android.newProjectWizard.tests.iml" filepath="$PROJECT_DIR$/android/android-npw/intellij.android.newProjectWizard.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/observable/intellij.android.observable.iml" filepath="$PROJECT_DIR$/android/observable/intellij.android.observable.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/observable-demo/intellij.android.observable-demo.iml" filepath="$PROJECT_DIR$/android/observable-demo/intellij.android.observable-demo.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/observable-ui/intellij.android.observable.ui.iml" filepath="$PROJECT_DIR$/android/observable-ui/intellij.android.observable.ui.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/perfd-host/intellij.android.perfdHost.iml" filepath="$PROJECT_DIR$/android/perfd-host/intellij.android.perfdHost.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/old-agp-tests/intellij.android.old-agp-tests.iml" filepath="$PROJECT_DIR$/android/old-agp-tests/intellij.android.old-agp-tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/android-plugin/intellij.android.plugin.iml" filepath="$PROJECT_DIR$/android/android-plugin/intellij.android.plugin.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/profilers/intellij.android.profilers.iml" filepath="$PROJECT_DIR$/android/profilers/intellij.android.profilers.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/profilers-atrace/intellij.android.profilers.atrace.iml" filepath="$PROJECT_DIR$/android/profilers-atrace/intellij.android.profilers.atrace.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/profilers-android/intellij.android.profilersAndroid.iml" filepath="$PROJECT_DIR$/android/profilers-android/intellij.android.profilersAndroid.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/project-system/intellij.android.projectSystem.iml" filepath="$PROJECT_DIR$/android/project-system/intellij.android.projectSystem.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle/intellij.android.projectSystem.gradle.iml" filepath="$PROJECT_DIR$/android/project-system-gradle/intellij.android.projectSystem.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-models/intellij.android.projectSystem.gradle.models.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-models/intellij.android.projectSystem.gradle.models.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-models/intellij.android.projectSystem.gradle.models.tests.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-models/intellij.android.projectSystem.gradle.models.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-psd/intellij.android.projectSystem.gradle.psd.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-psd/intellij.android.projectSystem.gradle.psd.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-psd/intellij.android.projectSystem.gradle.psd.tests.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-psd/intellij.android.projectSystem.gradle.psd.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-repository-search/intellij.android.projectSystem.gradle.repositorySearch.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-repository-search/intellij.android.projectSystem.gradle.repositorySearch.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-repository-search/intellij.android.projectSystem.gradle.repositorySearch.tests.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-repository-search/intellij.android.projectSystem.gradle.repositorySearch.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-sync/intellij.android.projectSystem.gradle.sync.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-sync/intellij.android.projectSystem.gradle.sync.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-sync/intellij.android.projectSystem.gradle.sync.tests.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-sync/intellij.android.projectSystem.gradle.sync.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle/intellij.android.projectSystem.gradle.tests.iml" filepath="$PROJECT_DIR$/android/project-system-gradle/intellij.android.projectSystem.gradle.tests.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/project-system/intellij.android.projectSystem.tests.iml" filepath="$PROJECT_DIR$/android/project-system/intellij.android.projectSystem.tests.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/rt/intellij.android.rt.iml" filepath="$PROJECT_DIR$/android/rt/intellij.android.rt.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-upgrade/intellij.android.projectSystem.gradle.upgrade.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-upgrade/intellij.android.projectSystem.gradle.upgrade.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/project-system-gradle-upgrade/intellij.android.projectSystem.gradle.upgrade.tests.iml" filepath="$PROJECT_DIR$/android/project-system-gradle-upgrade/intellij.android.projectSystem.gradle.upgrade.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/room/intellij.android.room.iml" filepath="$PROJECT_DIR$/android/room/intellij.android.room.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/room/intellij.android.room.tests.iml" filepath="$PROJECT_DIR$/android/room/intellij.android.room.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/sdk-updates/intellij.android.sdkUpdates.iml" filepath="$PROJECT_DIR$/android/sdk-updates/intellij.android.sdkUpdates.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/sdk-updates/intellij.android.sdkUpdates.tests.iml" filepath="$PROJECT_DIR$/android/sdk-updates/intellij.android.sdkUpdates.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/server-flags/intellij.android.server-flags.iml" filepath="$PROJECT_DIR$/android/server-flags/intellij.android.server-flags.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/smali/intellij.android.smali.iml" filepath="$PROJECT_DIR$/android/smali/intellij.android.smali.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/studio/integration/intellij.android.studio.integration/intellij.android.studio.integration.iml" filepath="$PROJECT_DIR$/android/studio/integration/intellij.android.studio.integration/intellij.android.studio.integration.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/studio/tests/intellij.android.studio.tests.iml" filepath="$PROJECT_DIR$/android/studio/tests/intellij.android.studio.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/sync-perf-tests/intellij.android.sync-perf-tests.iml" filepath="$PROJECT_DIR$/android/sync-perf-tests/intellij.android.sync-perf-tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-templates/intellij.android.templates.iml" filepath="$PROJECT_DIR$/android/android-templates/intellij.android.templates.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-templates/intellij.android.templates.tests.iml" filepath="$PROJECT_DIR$/android/android-templates/intellij.android.templates.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/android-test-framework/intellij.android.testFramework.iml" filepath="$PROJECT_DIR$/android/android-test-framework/intellij.android.testFramework.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/uitest-framework-bazel/intellij.android.uitestFramework.bazel.iml" filepath="$PROJECT_DIR$/android/uitest-framework-bazel/intellij.android.uitestFramework.bazel.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/whats-new-assistant/intellij.android.whatsNewAssistant.iml" filepath="$PROJECT_DIR$/android/whats-new-assistant/intellij.android.whatsNewAssistant.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-test-retention/intellij.android.testRetention.iml" filepath="$PROJECT_DIR$/android/android-test-retention/intellij.android.testRetention.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-test-retention/intellij.android.testRetention.tests.iml" filepath="$PROJECT_DIR$/android/android-test-retention/intellij.android.testRetention.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/threading-checker/intellij.android.threading-checker.iml" filepath="$PROJECT_DIR$/android/threading-checker/intellij.android.threading-checker.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/threading-checker/intellij.android.threading-checker.tests.iml" filepath="$PROJECT_DIR$/android/threading-checker/intellij.android.threading-checker.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/android-transport/intellij.android.transport.iml" filepath="$PROJECT_DIR$/android/android-transport/intellij.android.transport.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/android/transport-database/intellij.android.transportDatabase.iml" filepath="$PROJECT_DIR$/android/transport-database/intellij.android.transportDatabase.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/utp/intellij.android.utp.iml" filepath="$PROJECT_DIR$/android/utp/intellij.android.utp.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/whats-new-assistant/intellij.android.whats-new-assistant.iml" filepath="$PROJECT_DIR$/android/whats-new-assistant/intellij.android.whats-new-assistant.iml" />
-       <module fileurl="file://$PROJECT_DIR$/android/whats-new-assistant/intellij.android.whats-new-assistant-test.iml" filepath="$PROJECT_DIR$/android/whats-new-assistant/intellij.android.whats-new-assistant-test.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/wear-pairing/intellij.android.wear-pairing.iml" filepath="$PROJECT_DIR$/android/wear-pairing/intellij.android.wear-pairing.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/wear-pairing/intellij.android.wear-pairing.tests.iml" filepath="$PROJECT_DIR$/android/wear-pairing/intellij.android.wear-pairing.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/wizard/intellij.android.wizard.iml" filepath="$PROJECT_DIR$/android/wizard/intellij.android.wizard.iml" />
        <module fileurl="file://$PROJECT_DIR$/android/wizard-model/intellij.android.wizard.model.iml" filepath="$PROJECT_DIR$/android/wizard-model/intellij.android.wizard.model.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/android/android/intellij.androidStudio.main.iml" filepath="$PROJECT_DIR$/android/android/intellij.androidStudio.main.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/wizard/intellij.android.wizard.tests.iml" filepath="$PROJECT_DIR$/android/wizard/intellij.android.wizard.tests.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/intellij.android.wizardTemplate.impl.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/intellij.android.wizardTemplate.impl.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/intellij.android.wizardTemplate.plugin.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/intellij.android.wizardTemplate.plugin.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/ant/intellij.ant.iml" filepath="$PROJECT_DIR$/plugins/ant/intellij.ant.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/ant/jps-plugin/intellij.ant.jps.iml" filepath="$PROJECT_DIR$/plugins/ant/jps-plugin/intellij.ant.jps.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/color-schemes/all-hallows-eve-color-scheme/intellij.color.scheme.all_hallows_eve.iml" filepath="$PROJECT_DIR$/plugins/color-schemes/all-hallows-eve-color-scheme/intellij.color.scheme.all_hallows_eve.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/color-schemes/blackboard-color-scheme/intellij.color.scheme.blackboard.iml" filepath="$PROJECT_DIR$/plugins/color-schemes/blackboard-color-scheme/intellij.color.scheme.blackboard.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/color-schemes/cobalt-color-scheme/intellij.color.scheme.cobalt.iml" filepath="$PROJECT_DIR$/plugins/color-schemes/cobalt-color-scheme/intellij.color.scheme.cobalt.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/color-schemes/github-color-scheme/intellij.color.scheme.github.iml" filepath="$PROJECT_DIR$/plugins/color-schemes/github-color-scheme/intellij.color.scheme.github.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/color-schemes/monokai-color-scheme/intellij.color.scheme.monokai.iml" filepath="$PROJECT_DIR$/plugins/color-schemes/monokai-color-scheme/intellij.color.scheme.monokai.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/color-schemes/rails-casts-color-scheme/intellij.color.scheme.rails_casts.iml" filepath="$PROJECT_DIR$/plugins/color-schemes/rails-casts-color-scheme/intellij.color.scheme.rails_casts.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/color-schemes/twilight-color-scheme/intellij.color.scheme.twilight.iml" filepath="$PROJECT_DIR$/plugins/color-schemes/twilight-color-scheme/intellij.color.scheme.twilight.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/color-schemes/vibrant-ink-color-scheme/intellij.color.scheme.vibrant_ink.iml" filepath="$PROJECT_DIR$/plugins/color-schemes/vibrant-ink-color-scheme/intellij.color.scheme.vibrant_ink.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/color-schemes/warm-neon-color-scheme/intellij.color.scheme.warmNeon.iml" filepath="$PROJECT_DIR$/plugins/color-schemes/warm-neon-color-scheme/intellij.color.scheme.warmNeon.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/commander/intellij.commander.iml" filepath="$PROJECT_DIR$/plugins/commander/intellij.commander.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/completion-ml-ranking/intellij.completionMlRanking.iml" filepath="$PROJECT_DIR$/plugins/completion-ml-ranking/intellij.completionMlRanking.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/completion-ml-ranking/intellij.completionMlRanking.tests.iml" filepath="$PROJECT_DIR$/plugins/completion-ml-ranking/intellij.completionMlRanking.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/completion-ml-ranking-models/intellij.completionMlRankingModels.iml" filepath="$PROJECT_DIR$/plugins/completion-ml-ranking-models/intellij.completionMlRankingModels.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/completion-ml-ranking-models/intellij.completionMlRankingModels.tests.iml" filepath="$PROJECT_DIR$/plugins/completion-ml-ranking-models/intellij.completionMlRankingModels.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/configuration-script/intellij.configurationScript.iml" filepath="$PROJECT_DIR$/plugins/configuration-script/intellij.configurationScript.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/configuration-script/intellij.configurationScript.test.java.iml" filepath="$PROJECT_DIR$/plugins/configuration-script/intellij.configurationScript.test.java.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/copyright/intellij.copyright.iml" filepath="$PROJECT_DIR$/plugins/copyright/intellij.copyright.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/cucumber-jvm-formatter/intellij.cucumber.jvmFormatter.iml" filepath="$PROJECT_DIR$/plugins/cucumber-jvm-formatter/intellij.cucumber.jvmFormatter.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/cucumber-jvm-formatter3/intellij.cucumber.jvmFormatter3.iml" filepath="$PROJECT_DIR$/plugins/cucumber-jvm-formatter3/intellij.cucumber.jvmFormatter3.iml" />
        <module fileurl="file://$PROJECT_DIR$/jvm/jvm-analysis-impl/intellij.jvm.analysis.impl.iml" filepath="$PROJECT_DIR$/jvm/jvm-analysis-impl/intellij.jvm.analysis.impl.iml" />
        <module fileurl="file://$PROJECT_DIR$/jvm/jvm-analysis-java-tests/intellij.jvm.analysis.java.tests.iml" filepath="$PROJECT_DIR$/jvm/jvm-analysis-java-tests/intellij.jvm.analysis.java.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/jvm/jvm-analysis-kotlin-tests/intellij.jvm.analysis.kotlin.tests.iml" filepath="$PROJECT_DIR$/jvm/jvm-analysis-kotlin-tests/intellij.jvm.analysis.kotlin.tests.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/jvm/jvm-analysis-tests-api/intellij.jvm.analysis.tests.iml" filepath="$PROJECT_DIR$/jvm/jvm-analysis-tests-api/intellij.jvm.analysis.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/jvm/jvm-analysis-tests/intellij.jvm.analysis.testFramework.iml" filepath="$PROJECT_DIR$/jvm/jvm-analysis-tests/intellij.jvm.analysis.testFramework.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/eclipse-keymap/intellij.keymap.eclipse.iml" filepath="$PROJECT_DIR$/plugins/keymaps/eclipse-keymap/intellij.keymap.eclipse.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/emacs-keymap/intellij.keymap.emacs.iml" filepath="$PROJECT_DIR$/plugins/keymaps/emacs-keymap/intellij.keymap.emacs.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/netbeans5.6-keymap/intellij.keymap.netbeans.iml" filepath="$PROJECT_DIR$/plugins/keymaps/netbeans5.6-keymap/intellij.keymap.netbeans.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/qtcreator-keymap/intellij.keymap.qtcreator.iml" filepath="$PROJECT_DIR$/plugins/keymaps/qtcreator-keymap/intellij.keymap.qtcreator.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/resharper-keymap/intellij.keymap.resharper.iml" filepath="$PROJECT_DIR$/plugins/keymaps/resharper-keymap/intellij.keymap.resharper.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/sublime-keymap/intellij.keymap.sublime.iml" filepath="$PROJECT_DIR$/plugins/keymaps/sublime-keymap/intellij.keymap.sublime.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/visual-assist-keymap/intellij.keymap.visualAssist.iml" filepath="$PROJECT_DIR$/plugins/keymaps/visual-assist-keymap/intellij.keymap.visualAssist.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/visual-studio-keymap/intellij.keymap.visualStudio.iml" filepath="$PROJECT_DIR$/plugins/keymaps/visual-studio-keymap/intellij.keymap.visualStudio.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/visual-studio2022-keymap/intellij.keymap.visualStudio2022.iml" filepath="$PROJECT_DIR$/plugins/keymaps/visual-studio2022-keymap/intellij.keymap.visualStudio2022.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/visual-studio-for-mac-keymap/intellij.keymap.visualStudioForMac.iml" filepath="$PROJECT_DIR$/plugins/keymaps/visual-studio-for-mac-keymap/intellij.keymap.visualStudioForMac.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/vscode-keymap/intellij.keymap.vscode.iml" filepath="$PROJECT_DIR$/plugins/keymaps/vscode-keymap/intellij.keymap.vscode.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/keymaps/xcode-keymap/intellij.keymap.xcode.iml" filepath="$PROJECT_DIR$/plugins/keymaps/xcode-keymap/intellij.keymap.xcode.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/intellij.kotlin.plugin.community.main.iml" filepath="$PROJECT_DIR$/plugins/kotlin/intellij.kotlin.plugin.community.main.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/laf/macos/intellij.laf.macos.iml" filepath="$PROJECT_DIR$/plugins/laf/macos/intellij.laf.macos.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/laf/win10/intellij.laf.win10.iml" filepath="$PROJECT_DIR$/plugins/laf/win10/intellij.laf.win10.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/lint/intellij.lint.iml" filepath="$PROJECT_DIR$/android/lint/intellij.lint.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/lint/tests/intellij.lint.tests.iml" filepath="$PROJECT_DIR$/android/lint/tests/intellij.lint.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/lombok/intellij.lombok.iml" filepath="$PROJECT_DIR$/plugins/lombok/intellij.lombok.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/lombok/generated/intellij.lombok.generated.iml" filepath="$PROJECT_DIR$/plugins/lombok/generated/intellij.lombok.generated.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/markdown/intellij.markdown.iml" filepath="$PROJECT_DIR$/plugins/markdown/intellij.markdown.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/markdown/core/intellij.markdown.core.iml" filepath="$PROJECT_DIR$/plugins/markdown/core/intellij.markdown.core.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/markdown/fenceInjection/intellij.markdown.fenceInjection.iml" filepath="$PROJECT_DIR$/plugins/markdown/fenceInjection/intellij.markdown.fenceInjection.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/markdown/frontmatter/intellij.markdown.frontmatter.iml" filepath="$PROJECT_DIR$/plugins/markdown/frontmatter/intellij.markdown.frontmatter.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/markdown/test/intellij.markdown.tests.iml" filepath="$PROJECT_DIR$/plugins/markdown/test/intellij.markdown.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/maven/intellij.maven.iml" filepath="$PROJECT_DIR$/plugins/maven/intellij.maven.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/maven/artifact-resolver/common/intellij.maven.artifactResolver.common.iml" filepath="$PROJECT_DIR$/plugins/maven/artifact-resolver/common/intellij.maven.artifactResolver.common.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/plugins/maven/artifact-resolver-m2/intellij.maven.artifactResolver.m2.iml" filepath="$PROJECT_DIR$/plugins/maven/artifact-resolver-m2/intellij.maven.artifactResolver.m2.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/maven/artifact-resolver-m3/intellij.maven.artifactResolver.m3.iml" filepath="$PROJECT_DIR$/plugins/maven/artifact-resolver-m3/intellij.maven.artifactResolver.m3.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/maven/artifact-resolver-m31/intellij.maven.artifactResolver.m31.iml" filepath="$PROJECT_DIR$/plugins/maven/artifact-resolver-m31/intellij.maven.artifactResolver.m31.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/maven/error-prone-compiler/intellij.maven.errorProne.compiler.iml" filepath="$PROJECT_DIR$/plugins/maven/error-prone-compiler/intellij.maven.errorProne.compiler.iml" />
        <module fileurl="file://$PROJECT_DIR$/xml/xml-structure-view-impl/intellij.xml.structureView.impl.iml" filepath="$PROJECT_DIR$/xml/xml-structure-view-impl/intellij.xml.structureView.impl.iml" />
        <module fileurl="file://$PROJECT_DIR$/xml/tests/intellij.xml.tests.iml" filepath="$PROJECT_DIR$/xml/tests/intellij.xml.tests.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/xpath/intellij.xpath.iml" filepath="$PROJECT_DIR$/plugins/xpath/intellij.xpath.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/xpath/xslt-rt/intellij.xpath.rt.iml" filepath="$PROJECT_DIR$/plugins/xpath/xslt-rt/intellij.xpath.rt.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/xslt-debugger/intellij.xslt.debugger.iml" filepath="$PROJECT_DIR$/plugins/xslt-debugger/intellij.xslt.debugger.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/plugins/xslt-debugger/engine/intellij.xslt.debugger.engine.iml" filepath="$PROJECT_DIR$/plugins/xslt-debugger/engine/intellij.xslt.debugger.engine.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/plugins/xslt-debugger/engine/impl/intellij.xslt.debugger.engine.impl.iml" filepath="$PROJECT_DIR$/plugins/xslt-debugger/engine/impl/intellij.xslt.debugger.engine.impl.iml" />
 -      <module fileurl="file://$PROJECT_DIR$/plugins/xpath/xslt-rt/intellij.xslt.debugger.rt.iml" filepath="$PROJECT_DIR$/plugins/xpath/xslt-rt/intellij.xslt.debugger.rt.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/xslt-debugger/engine/impl/intellij.xslt.debugger.impl.rt.iml" filepath="$PROJECT_DIR$/plugins/xslt-debugger/engine/impl/intellij.xslt.debugger.impl.rt.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/xslt-debugger/engine/intellij.xslt.debugger.rt.iml" filepath="$PROJECT_DIR$/plugins/xslt-debugger/engine/intellij.xslt.debugger.rt.iml" />
        <module fileurl="file://$PROJECT_DIR$/plugins/yaml/intellij.yaml.iml" filepath="$PROJECT_DIR$/plugins/yaml/intellij.yaml.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/kotlin-integration/kotlin-integration-tests.iml" filepath="$PROJECT_DIR$/android/kotlin-integration/kotlin-integration-tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/all-tests/kotlin.all-tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/all-tests/kotlin.all-tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/analysis/kotlin.base.analysis.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/analysis/kotlin.base.analysis.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/analysis-api-providers/kotlin.base.analysis-api-providers.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/analysis-api-providers/kotlin.base.analysis-api-providers.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/analysis-api/analysis-api-utils/kotlin.base.analysis-api.utils.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/analysis-api/analysis-api-utils/kotlin.base.analysis-api.utils.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/analysis/tests/kotlin.base.analysis.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/analysis/tests/kotlin.base.analysis.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/code-insight/kotlin.base.code-insight.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/code-insight/kotlin.base.code-insight.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/compiler-configuration/kotlin.base.compiler-configuration.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/compiler-configuration/kotlin.base.compiler-configuration.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/compiler-configuration-ui/kotlin.base.compiler-configuration-ui.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/compiler-configuration-ui/kotlin.base.compiler-configuration-ui.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/external-build-system/kotlin.base.external-build-system.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/external-build-system/kotlin.base.external-build-system.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/facet/kotlin.base.facet.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/facet/kotlin.base.facet.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/fe10/analysis/kotlin.base.fe10.analysis.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/fe10/analysis/kotlin.base.fe10.analysis.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/fe10/analysis-api-providers/kotlin.base.fe10.analysis-api-providers.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/fe10/analysis-api-providers/kotlin.base.fe10.analysis-api-providers.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/fe10/code-insight/kotlin.base.fe10.code-insight.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/fe10/code-insight/kotlin.base.fe10.code-insight.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/fe10/highlighting/kotlin.base.fe10.highlighting.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/fe10/highlighting/kotlin.base.fe10.highlighting.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/fe10/kdoc/kotlin.base.fe10.kdoc.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/fe10/kdoc/kotlin.base.fe10.kdoc.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/fe10/obsolete-compat/kotlin.base.fe10.obsolete-compat.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/fe10/obsolete-compat/kotlin.base.fe10.obsolete-compat.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/fe10/plugin/kotlin.base.fe10.plugin.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/fe10/plugin/kotlin.base.fe10.plugin.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/fir/analysis-api-providers/kotlin.base.fir.analysis-api-providers.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/fir/analysis-api-providers/kotlin.base.fir.analysis-api-providers.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/fir/code-insight/kotlin.base.fir.code-insight.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/fir/code-insight/kotlin.base.fir.code-insight.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/frontend-agnostic/kotlin.base.frontend-agnostic.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/frontend-agnostic/kotlin.base.frontend-agnostic.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/highlighting/kotlin.base.highlighting.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/highlighting/kotlin.base.highlighting.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/indices/kotlin.base.indices.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/indices/kotlin.base.indices.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/jps/kotlin.base.jps.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/jps/kotlin.base.jps.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/kdoc/kotlin.base.kdoc.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/kdoc/kotlin.base.kdoc.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/klib/kotlin.base.klib.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/klib/kotlin.base.klib.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/line-markers/kotlin.base.line-markers.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/line-markers/kotlin.base.line-markers.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/obsolete-compat/kotlin.base.obsolete-compat.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/obsolete-compat/kotlin.base.obsolete-compat.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/platforms/kotlin.base.platforms.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/platforms/kotlin.base.platforms.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/plugin/kotlin.base.plugin.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/plugin/kotlin.base.plugin.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/project-model/kotlin.base.project-model.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/project-model/kotlin.base.project-model.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/project-structure/kotlin.base.project-structure.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/project-structure/kotlin.base.project-structure.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/psi/kotlin.base.psi.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/psi/kotlin.base.psi.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/resources/kotlin.base.resources.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/resources/kotlin.base.resources.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/scripting/kotlin.base.scripting.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/scripting/kotlin.base.scripting.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/statistics/kotlin.base.statistics.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/statistics/kotlin.base.statistics.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/test/kotlin.base.test.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/test/kotlin.base.test.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/base/util/kotlin.base.util.iml" filepath="$PROJECT_DIR$/plugins/kotlin/base/util/kotlin.base.util.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/api/kotlin.code-insight.api.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/api/kotlin.code-insight.api.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/descriptions/kotlin.code-insight.descriptions.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/descriptions/kotlin.code-insight.descriptions.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/impl-base/kotlin.code-insight.impl-base.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/impl-base/kotlin.code-insight.impl-base.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-k2/kotlin.code-insight.inspections-k2.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-k2/kotlin.code-insight.inspections-k2.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-k2/tests/kotlin.code-insight.inspections-k2.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-k2/tests/kotlin.code-insight.inspections-k2.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-shared/kotlin.code-insight.inspections-shared.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-shared/kotlin.code-insight.inspections-shared.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-shared/tests/k1/kotlin.code-insight.inspections-shared.tests.k1.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-shared/tests/k1/kotlin.code-insight.inspections-shared.tests.k1.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-shared/tests/k2/kotlin.code-insight.inspections-shared.tests.k2.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/inspections-shared/tests/k2/kotlin.code-insight.inspections-shared.tests.k2.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-k2/kotlin.code-insight.intentions-k2.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-k2/kotlin.code-insight.intentions-k2.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-k2/tests/kotlin.code-insight.intentions-k2.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-k2/tests/kotlin.code-insight.intentions-k2.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-shared/kotlin.code-insight.intentions-shared.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-shared/kotlin.code-insight.intentions-shared.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-shared/tests/k1/kotlin.code-insight.intentions-shared.tests.k1.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-shared/tests/k1/kotlin.code-insight.intentions-shared.tests.k1.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-shared/tests/k2/kotlin.code-insight.intentions-shared.tests.k2.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/intentions-shared/tests/k2/kotlin.code-insight.intentions-shared.tests.k2.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/code-insight/utils/kotlin.code-insight.utils.iml" filepath="$PROJECT_DIR$/plugins/kotlin/code-insight/utils/kotlin.code-insight.utils.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/allopen/common/kotlin.compiler-plugins.allopen.common.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/allopen/common/kotlin.compiler-plugins.allopen.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/allopen/gradle/kotlin.compiler-plugins.allopen.gradle.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/allopen/gradle/kotlin.compiler-plugins.allopen.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/allopen/maven/kotlin.compiler-plugins.allopen.maven.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/allopen/maven/kotlin.compiler-plugins.allopen.maven.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/allopen/tests/kotlin.compiler-plugins.allopen.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/allopen/tests/kotlin.compiler-plugins.allopen.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/android-extensions-stubs/kotlin.compiler-plugins.android-extensions-stubs.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/android-extensions-stubs/kotlin.compiler-plugins.android-extensions-stubs.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/compiler-plugin-support/common/kotlin.compiler-plugins.compiler-plugin-support.common.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/compiler-plugin-support/common/kotlin.compiler-plugins.compiler-plugin-support.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/compiler-plugin-support/gradle/kotlin.compiler-plugins.compiler-plugin-support.gradle.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/compiler-plugin-support/gradle/kotlin.compiler-plugins.compiler-plugin-support.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/compiler-plugin-support/maven/kotlin.compiler-plugins.compiler-plugin-support.maven.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/compiler-plugin-support/maven/kotlin.compiler-plugins.compiler-plugin-support.maven.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kapt/kotlin.compiler-plugins.kapt.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kapt/kotlin.compiler-plugins.kapt.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kotlinx-serialization/common/kotlin.compiler-plugins.kotlinx-serialization.common.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kotlinx-serialization/common/kotlin.compiler-plugins.kotlinx-serialization.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kotlinx-serialization/gradle/kotlin.compiler-plugins.kotlinx-serialization.gradle.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kotlinx-serialization/gradle/kotlin.compiler-plugins.kotlinx-serialization.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kotlinx-serialization/maven/kotlin.compiler-plugins.kotlinx-serialization.maven.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kotlinx-serialization/maven/kotlin.compiler-plugins.kotlinx-serialization.maven.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kotlinx-serialization/tests/kotlin.compiler-plugins.kotlinx-serialization.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/kotlinx-serialization/tests/kotlin.compiler-plugins.kotlinx-serialization.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/lombok/gradle/kotlin.compiler-plugins.lombok.gradle.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/lombok/gradle/kotlin.compiler-plugins.lombok.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/lombok/maven/kotlin.compiler-plugins.lombok.maven.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/lombok/maven/kotlin.compiler-plugins.lombok.maven.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/noarg/common/kotlin.compiler-plugins.noarg.common.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/noarg/common/kotlin.compiler-plugins.noarg.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/noarg/gradle/kotlin.compiler-plugins.noarg.gradle.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/noarg/gradle/kotlin.compiler-plugins.noarg.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/noarg/maven/kotlin.compiler-plugins.noarg.maven.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/noarg/maven/kotlin.compiler-plugins.noarg.maven.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/noarg/tests/kotlin.compiler-plugins.noarg.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/noarg/tests/kotlin.compiler-plugins.noarg.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/parcelize/common/kotlin.compiler-plugins.parcelize.common.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/parcelize/common/kotlin.compiler-plugins.parcelize.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/parcelize/gradle/kotlin.compiler-plugins.parcelize.gradle.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/parcelize/gradle/kotlin.compiler-plugins.parcelize.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/parcelize/tests/kotlin.compiler-plugins.parcelize.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/parcelize/tests/kotlin.compiler-plugins.parcelize.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/sam-with-receiver/common/kotlin.compiler-plugins.sam-with-receiver.common.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/sam-with-receiver/common/kotlin.compiler-plugins.sam-with-receiver.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/sam-with-receiver/gradle/kotlin.compiler-plugins.sam-with-receiver.gradle.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/sam-with-receiver/gradle/kotlin.compiler-plugins.sam-with-receiver.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/sam-with-receiver/maven/kotlin.compiler-plugins.sam-with-receiver.maven.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/sam-with-receiver/maven/kotlin.compiler-plugins.sam-with-receiver.maven.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-plugins/scripting/kotlin.compiler-plugins.scripting.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-plugins/scripting/kotlin.compiler-plugins.scripting.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-reference-index/kotlin.compiler-reference-index.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-reference-index/kotlin.compiler-reference-index.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/compiler-reference-index/tests/kotlin.compiler-reference-index.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/compiler-reference-index/tests/kotlin.compiler-reference-index.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/copyright/kotlin.copyright.iml" filepath="$PROJECT_DIR$/plugins/kotlin/copyright/kotlin.copyright.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/copyright/tests/kotlin.copyright.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/copyright/tests/kotlin.copyright.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/core/kotlin.core.iml" filepath="$PROJECT_DIR$/plugins/kotlin/core/kotlin.core.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/core/tests/kotlin.core.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/core/tests/kotlin.core.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/coverage/kotlin.coverage.iml" filepath="$PROJECT_DIR$/plugins/kotlin/coverage/kotlin.coverage.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/coverage/tests/kotlin.coverage.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/coverage/tests/kotlin.coverage.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/features-trainer/kotlin.features-trainer.iml" filepath="$PROJECT_DIR$/plugins/kotlin/features-trainer/kotlin.features-trainer.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/features-trainer/tests/kotlin.features-trainer.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/features-trainer/tests/kotlin.features-trainer.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/fir/kotlin.fir.iml" filepath="$PROJECT_DIR$/plugins/kotlin/fir/kotlin.fir.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/all-tests-fir/kotlin.fir-all-tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/all-tests-fir/kotlin.fir-all-tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/fir-low-level-api-ide-impl/kotlin.fir.fir-low-level-api-ide-impl.iml" filepath="$PROJECT_DIR$/plugins/kotlin/fir-low-level-api-ide-impl/kotlin.fir.fir-low-level-api-ide-impl.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/frontend-independent/kotlin.fir.frontend-independent.iml" filepath="$PROJECT_DIR$/plugins/kotlin/frontend-independent/kotlin.fir.frontend-independent.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/frontend-independent/tests/kotlin.fir.frontend-independent.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/frontend-independent/tests/kotlin.fir.frontend-independent.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/formatter/kotlin.formatter.iml" filepath="$PROJECT_DIR$/plugins/kotlin/formatter/kotlin.formatter.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/git/kotlin.git.iml" filepath="$PROJECT_DIR$/plugins/kotlin/git/kotlin.git.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/gradle/code-insight-common/kotlin.gradle.code-insight-common.iml" filepath="$PROJECT_DIR$/plugins/kotlin/gradle/code-insight-common/kotlin.gradle.code-insight-common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/gradle/code-insight-groovy/kotlin.gradle.code-insight-groovy.iml" filepath="$PROJECT_DIR$/plugins/kotlin/gradle/code-insight-groovy/kotlin.gradle.code-insight-groovy.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/gradle/gradle/kotlin.gradle.gradle.iml" filepath="$PROJECT_DIR$/plugins/kotlin/gradle/gradle/kotlin.gradle.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/gradle/gradle-java/kotlin.gradle.gradle-java.iml" filepath="$PROJECT_DIR$/plugins/kotlin/gradle/gradle-java/kotlin.gradle.gradle-java.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/gradle/gradle-java/tests/kotlin.gradle.gradle-java.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/gradle/gradle-java/tests/kotlin.gradle.gradle-java.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/gradle/gradle-tooling/kotlin.gradle.gradle-tooling.iml" filepath="$PROJECT_DIR$/plugins/kotlin/gradle/gradle-tooling/kotlin.gradle.gradle-tooling.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/gradle/gradle-tooling/tests/kotlin.gradle.gradle-tooling.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/gradle/gradle-tooling/tests/kotlin.gradle.gradle-tooling.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/grazie/kotlin.grazie.iml" filepath="$PROJECT_DIR$/plugins/kotlin/grazie/kotlin.grazie.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/i18n/kotlin.i18n.iml" filepath="$PROJECT_DIR$/plugins/kotlin/i18n/kotlin.i18n.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/i18n/tests/kotlin.i18n.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/i18n/tests/kotlin.i18n.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/idea/kotlin.idea.iml" filepath="$PROJECT_DIR$/plugins/kotlin/idea/kotlin.idea.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/idea/tests/kotlin.idea.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/idea/tests/kotlin.idea.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/injection/kotlin.injection.iml" filepath="$PROJECT_DIR$/plugins/kotlin/injection/kotlin.injection.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/inspections/kotlin.inspections.iml" filepath="$PROJECT_DIR$/plugins/kotlin/inspections/kotlin.inspections.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/inspections-fe10/kotlin.inspections-fe10.iml" filepath="$PROJECT_DIR$/plugins/kotlin/inspections-fe10/kotlin.inspections-fe10.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/j2k/idea/kotlin.j2k.idea.iml" filepath="$PROJECT_DIR$/plugins/kotlin/j2k/idea/kotlin.j2k.idea.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/j2k/new/kotlin.j2k.new.iml" filepath="$PROJECT_DIR$/plugins/kotlin/j2k/new/kotlin.j2k.new.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/j2k/new/tests/kotlin.j2k.new.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/j2k/new/tests/kotlin.j2k.new.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/j2k/old/kotlin.j2k.old.iml" filepath="$PROJECT_DIR$/plugins/kotlin/j2k/old/kotlin.j2k.old.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/j2k/old/tests/kotlin.j2k.old.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/j2k/old/tests/kotlin.j2k.old.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/j2k/services/kotlin.j2k.services.iml" filepath="$PROJECT_DIR$/plugins/kotlin/j2k/services/kotlin.j2k.services.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm/kotlin.jvm.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm/kotlin.jvm.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/base/util/kotlin.jvm-debugger.base.util.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/base/util/kotlin.jvm-debugger.base.util.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/core/kotlin.jvm-debugger.core.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/core/kotlin.jvm-debugger.core.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/core-fe10/kotlin.jvm-debugger.core-fe10.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/core-fe10/kotlin.jvm-debugger.core-fe10.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/coroutines/kotlin.jvm-debugger.coroutines.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/coroutines/kotlin.jvm-debugger.coroutines.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/eval4j/kotlin.jvm-debugger.eval4j.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/eval4j/kotlin.jvm-debugger.eval4j.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/evaluation/kotlin.jvm-debugger.evaluation.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/evaluation/kotlin.jvm-debugger.evaluation.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/sequence/kotlin.jvm-debugger.sequence.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/sequence/kotlin.jvm-debugger.sequence.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/test/kotlin.jvm-debugger.test.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/test/kotlin.jvm-debugger.test.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/test/k2/kotlin.jvm-debugger.test.k2.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/test/k2/kotlin.jvm-debugger.test.k2.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-debugger/util/kotlin.jvm-debugger.util.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-debugger/util/kotlin.jvm-debugger.util.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/jvm-decompiler/kotlin.jvm-decompiler.iml" filepath="$PROJECT_DIR$/plugins/kotlin/jvm-decompiler/kotlin.jvm-decompiler.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/k2-fe10-bindings/kotlin.k2.fe10-bindings.iml" filepath="$PROJECT_DIR$/plugins/kotlin/k2-fe10-bindings/kotlin.k2.fe10-bindings.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/line-indent-provider/kotlin.line-indent-provider.iml" filepath="$PROJECT_DIR$/plugins/kotlin/line-indent-provider/kotlin.line-indent-provider.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/maven/kotlin.maven.iml" filepath="$PROJECT_DIR$/plugins/kotlin/maven/kotlin.maven.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/maven/tests/kotlin.maven.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/maven/tests/kotlin.maven.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/migration/kotlin.migration.iml" filepath="$PROJECT_DIR$/plugins/kotlin/migration/kotlin.migration.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/ml-completion/kotlin.ml-completion.iml" filepath="$PROJECT_DIR$/plugins/kotlin/ml-completion/kotlin.ml-completion.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/native/kotlin.native.iml" filepath="$PROJECT_DIR$/plugins/kotlin/native/kotlin.native.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/native/tests/kotlin.native.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/native/tests/kotlin.native.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/navigation/kotlin.navigation.iml" filepath="$PROJECT_DIR$/plugins/kotlin/navigation/kotlin.navigation.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/navigation/tests/kotlin.navigation.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/navigation/tests/kotlin.navigation.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/performance-tests/kotlin.performance-tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/performance-tests/kotlin.performance-tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/performance-tests/performance-test-utils/kotlin.performance-tests.performance-test-utils.iml" filepath="$PROJECT_DIR$/plugins/kotlin/performance-tests/performance-test-utils/kotlin.performance-tests.performance-test-utils.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/plugin/kotlin.plugin.iml" filepath="$PROJECT_DIR$/plugins/kotlin/plugin/kotlin.plugin.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/plugin-updater/kotlin.plugin-updater.iml" filepath="$PROJECT_DIR$/plugins/kotlin/plugin-updater/kotlin.plugin-updater.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/plugin/common/kotlin.plugin.common.iml" filepath="$PROJECT_DIR$/plugins/kotlin/plugin/common/kotlin.plugin.common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/plugin/k1/kotlin.plugin.k1.iml" filepath="$PROJECT_DIR$/plugins/kotlin/plugin/k1/kotlin.plugin.k1.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/plugin/k2/kotlin.plugin.k2.iml" filepath="$PROJECT_DIR$/plugins/kotlin/plugin/k2/kotlin.plugin.k2.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/preferences/kotlin.preferences.iml" filepath="$PROJECT_DIR$/plugins/kotlin/preferences/kotlin.preferences.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-configuration/kotlin.project-configuration.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-configuration/kotlin.project-configuration.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-tests/project-tests-base/kotlin.project-tests.project-tests-base.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-tests/project-tests-base/kotlin.project-tests.project-tests-base.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-tests/project-tests-fe10/kotlin.project-tests.project-tests-fe10.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-tests/project-tests-fe10/kotlin.project-tests.project-tests-fe10.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-tests/project-tests-fir/kotlin.project-tests.project-tests-fir.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-tests/project-tests-fir/kotlin.project-tests.project-tests-fir.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-wizard/cli/kotlin.project-wizard.cli.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-wizard/cli/kotlin.project-wizard.cli.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-wizard/compose/kotlin.project-wizard.compose.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-wizard/compose/kotlin.project-wizard.compose.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-wizard/core/kotlin.project-wizard.core.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-wizard/core/kotlin.project-wizard.core.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-wizard/gradle/kotlin.project-wizard.gradle.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-wizard/gradle/kotlin.project-wizard.gradle.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-wizard/idea/kotlin.project-wizard.idea.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-wizard/idea/kotlin.project-wizard.idea.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-wizard/maven/kotlin.project-wizard.maven.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-wizard/maven/kotlin.project-wizard.maven.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/project-wizard/tests/kotlin.project-wizard.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/project-wizard/tests/kotlin.project-wizard.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/properties/kotlin.properties.iml" filepath="$PROJECT_DIR$/plugins/kotlin/properties/kotlin.properties.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/repl/kotlin.repl.iml" filepath="$PROJECT_DIR$/plugins/kotlin/repl/kotlin.repl.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/run-configurations/junit/kotlin.run-configurations.junit.iml" filepath="$PROJECT_DIR$/plugins/kotlin/run-configurations/junit/kotlin.run-configurations.junit.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/run-configurations/junit-fe10/kotlin.run-configurations.junit-fe10.iml" filepath="$PROJECT_DIR$/plugins/kotlin/run-configurations/junit-fe10/kotlin.run-configurations.junit-fe10.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/run-configurations/jvm/kotlin.run-configurations.jvm.iml" filepath="$PROJECT_DIR$/plugins/kotlin/run-configurations/jvm/kotlin.run-configurations.jvm.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/run-configurations/testng/kotlin.run-configurations.testng.iml" filepath="$PROJECT_DIR$/plugins/kotlin/run-configurations/testng/kotlin.run-configurations.testng.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/scripting/kotlin.scripting.iml" filepath="$PROJECT_DIR$/plugins/kotlin/scripting/kotlin.scripting.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/scripting-support/kotlin.scripting-support.iml" filepath="$PROJECT_DIR$/plugins/kotlin/scripting-support/kotlin.scripting-support.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/spellchecker/kotlin.spellchecker.iml" filepath="$PROJECT_DIR$/plugins/kotlin/spellchecker/kotlin.spellchecker.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/test-framework/kotlin.test-framework.iml" filepath="$PROJECT_DIR$/plugins/kotlin/test-framework/kotlin.test-framework.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/tests-common/kotlin.tests-common.iml" filepath="$PROJECT_DIR$/plugins/kotlin/tests-common/kotlin.tests-common.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/tests-from-compiler/kotlin.testsFromCompiler.iml" filepath="$PROJECT_DIR$/plugins/kotlin/tests-from-compiler/kotlin.testsFromCompiler.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin/kotlin.uast.uast-kotlin.iml" filepath="$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin/kotlin.uast.uast-kotlin.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-base/kotlin.uast.uast-kotlin-base.iml" filepath="$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-base/kotlin.uast.uast-kotlin-base.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-fir/kotlin.uast.uast-kotlin-fir.iml" filepath="$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-fir/kotlin.uast.uast-kotlin-fir.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-idea/kotlin.uast.uast-kotlin-idea.iml" filepath="$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-idea/kotlin.uast.uast-kotlin-idea.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-idea-base/kotlin.uast.uast-kotlin-idea-base.iml" filepath="$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-idea-base/kotlin.uast.uast-kotlin-idea-base.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-idea-fir/kotlin.uast.uast-kotlin-idea-fir.iml" filepath="$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-idea-fir/kotlin.uast.uast-kotlin-idea-fir.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-idea/tests/kotlin.uast.uast-kotlin-idea.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin-idea/tests/kotlin.uast.uast-kotlin-idea.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin/tests/kotlin.uast.uast-kotlin.tests.iml" filepath="$PROJECT_DIR$/plugins/kotlin/uast/uast-kotlin/tests/kotlin.uast.uast-kotlin.tests.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/kotlin-compiler-classpath/kotlin.util.compiler-classpath.iml" filepath="$PROJECT_DIR$/plugins/kotlin/kotlin-compiler-classpath/kotlin.util.compiler-classpath.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/util/compiler-dependencies/kotlin.util.compiler-dependencies.iml" filepath="$PROJECT_DIR$/plugins/kotlin/util/compiler-dependencies/kotlin.util.compiler-dependencies.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/util/project-model-updater/kotlin.util.project-model-updater.iml" filepath="$PROJECT_DIR$/plugins/kotlin/util/project-model-updater/kotlin.util.project-model-updater.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/util/test-generator/kotlin.util.test-generator.iml" filepath="$PROJECT_DIR$/plugins/kotlin/util/test-generator/kotlin.util.test-generator.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/util/test-generator-fe10/kotlin.util.test-generator-fe10.iml" filepath="$PROJECT_DIR$/plugins/kotlin/util/test-generator-fe10/kotlin.util.test-generator-fe10.iml" />
 +      <module fileurl="file://$PROJECT_DIR$/plugins/kotlin/util/test-generator-fir/kotlin.util.test-generator-fir.iml" filepath="$PROJECT_DIR$/plugins/kotlin/util/test-generator-fir/kotlin.util.test-generator-fir.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/native-symbolizer/native-symbolizer.iml" filepath="$PROJECT_DIR$/android/native-symbolizer/native-symbolizer.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/perf-logger.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/perf-logger.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/android/prebuilts/sdktools/usb-devices.iml" filepath="$PROJECT_DIR$/android/android/prebuilts/sdktools/usb-devices.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/utp/utp.iml" filepath="$PROJECT_DIR$/android/utp/utp.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/whats-new-assistant/whats-new-assistant.iml" filepath="$PROJECT_DIR$/android/whats-new-assistant/whats-new-assistant.iml" />
++      <module fileurl="file://$PROJECT_DIR$/android/whats-new-assistant/whats-new-assistant-test.iml" filepath="$PROJECT_DIR$/android/whats-new-assistant/whats-new-assistant-test.iml" />
      </modules>
    </component>
  </project>
index 9f43412211397176483e1e2d9ad920f9604f7fa6,0000000000000000000000000000000000000000..60a846b5ce5d7d381fb71db3c25576b14956877b
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,153 @@@
 +### Starter for IntelliJ IDEA based IDE's
 +
 +#### Overview
 +
 +Starter helps you write tests/code, that will start IntelliJ-based IDE from installer in external process.
 +Aside from that, you may find useful functionality as below:
 +
 +* execution commands in plugins (list of available commands described below)
 +* implementing your custom command to be invoked later in tests
 +* execution custom code (though, you cannot use external libraries here)
 +* integration with CI (optional)
 +* collecting test artifacts
 +* reporting of artifacts to CI (optional)
 +* run a test with a profiler (not yet included)
 +
 +#### Supported products
 +
 +* IDEA
 +* GoLand
 +* WebStorm
 +* PhpStorm
 +* DataGrip
 +* PyCharm
 +* RubyMine
 +* Android Studio
 +
 +##### How to setup
 +
 +Configure maven repositories in your `build.gradle` file
 +
 +```
 +repositories {
 +  maven { url = "https://cache-redirector.jetbrains.com/maven-central" }
 +  maven { url = "https://cache-redirector.jetbrains.com/intellij-dependencies" }
 +
 +  maven { url = "https://www.jetbrains.com/intellij-repository/releases" }
 +}
 +```
 +
 +Instead of `maven { url = "https://cache-redirector.jetbrains.com/maven-central" }` you may use `mavenCentral()`
 +
 +If you're sure, that you need more recent version of packages, you might use
 +`maven { url = "https://www.jetbrains.com/intellij-repository/snapshots" }`
 +OR
 +`maven { url = "https://www.jetbrains.com/intellij-repository/nightly" }`
 +
 +But don't forget to change accordingly version of the packages as such:
++
 +* nightly -> LATEST-TRUNK-SNAPSHOT
 +* snapshots -> LATEST-EAP-SNAPSHOT
 +* releases -> semver package version
 +
 +Minimal setup example:
 +
 +```
 +dependencies {
 +  testImplementation("com.jetbrains.intellij.ide:ide-starter:LATEST-EAP-SNAPSHOT")
 +  testImplementation("com.jetbrains.intellij.performanceTesting:performance-testing-commands:LATEST-EAP-SNAPSHOT")
 +  
 +  testImplementation("junit:junit:4.13.2")
 +}
 +```
 +
 +To make sure, that you will not get problem with Kotlin Duration, add the following
 +
 +```
 +compileTestKotlin {
 +  sourceCompatibility = JavaVersion.VERSION_11
 +  targetCompatibility = JavaVersion.VERSION_11
 +
 +  kotlinOptions {
 +    jvmTarget = JavaVersion.VERSION_11.toString()
 +    freeCompilerArgs += [
 +      "-Xopt-in=kotlin.time.ExperimentalTime"
 +    ]
 +  }
 +}
 +
 +```
 +
 +##### Run with JUnit4
 +
 +[Example of simple test, that will download IntelliJ IDEA and start import of gradle project](https://github.com/JetBrains/intellij-ide-starter/tree/master/testSrc/com/intellij/ide/starter/tests/examples/junit4)
 +
 +You should create appropriate classes in your tests for JUnit4StarterRule, IdeaCases (we don't provide that as an artifact yet)
 +
 +##### Run with JUnit5
 +
 +[Example of simple test, that will download IntelliJ IDEA and start import of gradle project](https://github.com/JetBrains/intellij-ide-starter/tree/master/testSrc/com/intellij/ide/starter/tests/examples/junit5)
 +
 +##### Available commands from plugins
 +
 +Dependency `performance-testing-commands`
++
 +- waitForSmartMode()
 +- flushIndexes()
 +- setupProjectSdk(sdkName: String, sdkType: String, sdkPath: String)
 +- setupProjectSdk(sdkObject: SdkObject)
 +- setupProjectJdk(sdkName: String, sdkPath: String) = setupProjectSdk(sdkName, "JavaSDK", sdkPath)
 +- openFile(relativePath: String)
 +- openProject(projectPath: Path)
 +- reopenProject()
 +- goto(line: String, column: String)
 +- findUsages()
 +- inspectCode()
 +- checkOnRedCode()
 +- exitApp(forceExit: Boolean = true)
 +- exitAppWithTimeout(timeoutInSeconds: Long)
 +- memoryDump()
 +- dumpProjectFiles()
 +- compareProjectFiles(firstDir: String, secondDir: String)
 +- cleanCaches()
 +- doComplete()
 +- doComplete(times: Int)
 +- openProjectView()
 +- pressKey(key: String)
 +- delayType(command: String)
 +- doLocalInspection()
 +- altEnter(intention: String)
 +- callAltEnter(times: Int, intention: String = "")
 +- createAllServicesAndExtensions()
 +- runConfiguration(command: String)
 +- openFileWithTerminate(relativePath: String, terminateIdeInSeconds: Long)
 +- searchEverywhere(text: String)
 +- storeIndices()
 +- compareIndices()
 +- recoveryAction(action: RecoveryActionType)
 +- ... **TBD**
 +
 +Dependency `performance-testing-maven-commands`
++
 +- importMavenProject()
 +
 +Dependency `performance-testing-gradle-commands`
++
 +- importGradleProject()
 +
 +#### What behaviour might be extended / modified
 +
 +Everything, that initializes via DI framework (Kodein DI) might be modified in your code for your need.   
 +[DI container initialization](https://github.com/JetBrains/intellij-community/blob/master/tools/intellij.ide.starter/src/com/intellij/ide/starter/di/diContainer.kt)  
 +For example, you might write your own implementation of CIServer and provide it via DI.
 +
 +NOTE: Be sure to use the same version of Kodein, that is used in `build.gradle` for starter project.
 +
 +E.g:
++
 +```
 +di = DI {
 +      extend(di)
 +      bindSingleton<CIServer>(overrides = true) { YourImplementationOfCI() }
 +}
 +```
index 3e6666d2ada26b4e4bc10bc59ddb13cbc6c8ce52,0000000000000000000000000000000000000000..62f4b3359d39ae396eec40d672482cde8bf4112b
mode 100644,000000..100644
--- /dev/null
@@@ -1,73 -1,0 +1,75 @@@
-       <configuration version="3" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false">
 +<?xml version="1.0" encoding="UTF-8"?>
 +<module type="JAVA_MODULE" version="4">
 +  <component name="FacetManager">
 +    <facet type="kotlin-language" name="Kotlin">
-           <option name="additionalArguments" value="-version -Xjvm-default=enable -Xopt-in=kotlin.time.ExperimentalTime" />
++      <configuration version="5" platform="JVM 11" allPlatforms="JVM [11]" useProjectSettings="false">
 +        <compilerSettings>
-           <option name="jvmTarget" value="11" />
-           <option name="languageVersion" value="1.7" />
-           <option name="apiVersion" value="1.7" />
++          <option name="additionalArguments" value="-version -Xjvm-default=enable -opt-in=kotlin.time.ExperimentalTime" />
 +        </compilerSettings>
 +        <compilerArguments>
++          <stringArguments>
++            <stringArg name="jvmTarget" arg="11" />
++            <stringArg name="apiVersion" arg="1.7" />
++            <stringArg name="languageVersion" arg="1.7" />
++          </stringArguments>
 +        </compilerArguments>
 +      </configuration>
 +    </facet>
 +  </component>
 +  <component name="NewModuleRootManager" inherit-compiler-output="true">
 +    <exclude-output />
 +    <content url="file://$MODULE_DIR$">
 +      <sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
 +      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
 +    </content>
 +    <orderEntry type="inheritedJdk" />
 +    <orderEntry type="sourceFolder" forTests="false" />
 +    <orderEntry type="library" name="kotlinx-coroutines-jdk8" level="project" />
 +    <orderEntry type="library" name="kotlin-stdlib-jdk8" level="project" />
 +    <orderEntry type="library" name="kotlin-reflect" level="project" />
 +    <orderEntry type="library" name="jackson" level="project" />
 +    <orderEntry type="library" name="jackson-databind" level="project" />
 +    <orderEntry type="library" name="jackson-module-kotlin" level="project" />
 +    <orderEntry type="library" name="http-client" level="project" />
 +    <orderEntry type="library" name="Gradle" level="project" />
 +    <orderEntry type="library" name="kodein-di-jvm" level="project" />
 +    <orderEntry type="module-library">
 +      <library name="rauschig.jarchivelib" type="repository">
 +        <properties maven-id="org.rauschig:jarchivelib:1.2.0" />
 +        <CLASSES>
 +          <root url="jar://$MAVEN_REPOSITORY$/org/rauschig/jarchivelib/1.2.0/jarchivelib-1.2.0.jar!/" />
 +          <root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-compress/1.21/commons-compress-1.21.jar!/" />
 +        </CLASSES>
 +        <JAVADOC />
 +        <SOURCES />
 +        <excluded>
 +          <root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-compress/1.21/commons-compress-1.21.jar!/" />
 +        </excluded>
 +      </library>
 +    </orderEntry>
 +    <orderEntry type="library" name="commons-logging" level="project" />
 +    <orderEntry type="module-library">
 +      <library name="jackson-datatype-jsr310" type="repository">
 +        <properties include-transitive-deps="false" maven-id="com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.1" />
 +        <CLASSES>
 +          <root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.13.1/jackson-datatype-jsr310-2.13.1.jar!/" />
 +        </CLASSES>
 +        <JAVADOC />
 +        <SOURCES />
 +      </library>
 +    </orderEntry>
 +    <orderEntry type="module-library">
 +      <library name="qodana-sarif" type="repository">
 +        <properties include-transitive-deps="false" maven-id="com.jetbrains.qodana:qodana-sarif:0.1.95" />
 +        <CLASSES>
 +          <root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/qodana/qodana-sarif/0.1.95/qodana-sarif-0.1.95.jar!/" />
 +        </CLASSES>
 +        <JAVADOC />
 +        <SOURCES />
 +      </library>
 +    </orderEntry>
 +    <orderEntry type="library" name="gson" level="project" />
 +    <orderEntry type="module" module-name="intellij.platform.lang.impl" />
 +    <orderEntry type="module" module-name="intellij.platform.ide.impl" />
 +  </component>
 +</module>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af6438896daa4b52aaa0577028a8d0db05494b05
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++# JVM TI agent that logs loading and preparation stages of class lifecycle
++# Set JAVA_HOME to JBR 17 location
++
++# Usage VM Options: -agentpath:/var/tmp/libvmtrace.so=output.log
++# Original source: https://github.com/odnoklassniki/jvmti-tools#vmtrace
++
++build-linux:
++      g++ -O2 -fPIC -shared -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/linux -olibvmtrace.so vmtrace.cpp
++
++build-macos:
++      g++ -O2 -fPIC -shared -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/darwin -olibvmtrace.dylib vmtrace.cpp
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..244c4cd835ef3f8539c39ccd0af74e2b6012e7b6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++/*
++ * Copyright 2019 Odnoklassniki Ltd, Mail.Ru Group
++ *
++ * Licensed under the Apache License, Version 2.0 (the "License");
++ * you may not use this file except in compliance with the License.
++ * You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++// Original source: https://github.com/odnoklassniki/jvmti-tools#vmtrace
++
++#include <jvmti.h>
++#include <stdarg.h>
++#include <string.h>
++#include <stdio.h>
++
++static FILE* out;
++static jrawMonitorID vmtrace_lock;
++static jlong start_time;
++
++static void trace(jvmtiEnv* jvmti, const char* fmt, ...) {
++    jlong current_time;
++    jvmti->GetTime(&current_time);
++
++    char buf[1024];
++    va_list args;
++    va_start(args, fmt);
++    vsnprintf(buf, sizeof(buf), fmt, args);
++    va_end(args);
++
++    jvmti->RawMonitorEnter(vmtrace_lock);
++
++    fprintf(out, "[%.5f] %s\n", (current_time - start_time) / 1000000000.0, buf);
++    
++    jvmti->RawMonitorExit(vmtrace_lock);
++}
++
++static char* fix_class_name(char* class_name) {
++    // Strip 'L' and ';' from class signature
++    class_name[strlen(class_name) - 1] = 0;
++    return class_name + 1;
++}
++
++class ClassName {
++  private:
++    jvmtiEnv* _jvmti;
++    char* _name;
++
++  public:
++    ClassName(jvmtiEnv* jvmti, jclass klass) : _jvmti(jvmti), _name(NULL) {
++        _jvmti->GetClassSignature(klass, &_name, NULL);
++    }
++
++    ~ClassName() {
++        _jvmti->Deallocate((unsigned char*) _name);
++    }
++
++    char* name() {
++        return _name == NULL ? NULL : fix_class_name(_name);
++    }
++};
++
++void JNICALL ClassFileLoadHook(jvmtiEnv* jvmti, JNIEnv* env,
++                               jclass class_being_redefined, jobject loader,
++                               const char* name, jobject protection_domain,
++                               jint data_len, const unsigned char* data,
++                               jint* new_data_len, unsigned char** new_data) {
++    trace(jvmti, "Loading class: %s (%d bytes)", name, data_len);
++}
++
++void JNICALL ClassPrepare(jvmtiEnv* jvmti, JNIEnv* env,
++                          jthread thread, jclass klass) {
++    ClassName cn(jvmti, klass);
++    trace(jvmti, "Class prepared: %s", cn.name());
++}
++
++JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
++    if (options == NULL || !options[0]) {
++        out = stderr;
++    } else if ((out = fopen(options, "w")) == NULL) {
++        fprintf(stderr, "Cannot open output file: %s\n", options);
++        return 1;
++    }
++
++    jvmtiEnv* jvmti;
++    vm->GetEnv((void**) &jvmti, JVMTI_VERSION_1_0);
++
++    jvmti->CreateRawMonitor("vmtrace_lock", &vmtrace_lock);
++    jvmti->GetTime(&start_time);
++
++    trace(jvmti, "VMTrace started");
++
++    jvmtiCapabilities capabilities = {0};
++    capabilities.can_generate_all_class_hook_events = 1;
++    jvmti->AddCapabilities(&capabilities);
++
++    jvmtiEventCallbacks callbacks = {0};
++    callbacks.ClassFileLoadHook = ClassFileLoadHook;
++    callbacks.ClassPrepare = ClassPrepare;
++    jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
++
++    jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
++    jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);
++
++    return 0;
++}
++
++JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm) {
++    if (out != NULL && out != stderr) {
++        fclose(out);
++    }
++}
index ef5d50ba15a3bf31029a44185b7a922e68c7be0a,0000000000000000000000000000000000000000..1ca3a5dd93ce1d73b50faef573e12945c5fc55f6
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,13 @@@
-     <option name="useSafeWrite" value="false" />
 +<application>
 +  <component name="GeneralSettings">
++    <option name="useSafeWrite" value="false"/>
 +    <option name="confirmExit" value="false"/>
 +    <option name="showTipsOnStartup" value="false"/>
 +  </component>
 +  <component name="Registry">
 +    <entry key="compiler.automake.allow.parallel" value="true"/>
 +    <entry key="vcs.log.index.git" value="false"/>
 +    <entry key="vcs.log.keep.up.to.date" value="false"/>
 +    <entry key="actionSystem.playback.typecommand.delay" value="0"/>
 +  </component>
 +</application>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf0582a489fec8cd7a1343bf2cc82fe660eabf11
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d072158fdaf92e6dac4085460d78effb7f66635
new file mode 100644 (file)
Binary files differ
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..70489e9c4ae504b792bfc749e4795ebc3f93ceef
new file mode 100755 (executable)
Binary files differ
index fa0fa6e036061216ce478a87f33f8b29cb7174e3,0000000000000000000000000000000000000000..1c2194466489c1a6432453a3c94284c2031fbddd
mode 100644,000000..100644
--- /dev/null
@@@ -1,162 -1,0 +1,164 @@@
- import com.intellij.ide.starter.exec.ExecOutputRedirect
- import com.intellij.ide.starter.exec.exec
 +package com.intellij.ide.starter
 +
 +import com.intellij.ide.starter.di.di
-     exec(presentablePurpose = "android-sdk-licenses",
-          workDir = home,
-          environmentVariables = envVariablesWithJavaHome,
-          args = listOf(sdkManager.toString(), "--sdk_root=$home", "--licenses"),
-          stderrRedirect = ExecOutputRedirect.ToStdOut("[sdkmanager-err]"),
-          stdInBytes = "yes\n".repeat(10).toByteArray(), // it asks the confirmation at least two times
-          timeout = 15.minutes
-     )
 +import com.intellij.ide.starter.ide.IDETestContext
 +import com.intellij.ide.starter.path.GlobalPaths
++import com.intellij.ide.starter.process.exec.ExecOutputRedirect
++import com.intellij.ide.starter.process.exec.ProcessExecutor
 +import com.intellij.ide.starter.system.SystemInfo
 +import com.intellij.ide.starter.utils.FileSystem
 +import com.intellij.ide.starter.utils.HttpClient
 +import com.intellij.ide.starter.utils.logOutput
 +import com.intellij.ide.starter.utils.resolveInstalledJdk11
 +import org.gradle.internal.hash.Hashing
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import java.io.File
 +import java.nio.file.Path
 +import kotlin.io.path.div
 +import kotlin.io.path.isDirectory
 +import kotlin.time.Duration.Companion.minutes
 +
 +/**
 + * Resolve platform specific android studio installer and return paths
 + * @return Pair<InstallDir / InstallerFile>
 + */
 +fun downloadAndroidStudio(): Pair<Path, File> {
 +  val ext = when {
 +    SystemInfo.isWindows -> "-windows.zip"
 +    SystemInfo.isMac -> "-mac.zip"
 +    SystemInfo.isLinux -> "-linux.tar.gz"
 +    else -> error("Not supported OS")
 +  }
 +
 +  val downloadUrl = "https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2021.1.1.11/android-studio-2021.1.1.11$ext"
 +  val asFileName = downloadUrl.split("/").last()
 +  val globalPaths by di.instance<GlobalPaths>()
 +  val zipFile = globalPaths.getCacheDirectoryFor("android-studio").resolve(asFileName)
 +  HttpClient.downloadIfMissing(downloadUrl, zipFile)
 +
 +  val installDir = globalPaths.getCacheDirectoryFor("builds") / "AI-211"
 +
 +  installDir.toFile().deleteRecursively()
 +
 +  val installerFile = zipFile.toFile()
 +
 +  return Pair(installDir, installerFile)
 +}
 +
 +fun downloadLatestAndroidSdk(javaHome: Path): Path {
 +  val packages = listOf(
 +    "build-tools;28.0.3",
 +    //"cmake;3.10.2.4988404",
 +    //"docs",
 +    //"ndk;20.0.5594570",
 +    "platforms;android-28",
 +    "sources;android-28",
 +    "platform-tools"
 +  )
 +
 +  val sdkManager = downloadSdkManager()
 +
 +  // we use unique home folder per installation to ensure only expected
 +  // packages are included into the SDK home path
 +  val packagesHash = Hashing.sha1().hashString(packages.joinToString("$"))
 +  val home = di.direct.instance<GlobalPaths>().getCacheDirectoryFor("android-sdk") / "sdk-roots" / "sdk-root-$packagesHash"
 +  if (home.isDirectory() && home.toFile().walk().count() > 10) return home
 +
 +  val envVariablesWithJavaHome = System.getenv() + ("JAVA_HOME" to javaHome.toAbsolutePath().toString())
 +
 +  try {
 +    home.toFile().mkdirs()
 +    /// https://stackoverflow.com/questions/38096225/automatically-accept-all-sdk-licences
 +    /// sending "yes" to the process in the STDIN :(
-     exec(presentablePurpose = "android-sdk-loading",
-          workDir = home,
-          environmentVariables = envVariablesWithJavaHome,
-          args = listOf(sdkManager.toString(), "--sdk_root=$home", "--list"),
-          stderrRedirect = ExecOutputRedirect.ToStdOut("[sdkmanager-err]"),
-          timeout = 15.minutes
-     )
++    ProcessExecutor(presentableName = "android-sdk-licenses",
++                    workDir = home,
++                    environmentVariables = envVariablesWithJavaHome,
++                    args = listOf(sdkManager.toString(), "--sdk_root=$home", "--licenses"),
++                    stderrRedirect = ExecOutputRedirect.ToStdOut("[sdkmanager-err]"),
++                    stdInBytes = "yes\n".repeat(10).toByteArray(), // it asks the confirmation at least two times
++                    timeout = 15.minutes
++    ).start()
 +
 +    //loading SDK
-     exec(presentablePurpose = "android-sdk-installing",
-          workDir = home,
-          environmentVariables = envVariablesWithJavaHome,
-          args = listOf(sdkManager.toString(), "--sdk_root=$home", "--install", "--verbose") + packages,
-          stderrRedirect = ExecOutputRedirect.ToStdOut("[sdkmanager-err]"),
-          timeout = 15.minutes
-     )
++    ProcessExecutor(presentableName = "android-sdk-loading",
++                    workDir = home,
++                    environmentVariables = envVariablesWithJavaHome,
++                    args = listOf(sdkManager.toString(), "--sdk_root=$home", "--list"),
++                    stderrRedirect = ExecOutputRedirect.ToStdOut("[sdkmanager-err]"),
++                    timeout = 15.minutes
++    ).start()
 +
 +    //loading SDK
-     exec(
++    ProcessExecutor(presentableName = "android-sdk-installing",
++                    workDir = home,
++                    environmentVariables = envVariablesWithJavaHome,
++                    args = listOf(sdkManager.toString(), "--sdk_root=$home", "--install", "--verbose") + packages,
++                    stderrRedirect = ExecOutputRedirect.ToStdOut("[sdkmanager-err]"),
++                    timeout = 15.minutes
++    ).start()
 +    return home
 +  }
 +  catch (t: Throwable) {
 +    home.toFile().deleteRecursively()
 +    throw Exception("Failed to prepare Android SDK to $home. ${t.message}", t)
 +  }
 +}
 +
 +private fun downloadSdkManager(): Path {
 +  val url = when {
 +    SystemInfo.isMac -> "https://dl.google.com/android/repository/commandlinetools-mac-6200805_latest.zip"
 +    SystemInfo.isWindows -> "https://dl.google.com/android/repository/commandlinetools-win-6200805_latest.zip"
 +    SystemInfo.isLinux -> "https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip"
 +    else -> error("Unsupported OS: ${SystemInfo.OS_NAME} ${SystemInfo.OS_VERSION}")
 +  }
 +
 +  val name = url.split("/").last()
 +  val androidSdkCache = di.direct.instance<GlobalPaths>().getCacheDirectoryFor("android-sdk")
 +  val targetArchive = androidSdkCache / "archives" / name
 +  val targetUnpack = androidSdkCache / "builds" / name
 +  HttpClient.downloadIfMissing(url, targetArchive)
 +
 +  FileSystem.unpackIfMissing(targetArchive, targetUnpack)
 +
 +  val ext = if (SystemInfo.isWindows) ".bat" else ""
 +
 +  @Suppress("SpellCheckingInspection")
 +  val sdkManager = targetUnpack.toFile().walk().first { it.endsWith("tools/bin/sdkmanager$ext") }
 +
 +  if (SystemInfo.isMac || SystemInfo.isLinux) {
 +    sdkManager.setExecutable(true)
 +  }
 +
 +  return sdkManager.toPath()
 +}
 +
 +fun main() {
 +  downloadLatestAndroidSdk(resolveInstalledJdk11())
 +}
 +
 +fun IDETestContext.downloadAndroidPluginProject(): IDETestContext {
 +  val projectHome = resolvedProjectHome
 +  if (projectHome.toFile().name == "intellij-community-master" && !(projectHome / "android").toFile().exists()) {
 +    val scriptName = "getPlugins.sh"
 +
 +    val script = (projectHome / scriptName).toFile()
 +    assert(script.exists()) { "File $script does not exist" }
 +    val scriptContent = script.readText()
 +
 +    val stdout = ExecOutputRedirect.ToString()
 +    val stderr = ExecOutputRedirect.ToString()
-     )
++
++    ProcessExecutor(
 +      "git-clone-android-plugin",
 +      workDir = projectHome, timeout = 10.minutes,
 +      args = scriptContent.split(" "),
 +      stdoutRedirect = stdout,
 +      stderrRedirect = stderr
++    ).start()
++
 +    logOutput(stdout.read().trim())
 +  }
 +  return this
 +}
 +
index 058448905e0ff2d01ba274816dd3a8275fa5e995,0000000000000000000000000000000000000000..5a4eb0fb2451420fa3ff6aa40dcd9d030f841506
mode 100644,000000..100644
--- /dev/null
@@@ -1,103 -1,0 +1,102 @@@
- import kotlinx.coroutines.flow.collect
 +package com.intellij.ide.starter.bus
 +
 +import com.intellij.ide.starter.utils.catchAll
 +import kotlinx.coroutines.*
 +import kotlinx.coroutines.flow.drop
 +import kotlinx.coroutines.flow.filterNotNull
 +
 +/**
 + * @author https://github.com/Kosert/FlowBus
 + * @license Apache 2.0 https://github.com/Kosert/FlowBus/blob/master/LICENSE
 + * Class for receiving events posted to [FlowBus]
 + *
 + * @param bus [FlowBus] instance to subscribe to. If not set, [StarterBus] will be used
 + */
 +open class EventsReceiver @JvmOverloads constructor(
 +  private val bus: FlowBus = StarterBus
 +) {
 +
 +  private val jobs = mutableMapOf<Class<*>, Job>()
 +
 +  private var returnDispatcher: CoroutineDispatcher = Dispatchers.Default
 +
 +  /**
 +   * Set the `CoroutineDispatcher` which will be used to launch your callbacks.
 +   *
 +   * If this [EventsReceiver] was created on the main thread the default dispatcher will be [Dispatchers.Main].
 +   * In any other case [Dispatchers.Default] will be used.
 +   */
 +  fun returnOn(dispatcher: CoroutineDispatcher): EventsReceiver {
 +    returnDispatcher = dispatcher
 +    return this
 +  }
 +
 +  /**
 +   * Subscribe to events that are type of [clazz] with the given [callback] function.
 +   * The [callback] can be called immediately if event of type [clazz] is present in the flow.
 +   *
 +   * @param clazz Type of event to subscribe to
 +   * @param skipRetained Skips event already present in the flow. This is `false` by default
 +   * @param callback The callback function
 +   * @return This instance of [EventsReceiver] for chaining
 +   */
 +  @JvmOverloads
 +  fun <T : Any> subscribeTo(
 +    clazz: Class<T>,
 +    skipRetained: Boolean = false,
 +    callback: suspend (event: T) -> Unit
 +  ): EventsReceiver {
 +
 +    if (jobs.containsKey(clazz))
 +      throw IllegalArgumentException("Already subscribed for event type: $clazz")
 +
 +    val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
 +      throw throwable
 +    }
 +
 +    val job = CoroutineScope(Job() + Dispatchers.Default + exceptionHandler).launch {
 +      bus.forEvent(clazz)
 +        .drop(if (skipRetained) 1 else 0)
 +        .filterNotNull()
 +        .collect {
 +          catchAll {
 +            withContext(returnDispatcher) { callback(it) }
 +          }
 +        }
 +    }
 +
 +    jobs[clazz] = job
 +    return this
 +  }
 +
 +  /**
 +   * A variant of [subscribeTo] that uses an instance of [EventCallback] as callback.
 +   *
 +   * @param clazz Type of event to subscribe to
 +   * @param skipRetained Skips event already present in the flow. This is `false` by default
 +   * @param callback Interface with implemented callback function
 +   * @return This instance of [EventsReceiver] for chaining
 +   * @see [subscribeTo]
 +   */
 +  @JvmOverloads
 +  fun <T : Any> subscribeTo(
 +    clazz: Class<T>,
 +    callback: EventCallback<T>,
 +    skipRetained: Boolean = false
 +  ): EventsReceiver = subscribeTo(clazz, skipRetained) { callback.onEvent(it) }
 +
 +  /**
 +   * Unsubscribe from events type of [clazz]
 +   */
 +  fun <T : Any> unsubscribe(clazz: Class<T>) {
 +    jobs.remove(clazz)?.cancel()
 +  }
 +
 +  /**
 +   * Unsubscribe from all events
 +   */
 +  fun unsubscribe() {
 +    jobs.values.forEach { it.cancel() }
 +    jobs.clear()
 +  }
 +}
index 754491fd754e4763be75a526a4a5d32d86068ba7,0000000000000000000000000000000000000000..aa67d0e7594a17d5c5fb7f9f8b2ce8434b4cf800
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,41 @@@
- inline fun <reified T : Any> EventsReceiver.subscribe(skipRetained: Boolean = false, noinline callback: suspend (event: T) -> Unit): EventsReceiver {
-     return subscribeTo(T::class.java, skipRetained, callback)
 +package com.intellij.ide.starter.bus
 +
 +/**
 + * @author https://github.com/Kosert/FlowBus
 + * @license Apache 2.0 https://github.com/Kosert/FlowBus/blob/master/LICENSE
 + **/
 +
 +/**
 + * @see FlowBus.dropEvent
 + */
 +inline fun <reified T : Any> FlowBus.dropEvent() = dropEvent(T::class.java)
 +
 +/**
 + * @see FlowBus.getFlow
 + */
 +inline fun <reified T : Any> FlowBus.getFlow() = getFlow(T::class.java)
 +
 +/**
 + * Simplified [EventsReceiver.subscribeTo] for Kotlin.
 + * Type of event is automatically inferred from [callback] parameter type.
 + *
 + * @param skipRetained Skips event already present in the flow. This is `false` by default
 + * @param callback The callback function
 + * @return This instance of [EventsReceiver] for chaining
 + */
-     return subscribeTo(T::class.java, callback, skipRetained)
++inline fun <reified T : Any> EventsReceiver.subscribe(skipRetained: Boolean = false,
++                                                      noinline callback: suspend (event: T) -> Unit): EventsReceiver {
++  return subscribeTo(T::class.java, skipRetained, callback)
 +}
 +
 +/**
 + * A variant of [subscribe] that uses an instance of [EventCallback] as callback.
 + *
 + * @param skipRetained Skips event already present in the flow. This is `false` by default
 + * @param callback Interface with implemented callback function
 + * @return This instance of [EventsReceiver] for chaining
 + * @see [subscribe]
 + */
 +inline fun <reified T : Any> EventsReceiver.subscribe(callback: EventCallback<T>, skipRetained: Boolean = false): EventsReceiver {
++  return subscribeTo(T::class.java, callback, skipRetained)
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8b8d15dffa84bb1ed721c8ada625daf5d133cd24
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++Copyright 2021 Robert Kosakowski
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file except in compliance with the License.
++You may obtain a copy of the License at
++
++   http://www.apache.org/licenses/LICENSE-2.0
++
++Unless required by applicable law or agreed to in writing, software
++distributed under the License is distributed on an "AS IS" BASIS,
++WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++See the License for the specific language governing permissions and
++limitations under the License.
index 123d3728b566ab6b2538c033ad1df9ace35ff4dd,0000000000000000000000000000000000000000..abae931d7745a4c2b70ffe385baa42734867fd9c
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,29 @@@
-   val code: String,
-   val type: String = "release",
 +package com.intellij.ide.starter.community
 +
 +import org.apache.http.client.utils.URIBuilder
 +
 +data class ProductInfoRequestParameters(
-   val build: String = "",
++  val type: String,
++  val snapshot: String = "release",
 +  // e.g "2022.2"
 +  val majorVersion: String = "",
 +  // e.g  "221.5591.52",
-   val version: String = ""
++  val buildNumber: String = "",
 +  // e.g "2022.1.1"
-     if (code.isNotBlank()) builder.addParameter("code", code)
-     if (type.isNotBlank()) builder.addParameter("type", type)
++  val versionNumber: String = ""
 +) {
 +  fun toUriQuery(): URIBuilder {
 +    val builder = URIBuilder()
 +
 +    // API seems to filter only by code and type. It doesn't respond to majorVersion, build or version params
 +
++    if (type.isNotBlank()) builder.addParameter("code", type)
++    if (snapshot.isNotBlank()) builder.addParameter("type", snapshot)
 +
 +    return builder
 +  }
 +
 +  override fun toString(): String {
 +    return toUriQuery().toString()
 +  }
 +}
index 3aae8de8ae9c2222e76b716c79d260f2ceab12b4,0000000000000000000000000000000000000000..1b9392fe43b24b0c4eeb5df2e8a5b89ff58dd23d
mode 100644,000000..100644
--- /dev/null
@@@ -1,65 -1,0 +1,65 @@@
-     if (filteringParams.version.isBlank() && filteringParams.build.isBlank()) return sorted.first()
 +package com.intellij.ide.starter.community
 +
 +import com.intellij.ide.starter.community.model.ReleaseInfo
 +import com.intellij.ide.starter.ide.IdeDownloader
 +import com.intellij.ide.starter.ide.IdeInstaller
 +import com.intellij.ide.starter.models.IdeInfo
 +import com.intellij.ide.starter.system.OsType
 +import com.intellij.ide.starter.system.SystemInfo
 +import com.intellij.ide.starter.utils.HttpClient
 +import com.intellij.ide.starter.utils.logOutput
 +import java.nio.file.Path
 +import kotlin.io.path.exists
 +
 +object PublicIdeDownloader : IdeDownloader {
 +
 +  /** Filter release map: <ProductCode, List of releases> */
 +  private fun findSpecificRelease(releaseInfoMap: Map<String, List<ReleaseInfo>>,
 +                                  filteringParams: ProductInfoRequestParameters): ReleaseInfo {
 +    val sorted = releaseInfoMap.values.first().sortedByDescending { it.date }
 +
 +    if (filteringParams.majorVersion.isNotBlank()) return sorted.first { it.majorVersion == filteringParams.majorVersion }
 +
 +    // find latest release / eap, if no specific params were provided
-     if (filteringParams.version.isNotBlank()) return sorted.first { it.version == filteringParams.version }
-     if (filteringParams.build.isNotBlank()) return sorted.first { it.build == filteringParams.build }
++    if (filteringParams.versionNumber.isBlank() && filteringParams.buildNumber.isBlank()) return sorted.first()
 +
-     val params = ProductInfoRequestParameters(code = ideInfo.productCode,
-                                               type = ideInfo.buildType,
-                                               build = ideInfo.buildNumber,
-                                               version = ideInfo.version)
++    if (filteringParams.versionNumber.isNotBlank()) return sorted.first { it.version == filteringParams.versionNumber }
++    if (filteringParams.buildNumber.isNotBlank()) return sorted.first { it.build == filteringParams.buildNumber }
 +
 +    throw NoSuchElementException("Couldn't find specified release by parameters $filteringParams")
 +  }
 +
 +  override fun downloadIdeInstaller(ideInfo: IdeInfo, installerDirectory: Path): IdeInstaller {
++    val params = ProductInfoRequestParameters(type = ideInfo.productCode,
++                                              snapshot = ideInfo.buildType,
++                                              buildNumber = ideInfo.buildNumber,
++                                              versionNumber = ideInfo.version)
 +
 +    val releaseInfoMap = JetBrainsDataServiceClient.getReleases(params)
 +    if (releaseInfoMap.size != 1) throw RuntimeException("Only one product can be downloaded at once. Found ${releaseInfoMap.keys}")
 +
 +    val possibleBuild: ReleaseInfo = findSpecificRelease(releaseInfoMap, params)
 +
 +    val downloadLink: String = when (SystemInfo.getOsType()) {
 +      OsType.Linux -> possibleBuild.downloads.linux!!.link
 +      OsType.MacOS -> {
 +        if (SystemInfo.OS_ARCH == "aarch64") possibleBuild.downloads.macM1!!.link // macM1
 +        else possibleBuild.downloads.mac!!.link
 +      }
 +      OsType.Windows -> possibleBuild.downloads.windows!!.link
 +      else -> throw RuntimeException("Unsupported OS ${SystemInfo.getOsType()}")
 +    }
 +
 +    val installerFile = installerDirectory.resolve(
 +      "${ideInfo.installerFilePrefix}-" + possibleBuild.build.replace(".", "") + ideInfo.installerFileExt
 +    )
 +
 +    if (!installerFile.exists()) {
 +      logOutput("Downloading $ideInfo ...")
 +      HttpClient.download(downloadLink, installerFile)
 +    }
 +    else logOutput("Installer file $installerFile already exists. Skipping download.")
 +
 +    return IdeInstaller(installerFile, possibleBuild.build)
 +  }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fccb1d2baf938f3e093ae832d849895bbd6dd8b9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++package com.intellij.ide.starter.coroutine
++
++import kotlinx.coroutines.CoroutineScope
++import kotlinx.coroutines.Dispatchers
++import kotlinx.coroutines.Job
++import kotlinx.coroutines.SupervisorJob
++
++val supervisorScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
++val simpleScope = CoroutineScope(Job() + Dispatchers.IO)
index 44c8da68ea2c8efefbe87bfccafb94e5ada0657c,0000000000000000000000000000000000000000..85d9d4552228e288c85513eb543816fa5fb0c37c
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,57 @@@
-     if (ideInfo.productCode == "AI") {
 +package com.intellij.ide.starter.di
 +
 +import com.intellij.ide.starter.buildTool.BuildToolDefaultProvider
 +import com.intellij.ide.starter.buildTool.BuildToolProvider
 +import com.intellij.ide.starter.ci.CIServer
 +import com.intellij.ide.starter.ci.NoCIServer
 +import com.intellij.ide.starter.community.PublicIdeDownloader
 +import com.intellij.ide.starter.ide.*
 +import com.intellij.ide.starter.models.IdeInfo
 +import com.intellij.ide.starter.models.IdeProduct
 +import com.intellij.ide.starter.models.IdeProductImp
 +import com.intellij.ide.starter.path.GlobalPaths
 +import com.intellij.ide.starter.path.InstallerGlobalPaths
 +import com.intellij.ide.starter.plugins.PluginConfigurator
 +import com.intellij.ide.starter.report.publisher.ReportPublisher
 +import com.intellij.ide.starter.report.publisher.impl.ConsoleTestResultPublisher
 +import com.intellij.ide.starter.report.publisher.impl.QodanaTestResultPublisher
 +import com.intellij.ide.starter.runner.CodeBuilderHost
++import com.intellij.ide.starter.runner.CurrentTestMethod
 +import com.intellij.ide.starter.utils.logOutput
 +import org.kodein.di.DI
 +import org.kodein.di.bindFactory
 +import org.kodein.di.bindSingleton
 +
 +/**
 + * Reinitialize / override bindings for this DI container in your module before executing tests
 + * https://docs.kodein.org/kodein-di/7.9/core/bindings.html
 + *
 + * E.g:
 + * ```
 + * di = DI {
 + *      extend(di)
 + *      bindSingleton<GlobalPaths>(overrides = true) { YourImplementationOfPaths() }
 + *    }
 + * ```
 + * */
 +var di = DI {
 +  bindSingleton<GlobalPaths> { InstallerGlobalPaths() }
 +  bindSingleton<CIServer> { NoCIServer }
 +  bindSingleton<CodeInjector> { CodeBuilderHost() }
 +  bindFactory { testContext: IDETestContext -> PluginConfigurator(testContext) }
 +  bindSingleton<IdeDownloader> { PublicIdeDownloader }
 +  bindFactory<IdeInfo, IdeInstallator> { ideInfo ->
++    if (ideInfo.productCode == IdeProductProvider.AI.productCode) {
 +      AndroidInstaller()
 +    }
 +    else {
 +      SimpleInstaller()
 +    }
 +  }
 +  bindFactory<IDETestContext, BuildToolProvider> { testContext: IDETestContext -> BuildToolDefaultProvider(testContext) }
 +  bindSingleton<List<ReportPublisher>> { listOf(ConsoleTestResultPublisher, QodanaTestResultPublisher) }
 +  bindSingleton<IdeProduct> { IdeProductImp }
++  bindSingleton<CurrentTestMethod> { CurrentTestMethod }
 +}.apply {
 +  logOutput("DI was initialized")
 +}
index 72c373f3b5765ec477eef08da481df27a2a27dc3,0000000000000000000000000000000000000000..57459b6bc3717153f1a6caa096723f48a6310b2a
mode 100644,000000..100644
--- /dev/null
@@@ -1,546 -1,0 +1,558 @@@
- import com.intellij.ide.starter.models.*
 +package com.intellij.ide.starter.ide
 +
 +import com.intellij.ide.starter.buildTool.BuildToolProvider
 +import com.intellij.ide.starter.ci.CIServer
 +import com.intellij.ide.starter.di.di
 +import com.intellij.ide.starter.ide.command.CommandChain
 +import com.intellij.ide.starter.ide.command.MarshallableCommand
-         patchVMOptions = {
-           val warmupReports = IDEStartupReports(paths.reportsDir)
++import com.intellij.ide.starter.models.IDEStartResult
++import com.intellij.ide.starter.models.TestCase
++import com.intellij.ide.starter.models.VMOptions
++import com.intellij.ide.starter.models.andThen
 +import com.intellij.ide.starter.path.IDEDataPaths
 +import com.intellij.ide.starter.plugins.PluginConfigurator
 +import com.intellij.ide.starter.profiler.ProfilerType
 +import com.intellij.ide.starter.report.publisher.ReportPublisher
 +import com.intellij.ide.starter.runner.IDECommandLine
 +import com.intellij.ide.starter.runner.IDERunContext
 +import com.intellij.ide.starter.system.SystemInfo
 +import com.intellij.ide.starter.utils.logOutput
 +import org.kodein.di.direct
 +import org.kodein.di.factory
 +import org.kodein.di.instance
 +import org.kodein.di.newInstance
 +import org.w3c.dom.Node
 +import org.w3c.dom.NodeList
 +import java.io.FileOutputStream
++import java.nio.file.Files
 +import java.nio.file.Path
 +import javax.xml.parsers.DocumentBuilderFactory
 +import javax.xml.transform.TransformerFactory
 +import javax.xml.transform.dom.DOMSource
 +import javax.xml.transform.stream.StreamResult
 +import kotlin.io.path.*
 +import kotlin.time.Duration
 +import kotlin.time.Duration.Companion.minutes
 +
 +data class IDETestContext(
 +  val paths: IDEDataPaths,
 +  val ide: InstalledIde,
 +  val testCase: TestCase,
 +  val testName: String,
 +  private val _resolvedProjectHome: Path?,
 +  var patchVMOptions: VMOptions.() -> VMOptions,
 +  val ciServer: CIServer,
 +  var profilerType: ProfilerType = ProfilerType.NONE,
 +  val publishers: List<ReportPublisher> = di.direct.instance(),
 +  var isReportPublishingEnabled: Boolean = false
 +) {
 +  companion object {
 +    const val OPENTELEMETRY_FILE = "opentelemetry.json"
 +  }
 +
 +  val resolvedProjectHome: Path
 +    get() = checkNotNull(_resolvedProjectHome) { "Project is not found for the test $testName" }
 +
 +  val pluginConfigurator: PluginConfigurator by di.newInstance { factory<IDETestContext, PluginConfigurator>().invoke(this@IDETestContext) }
 +
 +  val buildTools: BuildToolProvider by di.newInstance { factory<IDETestContext, BuildToolProvider>().invoke(this@IDETestContext) }
 +
 +  fun addVMOptionsPatch(patchVMOptions: VMOptions.() -> VMOptions): IDETestContext {
 +    this.patchVMOptions = this.patchVMOptions.andThen(patchVMOptions)
 +    return this
 +  }
 +
 +  fun addLockFileForUITest(fileName: String): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("uiLockTempFile", paths.tempDir / fileName)
 +    }
 +
 +  fun disableJcef(): IDETestContext =
 +    addVMOptionsPatch {
 +      // Disable JCEF (IDEA-243147). Otherwise, tests will fail with LOG.error
 +      addSystemProperty("ide.browser.jcef.enabled", false)
 +    }
 +
 +  fun disableLinuxNativeMenuForce(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("linux.native.menu.force.disable", true)
 +    }
 +
 +  fun setMemorySize(sizeMb: Int): IDETestContext =
 +    addVMOptionsPatch {
 +      this
 +        .withXmx(sizeMb)
 +    }
 +
 +  fun disableGitLogIndexing(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("vcs.log.index.git", false)
 +    }
 +
 +  fun executeAfterProjectOpening(executeAfterProjectOpening: Boolean = true) = addVMOptionsPatch {
 +    addSystemProperty("performance.execute.script.after.project.opened", executeAfterProjectOpening)
 +  }
 +
 +  fun executeDuringIndexing(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("performance.execute.script.after.scanning", true)
 +    }
 +
 +  fun withGtk2OnLinux(): IDETestContext =
 +    addVMOptionsPatch {
 +      this
 +        .addSystemProperty("jdk.gtk.verbose", true)
 +        .let {
 +          // Desperate attempt to fix JBR-2783
 +          if (SystemInfo.isLinux) {
 +            it.addSystemProperty("jdk.gtk.version", 2)
 +          }
 +          else {
 +            it
 +          }
 +        }
 +    }
 +
 +  fun disableInstantIdeShutdown(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("ide.instant.shutdown", false)
 +    }
 +
 +  fun enableSlowOperationsInEdtInTests(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("ide.slow.operations.assertion", false)
 +    }
 +
 +  /**
 +   * Does not allow IDE to fork a process that sends FUS statistics on IDE shutdown.
 +   * On Windows that forked process may prevent some files from removing.
 +   * See [com.intellij.internal.statistic.EventLogApplicationLifecycleListener]
 +   */
 +  fun disableFusSendingOnIdeClose(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("feature.usage.event.log.send.on.ide.close", false)
 +    }
 +
 +  fun withVerboseIndexingDiagnostics(dumpPaths: Boolean = false): IDETestContext =
 +    addVMOptionsPatch {
 +      this
 +        .addSystemProperty("intellij.indexes.diagnostics.should.dump.for.interrupted.index.updaters", true)
 +        .addSystemProperty("intellij.indexes.diagnostics.limit.of.files", 10000)
 +        .addSystemProperty("intellij.indexes.diagnostics.should.dump.paths.of.indexed.files", dumpPaths)
 +        // Dumping of lists of indexed file paths may require a lot of memory.
 +        .withXmx(4 * 1024)
 +    }
 +
 +  fun setPathForMemorySnapshot(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("memory.snapshots.path", paths.logsDir)
 +    }
 +
 +  fun setPathForSnapshots(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("snapshots.path", paths.snapshotsDir)
 +    }
 +
 +  @Suppress("unused")
 +  fun collectMemorySnapshotOnFailedPluginUnload(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("ide.plugins.snapshot.on.unload.fail", true)
 +    }
 +
 +  fun setPerProjectDynamicPluginsFlag(): IDETestContext =
 +    addVMOptionsPatch {
 +      addSystemProperty("ide.plugins.per.project", true)
 +    }
 +
 +  fun disableAutoImport(disabled: Boolean = true) = addVMOptionsPatch {
 +    addSystemProperty("external.system.auto.import.disabled", disabled)
 +  }
 +
 +  fun disableOrdinaryIndexes() = addVMOptionsPatch {
 +    addSystemProperty("idea.use.only.index.infrastructure.extension", true)
 +  }
 +
 +  fun setSharedIndexesDownload(enable: Boolean = true) = addVMOptionsPatch {
 +    addSystemProperty("shared.indexes.bundled", enable)
 +      .addSystemProperty("shared.indexes.download", enable)
 +      .addSystemProperty("shared.indexes.download.auto.consent", enable)
 +  }
 +
 +  fun skipIndicesInitialization() = addVMOptionsPatch {
 +    addSystemProperty("idea.skip.indices.initialization", true)
 +  }
 +
 +  fun collectImportProjectPerfMetrics() = addVMOptionsPatch {
 +    addSystemProperty("idea.collect.project.import.performance", true)
 +  }
 +
 +  fun collectOpenTelemetry() = addVMOptionsPatch {
 +    addSystemProperty("idea.diagnostic.opentelemetry.file", paths.logsDir.resolve(OPENTELEMETRY_FILE))
 +  }
 +
 +  fun enableWorkspaceModelVerboseLogs() = addVMOptionsPatch {
 +    configureLoggers(traceLoggers = listOf("com.intellij.workspaceModel"))
 +  }
 +
 +  fun wipeSystemDir() = apply {
 +    //TODO: it would be better to allocate a new context instead of wiping the folder
 +    logOutput("Cleaning system dir for $this at $paths")
 +    paths.systemDir.toFile().deleteRecursively()
 +  }
 +
 +  fun wipeLogsDir() = apply {
 +    //TODO: it would be better to allocate a new context instead of wiping the folder
 +    logOutput("Cleaning logs dir for $this at $paths")
 +    paths.logsDir.toFile().deleteRecursively()
 +  }
 +
++  fun wipeReportDir() = apply {
++    logOutput("Cleaning report dir for $this at $paths")
++    Files.walk(paths.reportsDir)
++      .filter { Files.isRegularFile(it) }
++      .map { it.toFile() }
++      .forEach { it.delete() }
++  }
++
 +  fun wipeProjectsDir() = apply {
 +    val path = paths.systemDir / "projects"
 +    logOutput("Cleaning project cache dir for $this at $path")
 +    path.toFile().deleteRecursively()
 +  }
 +
 +  fun wipeEventLogDataDir() = apply {
 +    val path = paths.systemDir / "event-log-data"
 +    logOutput("Cleaning event-log-data dir for $this at $path")
 +    path.toFile().deleteRecursively()
 +  }
 +
 +  fun wipeSnapshotDir() = apply {
 +    val path = paths.snapshotsDir
 +    logOutput("Cleaning snapshot dir for $this at $path")
 +    path.toFile().deleteRecursively()
 +  }
 +
 +  fun runContext(
 +    patchVMOptions: VMOptions.() -> VMOptions = { this },
 +    commandLine: IDECommandLine? = null,
 +    commands: Iterable<MarshallableCommand> = CommandChain(),
 +    codeBuilder: (CodeInjector.() -> Unit)? = null,
 +    runTimeout: Duration = 10.minutes,
 +    useStartupScript: Boolean = true,
 +    launchName: String = "",
 +    expectedKill: Boolean = false,
 +    collectNativeThreads: Boolean = false
 +  ): IDERunContext {
 +    return IDERunContext(testContext = this)
 +      .copy(
 +        commandLine = commandLine,
 +        commands = commands,
 +        codeBuilder = codeBuilder,
 +        runTimeout = runTimeout,
 +        useStartupScript = useStartupScript,
 +        launchName = launchName,
 +        expectedKill = expectedKill,
 +        collectNativeThreads = collectNativeThreads
 +      )
 +      .addVMOptionsPatch(patchVMOptions)
 +  }
 +
 +  /**
 +   * Setup profiler injection
 +   */
 +  fun setProfiler(profilerType: ProfilerType): IDETestContext {
 +    this.profilerType = profilerType
 +    return this
 +  }
 +
 +  fun internalMode() = addVMOptionsPatch { addSystemProperty("idea.is.internal", true) }
 +
 +  fun prepareProjectCleanImport(): IDETestContext {
 +    return removeIdeaProjectDirectory().removeAllImlFilesInProject()
 +  }
 +
 +  fun disableAutoSetupJavaProject() = addVMOptionsPatch {
 +    addSystemProperty("idea.java.project.setup.disabled", true)
 +  }
 +
 +  fun disablePackageSearchBuildFiles() = addVMOptionsPatch {
 +    addSystemProperty("idea.pkgs.disableLoading", true)
 +  }
 +
 +  fun removeIdeaProjectDirectory(): IDETestContext {
 +    val ideaDirPath = resolvedProjectHome.resolve(".idea")
 +
 +    logOutput("Removing $ideaDirPath ...")
 +
 +    if (ideaDirPath.notExists()) {
 +      logOutput("Idea project directory $ideaDirPath doesn't exist. So, it will not be deleted")
 +      return this
 +    }
 +
 +    ideaDirPath.toFile().deleteRecursively()
 +    return this
 +  }
 +
 +  fun removeAllImlFilesInProject(): IDETestContext {
 +    val projectDir = resolvedProjectHome
 +
 +    logOutput("Removing all .iml files in $projectDir ...")
 +
 +    projectDir.toFile().walkTopDown()
 +      .forEach {
 +        if (it.isFile && it.extension == "iml") {
 +          it.delete()
 +          logOutput("File ${it.path} is deleted")
 +        }
 +      }
 +
 +    return this
 +  }
 +
 +  fun runIDE(
 +    patchVMOptions: VMOptions.() -> VMOptions = { this },
 +    commandLine: IDECommandLine? = null,
 +    commands: Iterable<MarshallableCommand>,
 +    codeBuilder: (CodeInjector.() -> Unit)? = null,
 +    runTimeout: Duration = 10.minutes,
 +    useStartupScript: Boolean = true,
 +    launchName: String = "",
 +    expectedKill: Boolean = false,
 +    collectNativeThreads: Boolean = false
 +  ): IDEStartResult {
 +
 +    val ideRunResult = runContext(
 +      commandLine = commandLine,
 +      commands = commands,
 +      codeBuilder = codeBuilder,
 +      runTimeout = runTimeout,
 +      useStartupScript = useStartupScript,
 +      launchName = launchName,
 +      expectedKill = expectedKill,
 +      collectNativeThreads = collectNativeThreads,
 +      patchVMOptions = patchVMOptions
 +    ).runIDE()
 +
 +    if (isReportPublishingEnabled) publishers.forEach {
 +      it.publish(ideRunResult)
 +    }
 +    if (ideRunResult.failureError != null) throw ideRunResult.failureError
 +    return ideRunResult
 +  }
 +
 +  fun warmUp(
 +    patchVMOptions: VMOptions.() -> VMOptions = { this },
 +    commands: Iterable<MarshallableCommand>,
 +    runTimeout: Duration = 10.minutes,
 +    storeClassReport: Boolean = false
 +  ): IDEStartResult {
 +    val updatedContext = this.copy(testName = "${this.testName}/warmup")
 +    val result = updatedContext.runIDE(
-             this.enableStartupPerformanceLog(warmupReports).enableClassLoadingReport(
-               paths.reportsDir / "class-report.txt").patchVMOptions()
++      patchVMOptions = {
++        this.run {
 +          if (storeClassReport) {
-         },
-         commands = testCase.commands.plus(commands),
-         runTimeout = runTimeout
-       )
++            this.enableClassLoadingReport(paths.reportsDir / "class-report.txt")
 +          }
 +          else {
 +            this
 +          }
++        }.patchVMOptions()
++      },
++      commands = testCase.commands.plus(commands),
++      runTimeout = runTimeout
++    )
 +    updatedContext.publishArtifact(this.paths.reportsDir)
 +    return result
 +  }
 +
 +  fun removeAndUnpackProject(): IDETestContext {
 +    testCase.markNotReusable().projectInfo?.downloadAndUnpackProject()
 +    return this
 +  }
 +
 +  fun setProviderMemoryOnlyOnLinux(): IDETestContext {
 +    if (SystemInfo.isLinux) {
 +      val optionsConfig = paths.configDir.resolve("options")
 +      optionsConfig.toFile().mkdirs()
 +      val securityXml = optionsConfig.resolve("security.xml")
 +      securityXml.toFile().createNewFile()
 +      securityXml.toFile().writeText("""<application>
 +  <component name="PasswordSafe">
 +    <option name="PROVIDER" value="MEMORY_ONLY" />
 +  </component>
 +</application>""")
 +    }
 +    return this
 +  }
 +
 +  fun addBuildProcessProfiling(): IDETestContext {
 +    if (_resolvedProjectHome != null) {
 +      val ideaDir = resolvedProjectHome.resolve(".idea")
 +      val workspace = ideaDir.resolve("workspace.xml")
 +
 +      if (workspace.toFile().exists()) {
 +        val newContent = StringBuilder()
 +        val readText = workspace.toFile().readText()
 +        val userLocalBuildProcessVmOptions = when {
 +          (testName.contains(
 +            "intellij_sources")) -> "-Dprofiling.mode=true -Dgroovyc.in.process=true -Dgroovyc.asm.resolving.only=false"
 +          else -> "-Dprofiling.mode=true"
 +        }
 +        if (readText.contains("CompilerWorkspaceConfiguration")) {
 +          workspace.toFile().readLines().forEach {
 +            if (it.contains("<component name=\"CompilerWorkspaceConfiguration\">")) {
 +              val newLine = "<component name=\"CompilerWorkspaceConfiguration\">\n<option name=\"COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS\" value=\"$userLocalBuildProcessVmOptions\" />"
 +              newContent.appendLine(newLine)
 +            }
 +            else {
 +              newContent.appendLine(it)
 +            }
 +          }
 +          workspace.writeText(newContent.toString())
 +        }
 +        else {
 +          val xmlDoc = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().parse(workspace.toFile())
 +
 +          xmlDoc.documentElement.normalize()
 +
 +          val firstElement = xmlDoc.firstChild
 +          val componentElement = xmlDoc.createElement("component")
 +          componentElement.setAttribute("name", "CompilerWorkspaceConfiguration")
 +          val optionElement = xmlDoc.createElement("option")
 +          optionElement.setAttribute("name", "COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS")
 +          optionElement.setAttribute("value", userLocalBuildProcessVmOptions)
 +          firstElement.appendChild(componentElement).appendChild(optionElement)
 +          val source = DOMSource(xmlDoc)
 +          val outputStream = FileOutputStream(workspace.toFile())
 +          val result = StreamResult(outputStream)
 +          val transformerFactory = TransformerFactory.newInstance()
 +          val transformer = transformerFactory.newTransformer()
 +          transformer.transform(source, result)
 +        }
 +      }
 +    }
 +    return this
 +  }
 +
 +  fun checkThatBuildRunByIdea(): IDETestContext {
 +    if (_resolvedProjectHome != null) {
 +      val ideaDir = resolvedProjectHome.resolve(".idea")
 +      val gradle = ideaDir.resolve("gradle.xml")
 +      if (gradle.toFile().exists()) {
 +        val readText = gradle.toFile().readText()
 +        if (!readText.contains("<option name=\"delegatedBuild\" value=\"false\"/>")) {
 +          val xmlDoc = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().parse(gradle.toFile())
 +
 +          xmlDoc.documentElement.normalize()
 +
 +          val gradleProjectSettingsElements: NodeList = xmlDoc.getElementsByTagName("GradleProjectSettings")
 +          if (gradleProjectSettingsElements.length == 1) {
 +
 +            for (i in 0 until gradleProjectSettingsElements.length) {
 +              val component: Node = gradleProjectSettingsElements.item(i)
 +
 +              if (component.nodeType == Node.ELEMENT_NODE) {
 +                val optionElement = xmlDoc.createElement("option")
 +                optionElement.setAttribute("name", "delegatedBuild")
 +                optionElement.setAttribute("value", "false")
 +                component.appendChild(optionElement)
 +              }
 +            }
 +            val source = DOMSource(xmlDoc)
 +            val outputStream = FileOutputStream(gradle.toFile())
 +            val result = StreamResult(outputStream)
 +            val transformerFactory = TransformerFactory.newInstance()
 +            val transformer = transformerFactory.newTransformer()
 +            transformer.transform(source, result)
 +          }
 +        }
 +      }
 +    }
 +    return this
 +  }
 +
 +  fun setBuildProcessHeapSize(heapSizeValue: String): IDETestContext {
 +    if (_resolvedProjectHome != null) {
 +      val heapSize = when (heapSizeValue.isEmpty()) {
 +        true -> "2000"
 +        else -> heapSizeValue
 +      }
 +      val ideaDir = resolvedProjectHome.resolve(".idea")
 +      val compilerXml = ideaDir.resolve("compiler.xml")
 +      if (compilerXml.toFile().exists()) {
 +        val newContent = StringBuilder()
 +        val readText = compilerXml.toFile().readText()
 +        if (!readText.contains("BUILD_PROCESS_HEAP_SIZE")) {
 +          compilerXml.toFile().readLines().forEach {
 +            if (it.contains("<component name=\"CompilerConfiguration\">")) {
 +              val newLine = "<component name=\"CompilerConfiguration\">\n<option name=\"BUILD_PROCESS_HEAP_SIZE\" value=\"$heapSize\" />"
 +              newContent.appendLine(newLine)
 +            }
 +            else {
 +              newContent.appendLine(it)
 +            }
 +          }
 +          compilerXml.writeText(newContent.toString())
 +        }
 +        else if (heapSizeValue.isNotEmpty()) {
 +          compilerXml.toFile().readLines().forEach {
 +            if (it.contains("BUILD_PROCESS_HEAP_SIZE")) {
 +              val newLine = it.replace("value=\"\\d*\"".toRegex(), "value=\"$heapSize\"")
 +              newContent.appendLine(newLine)
 +            }
 +            else {
 +              newContent.appendLine(it)
 +            }
 +          }
 +          compilerXml.writeText(newContent.toString())
 +        }
 +      }
 +    }
 +    return this
 +  }
 +
 +  fun updateGeneralSettings(): IDETestContext {
 +    val patchedIdeGeneralXml = this::class.java.classLoader.getResourceAsStream("ide.general.xml")
 +    val pathToGeneralXml = paths.configDir.toAbsolutePath().resolve("options/ide.general.xml")
 +
 +    if (!pathToGeneralXml.exists()) {
 +      pathToGeneralXml.parent.createDirectories()
 +      patchedIdeGeneralXml.use {
 +        if (it != null) {
 +          pathToGeneralXml.writeBytes(it.readAllBytes())
 +        }
 +      }
 +    }
 +    return this
 +  }
 +
 +  @Suppress("unused")
 +  fun setLicense(pathToFileWithLicense: Path): IDETestContext {
 +    val licenseKeyFileName: String = when (this.ide.productCode) {
 +      IdeProductProvider.IU.productCode -> "idea.key"
 +      IdeProductProvider.RM.productCode -> "rubymine.key"
 +      IdeProductProvider.WS.productCode -> "webstorm.key"
 +      IdeProductProvider.PS.productCode -> "phpstorm.key"
 +      IdeProductProvider.GO.productCode -> "goland.key"
 +      IdeProductProvider.PY.productCode -> "pycharm.key"
 +      IdeProductProvider.DB.productCode -> "datagrip.key"
 +      else -> error("Setting license to the product ${this.ide.productCode} is not supported")
 +    }
 +
 +    val keyFile = paths.configDir.resolve(licenseKeyFileName)
 +    keyFile.toFile().createNewFile()
 +    keyFile.toFile().writeText(pathToFileWithLicense.toFile().readText())
 +    return this
 +  }
 +
 +  fun publishArtifact(source: Path,
 +                      artifactPath: String = testName,
 +                      artifactName: String = source.fileName.toString()) = ciServer.publishArtifact(source, artifactPath, artifactName)
 +
 +  @Suppress("unused")
 +  fun withReportPublishing(isEnabled: Boolean): IDETestContext {
 +    isReportPublishingEnabled = isEnabled
 +    return this
 +  }
 +}
 +
index 82347136c79baf11ca3cf709372ebc24694b3c00,0000000000000000000000000000000000000000..5d87b5ce4ade91c49026f81f900eb14e35a3ae47
mode 100644,000000..100644
--- /dev/null
@@@ -1,107 -1,0 +1,110 @@@
- import com.intellij.ide.starter.exec.ExecOutputRedirect
- import com.intellij.ide.starter.exec.exec
 +package com.intellij.ide.starter.ide
 +
 +import com.intellij.ide.starter.di.di
-       exec(presentablePurpose = "hdiutil",
-            workDir = target,
-            timeout = 10.minutes,
-            stderrRedirect = ExecOutputRedirect.ToStdOut("hdiutil"),
-            stdoutRedirect = ExecOutputRedirect.ToStdOut("hdiutil"),
-            args = listOf("hdiutil", "attach", "-readonly", "-noautoopen", "-noautofsck", "-nobrowse", "-mountpoint", "$mountDir",
-                          "$dmgFile"))
 +import com.intellij.ide.starter.path.GlobalPaths
++import com.intellij.ide.starter.process.exec.ExecOutputRedirect
++import com.intellij.ide.starter.process.exec.ProcessExecutor
 +import com.intellij.ide.starter.utils.FileSystem
 +import com.intellij.ide.starter.utils.HttpClient
 +import com.intellij.ide.starter.utils.catchAll
 +import com.intellij.ide.starter.utils.logOutput
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import java.io.File
 +import java.nio.file.Path
 +import kotlin.io.path.createDirectories
 +import kotlin.io.path.div
 +import kotlin.io.path.nameWithoutExtension
 +import kotlin.time.Duration.Companion.minutes
 +
 +object IdeArchiveExtractor {
 +
 +  fun unpackIdeIfNeeded(ideInstallerFile: File, unpackDir: File) {
 +    if (unpackDir.isDirectory && unpackDir.listFiles()?.isNotEmpty() == true) {
 +      logOutput("Build directory $unpackDir already exists for the binary $ideInstallerFile")
 +      return
 +    }
 +
 +    logOutput("Extracting application into $unpackDir")
 +    when {
 +      ideInstallerFile.extension == "dmg" -> unpackDmg(ideInstallerFile, unpackDir.toPath())
 +      ideInstallerFile.extension == "exe" -> unpackWin(ideInstallerFile, unpackDir)
 +      ideInstallerFile.extension == "zip" -> FileSystem.unpack(ideInstallerFile.toPath(), unpackDir.toPath())
 +      ideInstallerFile.name.endsWith(".tar.gz") -> FileSystem.unpackTarGz(ideInstallerFile, unpackDir)
 +      else -> error("Unsupported build file: $ideInstallerFile")
 +    }
 +  }
 +
 +  private fun unpackDmg(dmgFile: File, target: Path): Path {
 +    target.toFile().deleteRecursively()
 +    target.createDirectories()
 +
 +    val mountDir = File(dmgFile.path + "-mount${System.currentTimeMillis()}")
 +    try {
-       exec(
-         presentablePurpose = "copy-dmg",
++      ProcessExecutor(presentableName = "hdiutil",
++                      workDir = target,
++                      timeout = 10.minutes,
++                      stderrRedirect = ExecOutputRedirect.ToStdOut("hdiutil"),
++                      stdoutRedirect = ExecOutputRedirect.ToStdOut("hdiutil"),
++                      args = listOf("hdiutil", "attach", "-readonly", "-noautoopen", "-noautofsck", "-nobrowse", "-mountpoint", "$mountDir",
++                                    "$dmgFile")
++      ).start()
 +    }
 +    catch (t: Throwable) {
 +      dmgFile.delete()
 +      throw Error("Failed to mount $dmgFile. ${t.message}.", t)
 +    }
 +
 +    try {
 +      val appDir = mountDir.listFiles()?.singleOrNull { it.name.endsWith(".app") }
 +                   ?: error("Failed to find the only one .app folder in $dmgFile")
 +
 +      val targetAppDir = target / appDir.name
-         args = listOf("cp", "-R", "$appDir", "$targetAppDir"))
++      ProcessExecutor(
++        presentableName = "copy-dmg",
 +        workDir = target,
 +        timeout = 10.minutes,
 +        stderrRedirect = ExecOutputRedirect.ToStdOut("cp"),
-         exec(
-           presentablePurpose = "hdiutil",
++        args = listOf("cp", "-R", "$appDir", "$targetAppDir")
++      ).start()
 +
 +      return targetAppDir
 +    }
 +    finally {
 +      catchAll {
-           args = listOf("hdiutil", "detach", "-force", "$mountDir"))
++        ProcessExecutor(
++          presentableName = "hdiutil",
 +          workDir = target,
 +          timeout = 10.minutes,
 +          stdoutRedirect = ExecOutputRedirect.ToStdOut("hdiutil"),
 +          stderrRedirect = ExecOutputRedirect.ToStdOut("hdiutil"),
-     exec(
-       presentablePurpose = "unpack-zip",
++          args = listOf("hdiutil", "detach", "-force", "$mountDir")
++        ).start()
 +      }
 +    }
 +  }
 +
 +  private fun unpackWin(exeFile: File, targetDir: File) {
 +    targetDir.deleteRecursively()
 +
 +    //we use 7-Zip to unpack NSIS binaries, same way as in Toolbox App
 +    val sevenZipUrl = "https://repo.labs.intellij.net/thirdparty/7z-cmdline-15.06.zip"
 +    val sevenZipCacheDir = di.direct.instance<GlobalPaths>().getCacheDirectoryFor("7zip")
 +
 +    val sevenZipFile = sevenZipCacheDir / sevenZipUrl.split("/").last()
 +    val sevenZipTool = sevenZipCacheDir / sevenZipFile.fileName.nameWithoutExtension
 +
 +    HttpClient.downloadIfMissing(sevenZipUrl, sevenZipFile)
 +    FileSystem.unpackIfMissing(sevenZipFile, sevenZipTool)
 +
 +    val severZipToolExe = sevenZipTool.resolve("7z.exe")
 +
 +    targetDir.mkdirs()
-     )
++    ProcessExecutor(
++      presentableName = "unpack-zip",
 +      workDir = targetDir.toPath(),
 +      timeout = 10.minutes,
 +      args = listOf(severZipToolExe.toAbsolutePath().toString(), "x", "-y", "-o$targetDir", exeFile.path)
++    ).start()
 +  }
 +}
index 156d0b4e8338da1bb7f0cd7d494760300e2dce8b,0000000000000000000000000000000000000000..5609f758c7f8851f16d9f00243a38dc0931c8f40
mode 100644,000000..100644
--- /dev/null
@@@ -1,7 -1,0 +1,10 @@@
 +package com.intellij.ide.starter.ide
 +
 +import com.intellij.ide.starter.models.IdeInfo
 +
 +interface IdeInstallator {
++  /**
++   * @return <Build Number, InstalledIde>
++   */
 +  fun install(ideInfo: IdeInfo): Pair<String, InstalledIde>
 +}
index 2ef5b631d6a35e0ca54474cdfc6d51daf36d1d8a,0000000000000000000000000000000000000000..126d3378dadff7ba6263ef2cdd773558bc698df9
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,42 @@@
 +package com.intellij.ide.starter.ide
 +
 +import com.intellij.ide.starter.di.di
 +import com.intellij.ide.starter.models.IdeInfo
 +import com.intellij.ide.starter.models.IdeProduct
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import kotlin.reflect.full.declaredMemberProperties
 +
 +object IdeProductProvider {
 +  /** GoLand */
 +  val GO: IdeInfo = di.direct.instance<IdeProduct>().GO
 +
 +  /** IntelliJ Ultimate */
 +  val IU: IdeInfo = di.direct.instance<IdeProduct>().IU
 +
 +  /** IntelliJ Community */
 +  val IC: IdeInfo = di.direct.instance<IdeProduct>().IC
 +
 +  /** Android Studio */
 +  val AI: IdeInfo = di.direct.instance<IdeProduct>().AI
 +
 +  /** WebStorm */
 +  val WS: IdeInfo = di.direct.instance<IdeProduct>().WS
 +
 +  /** PhpStorm */
 +  val PS: IdeInfo = di.direct.instance<IdeProduct>().PS
 +
 +  /** DataGrip */
 +  val DB: IdeInfo = di.direct.instance<IdeProduct>().DB
 +
 +  /** RubyMine */
 +  val RM: IdeInfo = di.direct.instance<IdeProduct>().RM
 +
 +  /** PyCharm Professional */
 +  val PY: IdeInfo = di.direct.instance<IdeProduct>().PY
 +
++  /** CLion */
++  val CL: IdeInfo = di.direct.instance<IdeProduct>().CL
++
 +  fun getProducts(): List<IdeInfo> = IdeProductProvider::class.declaredMemberProperties.map { it.get(IdeProductProvider) as IdeInfo }
 +}
index 917436c24571a2e8b365eea6ba173035022468e0,0000000000000000000000000000000000000000..a15206e14265b639d0a1073bf39434b51cf30e8d
mode 100644,000000..100644
--- /dev/null
@@@ -1,129 -1,0 +1,130 @@@
- import com.intellij.ide.starter.exec.ExecOutputRedirect
- import com.intellij.ide.starter.exec.exec
 +package com.intellij.ide.starter.ide
 +
-       exec("xvfb-run", homePath, timeout = 5.seconds, args = listOf("which", toolName),
-            stdoutRedirect = ExecOutputRedirect.ToStdOut("xvfb-run-out"),
-            stderrRedirect = ExecOutputRedirect.ToStdOut("xvfb-run-err"))
 +import com.intellij.ide.starter.models.VMOptions
++import com.intellij.ide.starter.process.exec.ExecOutputRedirect
++import com.intellij.ide.starter.process.exec.ProcessExecutor
 +import com.intellij.ide.starter.system.SystemInfo
 +import com.intellij.ide.starter.utils.callJavaVersion
 +import com.intellij.ide.starter.utils.logOutput
 +import java.nio.file.Files
 +import java.nio.file.Path
 +import kotlin.io.path.*
 +import kotlin.time.Duration.Companion.seconds
 +
 +class LinuxIdeDistribution : IdeDistribution() {
 +  companion object {
 +
 +    private val xvfbRunTool by lazy {
 +      val toolName = "xvfb-run"
 +
 +      val homePath = Path(System.getProperty("user.home")).toAbsolutePath()
++      ProcessExecutor("xvfb-run", homePath, timeout = 5.seconds, args = listOf("which", toolName),
++                      stdoutRedirect = ExecOutputRedirect.ToStdOut("xvfb-run-out"),
++                      stderrRedirect = ExecOutputRedirect.ToStdOut("xvfb-run-err")
++      ).start()
 +      toolName
 +    }
 +
 +    fun linuxCommandLine(xvfbRunLog: Path): List<String> {
 +      return when {
 +        System.getenv("DISPLAY") != null -> listOf()
 +        else ->
 +          //hint https://gist.github.com/tullmann/2d8d38444c5e81a41b6d
 +          listOf(
 +            xvfbRunTool,
 +            "--error-file=" + xvfbRunLog.toAbsolutePath().toString(),
 +            "--server-args=-ac -screen 0 1920x1080x24",
 +            "--auto-servernum",
 +            "--server-num=88"
 +          )
 +      }
 +    }
 +
 +    fun createXvfbRunLog(logsDir: Path): Path {
 +      val logTxt = logsDir.resolve("xvfb-log.txt")
 +      logTxt.deleteIfExists()
 +
 +      return Files.createFile(logTxt)
 +    }
 +  }
 +
 +  override fun installIde(unpackDir: Path, executableFileName: String): InstalledIde {
 +    require(SystemInfo.isLinux) { "Can only run on Linux, docker is possible, please PR" }
 +
 +    val appHome = (unpackDir.toFile().listFiles()?.singleOrNull { it.isDirectory }?.toPath() ?: unpackDir)
 +
 +    val buildTxtPath = appHome.resolve("build.txt")
 +    require(buildTxtPath.isRegularFile()) { "Cannot find LinuxOS IDE vmoptions file in $unpackDir" }
 +    val (productCode, build) = buildTxtPath.readText().trim().split("-", limit = 2)
 +
 +    val binDir = appHome / "bin"
 +    val allBinFiles = binDir.listDirectoryEntries()
 +    val executablePath = allBinFiles.singleOrNull { file ->
 +      file.fileName.toString() == "$executableFileName.sh"
 +    } ?: error("Failed to detect IDE executable .sh in:\n${allBinFiles.joinToString("\n")}")
 +
 +    return object : InstalledIde {
 +      override val bundledPluginsDir = appHome.resolve("plugins")
 +
 +      val originalVMOptionsFile = executablePath.parent.resolve(
 +        executablePath.fileName.toString().removeSuffix(".sh") + "64.vmoptions") //TODO: which file to pick with 64 or without?
 +      override val originalVMOptions = VMOptions.readIdeVMOptions(this, originalVMOptionsFile)
 +      override val patchedVMOptionsFile = appHome.parent.resolve("${appHome.fileName}.vmoptions")
 +
 +      override fun startConfig(vmOptions: VMOptions, logsDir: Path) =
 +        object : InstalledBackedIDEStartConfig(patchedVMOptionsFile, vmOptions) {
 +
 +          override val environmentVariables: Map<String, String>
 +            get() = super.environmentVariables.filterKeys {
 +              when {
 +                it.startsWith("DESKTOP") -> false
 +                it.startsWith("DBUS") -> false
 +                it.startsWith("APPIMAGE") -> false
 +                it.startsWith("DEFAULTS_PATH") -> false
 +                it.startsWith("GDM") -> false
 +                it.startsWith("GNOME") -> false
 +                it.startsWith("GTK") -> false
 +                it.startsWith("MANDATORY_PATH") -> false
 +                it.startsWith("QT") -> false
 +                it.startsWith("SESSION") -> false
 +                it.startsWith("TOOLBOX_VERSION") -> false
 +                it.startsWith("XAUTHORITY") -> false
 +                it.startsWith("XDG") -> false
 +                it.startsWith("XMODIFIERS") -> false
 +                it.startsWith("GPG_") -> false
 +                it.startsWith("CLUTTER_IM_MODULE") -> false
 +                it.startsWith("APPDIR") -> false
 +                it.startsWith("LC") -> false
 +                it.startsWith("SSH") -> false
 +                else -> true
 +              }
 +            } + ("LC_ALL" to "en_US.UTF-8")
 +
 +          val xvfbRunLog = createXvfbRunLog(logsDir)
 +
 +          override val errorDiagnosticFiles = listOf(xvfbRunLog)
 +          override val workDir = appHome
 +          override val commandLine: List<String> = linuxCommandLine(xvfbRunLog) + executablePath.toAbsolutePath().toString()
 +        }
 +
 +      override val build = build
 +      override val os = "linux"
 +      override val productCode = productCode
 +      override val isFromSources = false
 +
 +      override fun toString() = "IDE{$productCode, $build, $os, home=$unpackDir}"
 +      override fun resolveAndDownloadTheSameJDK(): Path {
 +        val jbrHome = appHome.resolve("jbr")
 +        require(jbrHome.isDirectory()) {
 +          "JbrHome is not found under $jbrHome"
 +        }
 +
 +        val jbrFullVersion = callJavaVersion(jbrHome).substringAfter("build ").substringBefore(")")
 +        logOutput("Found following $jbrFullVersion in the product: $productCode $build")
 +        // in Android Studio bundled only JRE
 +        if (productCode == IdeProductProvider.AI.productCode) return jbrHome
 +        return downloadAndUnpackJbrIfNeeded(jbrFullVersion)
 +      }
 +    }
 +  }
 +}
index 30b4956e24748addced4ec6e9dcd738514935d43,0000000000000000000000000000000000000000..80d3f6fe785ff8c39c93cc0d750507f9521b7130
mode 100644,000000..100644
--- /dev/null
@@@ -1,30 -1,0 +1,33 @@@
 +package com.intellij.ide.starter.models
 +
 +interface IdeProduct {
 +  /** GoLand */
 +  val GO: IdeInfo
 +
 +  /** IntelliJ Ultimate */
 +  val IU: IdeInfo
 +
 +  /** IntelliJ Community */
 +  val IC: IdeInfo
 +
 +  /** Android Studio */
 +  val AI: IdeInfo
 +
 +  /** WebStorm */
 +  val WS: IdeInfo
 +
 +  /** PhpStorm */
 +  val PS: IdeInfo
 +
 +  /** DataGrip */
 +  val DB: IdeInfo
 +
 +  /** RubyMine */
 +  val RM: IdeInfo
 +
 +  /** PyCharm Professional */
 +  val PY: IdeInfo
++
++  /** CLion */
++  val CL: IdeInfo
 +}
index 5e06e880d5b005a903b3a04f419c145b16528b72,0000000000000000000000000000000000000000..57ec1ef3d602becf4b6a2f51c6f205486e6a0f18
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,73 @@@
 +package com.intellij.ide.starter.models
 +
 +object IdeProductImp : IdeProduct {
 +  /** GoLand */
 +  override val GO = IdeInfo(
 +    productCode = "GO",
 +    platformPrefix = "GoLand",
 +    executableFileName = "goland",
 +  )
 +
 +  /** IntelliJ Ultimate */
 +  override val IU = IdeInfo(
 +    productCode = "IU",
 +    platformPrefix = "idea",
 +    executableFileName = "idea"
 +  )
 +
 +  /** IntelliJ Community */
 +  override val IC = IdeInfo(
 +    productCode = "IC",
 +    platformPrefix = "Idea",
 +    executableFileName = "idea"
 +  )
 +
 +  /** Android Studio */
 +  override val AI = IdeInfo(
 +    productCode = "AI",
 +    platformPrefix = "AndroidStudio",
 +    executableFileName = "studio"
 +  )
 +
 +  /** WebStorm */
 +  override val WS = IdeInfo(
 +    productCode = "WS",
 +    platformPrefix = "WebStorm",
 +    executableFileName = "webstorm"
 +  )
 +
 +  /** PhpStorm */
 +  override val PS = IdeInfo(
 +    productCode = "PS",
 +    platformPrefix = "PhpStorm",
 +    executableFileName = "phpstorm"
 +  )
 +
 +  /** DataGrip */
 +  override val DB = IdeInfo(
 +    productCode = "DB",
 +    platformPrefix = "DataGrip",
 +    executableFileName = "datagrip"
 +  )
 +
 +  /** RubyMine */
 +  override val RM = IdeInfo(
 +    productCode = "RM",
 +    platformPrefix = "Ruby",
 +    executableFileName = "rubymine"
 +  )
 +
 +  /** PyCharm Professional */
 +  override val PY = IdeInfo(
 +    productCode = "PY",
 +    platformPrefix = "Python",
 +    executableFileName = "pycharm"
 +  )
++
++  /** CLion */
++  override val CL: IdeInfo = IdeInfo(
++    productCode = "CL",
++    platformPrefix = "CLion",
++    executableFileName = "clion"
++  )
 +}
index b1a8a7bac68b512d11f0b87257b9e27bbe649c56,0000000000000000000000000000000000000000..05d9c6b0128860c621f8fe60681c441498dc17c6
mode 100644,000000..100644
--- /dev/null
@@@ -1,46 -1,0 +1,43 @@@
-   /** Project in this case will be reused between tests */
-   fun markNotReusable(): TestCase = markReusable(false)
 +package com.intellij.ide.starter.models
 +
 +import com.intellij.ide.starter.community.model.BuildType
 +import com.intellij.ide.starter.ide.command.MarshallableCommand
 +import com.intellij.ide.starter.project.ProjectInfo
 +import com.intellij.ide.starter.project.ProjectInfoSpec
 +
 +data class TestCase(
 +  val ideInfo: IdeInfo,
 +  val projectInfo: ProjectInfoSpec? = null,
 +  val commands: Iterable<MarshallableCommand> = listOf(),
 +  val vmOptionsFix: VMOptions.() -> VMOptions = { this },
 +  val useInMemoryFileSystem: Boolean = false
 +) {
 +  fun withProject(projectInfo: ProjectInfoSpec): TestCase = copy(projectInfo = projectInfo)
 +
 +  fun withCommands(commands: Iterable<MarshallableCommand> = this.commands): TestCase = copy(commands = commands.toList())
 +
-   fun markReusable(isReusable: Boolean = true) = copy(projectInfo = (projectInfo as ProjectInfo).copy(isReusable = isReusable))
 +  /** On each test run the project will be unpacked again.
 +   * This guarantees that there is not side effects from previous test runs
 +   **/
++  fun markNotReusable(): TestCase = copy(projectInfo = (projectInfo as ProjectInfo).copy(isReusable = false))
 +
 +  /**
 +   * [buildNumber] - EAP build number to download
 +   * E.g: "222.3244.1"
 +   * If empty - the latest EAP will be downloaded.
 +   * [Downloads for IDEA Ultimate](https://www.jetbrains.com/idea/download/other.html)
 +   **/
 +  fun useEAP(buildNumber: String = ""): TestCase {
 +    return copy(ideInfo = ideInfo.copy(buildType = BuildType.EAP.type, buildNumber = buildNumber))
 +  }
 +
 +  /**
 +   * [version] - Release version to download
 +   * E.g: "2022.1.2"
 +   * If empty - the latest release will be downloaded.
 +   * [Downloads for IDEA Ultimate](https://www.jetbrains.com/idea/download/other.html)
 +   **/
 +  fun useRelease(version: String = ""): TestCase {
 +    return copy(ideInfo = ideInfo.copy(buildType = BuildType.RELEASE.type, version = version))
 +  }
 +}
index 2ea2bcced2fbb5f4a87f3f4c3539fb1558a261f8,0000000000000000000000000000000000000000..4adc8f548db3b64e6371c98ac4aa04683eb55a4f
mode 100644,000000..100644
--- /dev/null
@@@ -1,237 -1,0 +1,247 @@@
 +package com.intellij.ide.starter.models
 +
 +import com.intellij.ide.starter.ide.InstalledIde
 +import com.intellij.ide.starter.ide.command.MarshallableCommand
 +import com.intellij.ide.starter.path.IDEDataPaths
 +import com.intellij.ide.starter.system.SystemInfo
 +import com.intellij.ide.starter.utils.FileSystem.cleanPathFromSlashes
 +import com.intellij.ide.starter.utils.logOutput
 +import com.intellij.ide.starter.utils.writeJvmArgsFile
 +import java.io.File
 +import java.nio.file.Path
 +import kotlin.io.path.createDirectories
 +import kotlin.io.path.readLines
 +import kotlin.io.path.writeLines
 +import kotlin.io.path.writeText
 +
 +/**
 + * allows to combine VMOptions mapping functions easily by calling this function as
 + * ```
 + *    {}.andThen {} function
 + * ```
 + */
 +fun (VMOptions.() -> VMOptions).andThen(right: VMOptions.() -> VMOptions): VMOptions.() -> VMOptions = {
 +  val left = this@andThen
 +  this.left().right()
 +}
 +
 +
 +data class VMOptions(
 +  private val ide: InstalledIde,
 +  private val data: List<String>,
 +  val env: Map<String, String>
 +) {
 +  companion object {
 +    fun readIdeVMOptions(ide: InstalledIde, file: Path): VMOptions {
 +      return VMOptions(
 +        ide = ide,
 +        data = file
 +          .readLines()
 +          .map { it.trim() }
 +          .filter { it.isNotBlank() },
 +        env = emptyMap()
 +      )
 +    }
 +  }
 +
 +  override fun toString() = buildString {
 +    appendLine("VMOptions{")
 +    appendLine("  env=$env")
 +    for (line in data) {
 +      appendLine("  $line")
 +    }
 +    appendLine("} // VMOptions")
 +  }
 +
 +  fun addSystemProperty(key: String, value: Boolean): VMOptions = addSystemProperty(key, value.toString())
 +
 +  fun addSystemProperty(key: String, value: Int): VMOptions = addSystemProperty(key, value.toString())
 +
 +  fun addSystemProperty(key: String, value: Long): VMOptions = addSystemProperty(key, value.toString())
 +
 +  fun addSystemProperty(key: String, value: Path): VMOptions = addSystemProperty(key, value.toAbsolutePath().toString())
 +
 +  fun addSystemProperty(key: String, value: String): VMOptions {
 +    logOutput("Setting system property: [$key=$value]")
 +    System.setProperty(key, value) // to synchronize behaviour in IDEA and on test runner side
 +    return addLine(line = "-D$key=$value", filterPrefix = "-D$key=")
 +  }
 +
 +  fun addLine(line: String, filterPrefix: String? = null): VMOptions {
 +    if (data.contains(line)) return this
 +    val copy = if (filterPrefix == null) data else data.filterNot { it.trim().startsWith(filterPrefix) }
 +    return copy(data = copy + line)
 +  }
 +
 +  private fun filterKeys(toRemove: (String) -> Boolean) = copy(data = data.filterNot(toRemove))
 +
 +  fun withEnv(key: String, value: String) = copy(env = env + (key to value))
 +
 +  fun writeIntelliJVmOptionFile(path: Path) {
 +    path.writeLines(data)
 +    logOutput("Write vmoptions patch to $path")
 +  }
 +
 +  fun diffIntelliJVmOptionFile(theFile: Path): VMOptionsDiff {
 +    val loadedOptions = readIdeVMOptions(this.ide, theFile).data
 +    return VMOptionsDiff(originalLines = this.data, actualLines = loadedOptions)
 +  }
 +
 +  fun writeJavaArgsFile(theFile: File) {
 +    writeJvmArgsFile(theFile, this.data)
 +  }
 +
 +  fun overrideDirectories(paths: IDEDataPaths) = this
 +    .addSystemProperty("idea.config.path", paths.configDir)
 +    .addSystemProperty("idea.system.path", paths.systemDir)
 +    .addSystemProperty("idea.plugins.path", paths.pluginsDir)
 +    .addSystemProperty("idea.log.path", paths.logsDir)
 +
 +  fun enableStartupPerformanceLog(perf: IDEStartupReports): VMOptions {
 +    return this
 +      .addSystemProperty("idea.log.perf.stats.file", perf.statsJSON)
 +  }
 +
 +  fun enableClassLoadingReport(filePath: Path): VMOptions {
 +    return this
 +      .addSystemProperty("idea.log.class.list.file", filePath)
 +      .addSystemProperty("idea.record.classpath.info", "true")
 +  }
 +
++  fun enableVmtraceClassLoadingReport(filePath: Path): VMOptions {
++    if (!VMTrace.isSupported) return this
++
++    val vmTraceFile = VMTrace.vmTraceFile
++
++    return this
++      .addSystemProperty("idea.log.vmtrace.file", filePath)
++      .addLine("-agentpath:${vmTraceFile.toAbsolutePath()}=${filePath.toAbsolutePath()}")
++  }
++
 +  fun configureLoggers(
 +    debugLoggers: List<String> = emptyList(),
 +    traceLoggers: List<String> = emptyList()
 +  ): VMOptions {
 +    val withDebug = if (debugLoggers.isNotEmpty()) {
 +      this.addSystemProperty("idea.log.debug.categories", debugLoggers.joinToString(separator = ",") { "#" + it.removePrefix("#") })
 +    }
 +    else {
 +      this
 +    }
 +
 +    return if (traceLoggers.isNotEmpty()) {
 +      withDebug.addSystemProperty("idea.log.trace.categories", traceLoggers.joinToString(separator = ",") { "#" + it.removePrefix("#") })
 +    }
 +    else {
 +      withDebug
 +    }
 +  }
 +
 +  fun debug(port: Int = 5005, suspend: Boolean = true): VMOptions {
 +    val suspendKey = if (suspend) "y" else "n"
 +    val configLine = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=${suspendKey},address=*:${port}"
 +    return addLine(configLine, filterPrefix = "-agentlib:jdwp")
 +  }
 +
 +  fun inHeadlessMode() = this
 +    .addSystemProperty("java.awt.headless", true)
 +
 +  fun disableStartupDialogs() = this
 +    .addSystemProperty("jb.consents.confirmation.enabled", false)
 +    .addSystemProperty("jb.privacy.policy.text", "<!--999.999-->")
 +
 +  fun takeScreenshotIfFailure(logsDir: Path) = this
 +    .addSystemProperty("ide.performance.screenshot.before.kill", logsDir.resolve("screenshot_beforeKill.jpg").toString())
 +
 +  fun installTestScript(testName: String,
 +                        paths: IDEDataPaths,
 +                        commands: Iterable<MarshallableCommand>): VMOptions {
 +    val scriptText = commands.joinToString(separator = System.lineSeparator()) { it.storeToString() }
 +
 +    val scriptFileName = testName.cleanPathFromSlashes(replaceWith = "_") + ".text"
 +    val scriptFile = paths.systemDir.resolve(scriptFileName).apply {
 +      parent.createDirectories()
 +    }
 +    scriptFile.writeText(scriptText)
 +
 +    return this.addSystemProperty("testscript.filename", scriptFile)
 +      // Use non-success status code 1 when running IDE as command line tool.
 +      .addSystemProperty("testscript.must.exist.process.with.non.success.code.on.ide.error", "true")
 +      // No need to report TeamCity test failure from within test script.
 +      .addSystemProperty("testscript.must.report.teamcity.test.failure.on.error", "false")
 +  }
 +
 +  /** @see com.intellij.startupTime.StartupTimeWithCDSonJDK13.runOnJDK13 **/
 +  fun withCustomJRE(jre: Path): VMOptions {
 +    if (SystemInfo.isLinux) {
 +      val jrePath = jre.toAbsolutePath().toString()
 +      val envKey = when (ide.productCode) {
 +        "IU" -> "IDEA_JDK"
 +        "WS" -> "WEBIDE_JDK"
 +        else -> error("Not supported for product $ide")
 +      }
 +      return this.withEnv(envKey, jrePath)
 +    }
 +
 +    if (SystemInfo.isMac) {
 +      //Does not work -- https://intellij-support.jetbrains.com/hc/en-us/articles/206544879-Selecting-the-JDK-version-the-IDE-will-run-under
 +      //see https://youtrack.jetbrains.com/issue/IDEA-223075
 +      //see Launcher.m:226
 +      val jrePath = jre.toAbsolutePath().toString()
 +      val envKey = when (ide.productCode) {
 +        "IU" -> "IDEA_JDK"
 +        "WS" -> "WEBSTORM_JDK"
 +        else -> error("Not supported for product $ide")
 +      }
 +      return this.withEnv(envKey, jrePath)
 +    }
 +
 +    if (SystemInfo.isWindows) {
 +      //see WinLauncher.rc and WinLauncher.cpp:294
 +      //see https://youtrack.jetbrains.com/issue/IDEA-223348
 +      val jrePath = jre.toRealPath().toString().replace("/", "\\")
 +      val envKey = when (ide.productCode) {
 +        "IU" -> "IDEA_JDK_64"
 +        "WS" -> "WEBIDE_JDK_64"
 +        else -> error("Not supported for product $ide")
 +      }
 +      return this.withEnv(envKey, jrePath)
 +    }
 +
 +    error("Current OS is not supported")
 +  }
 +
 +  fun usingStartupFramework() = this
 +    .addSystemProperty("startup.performance.framework", true)
 +
 +  fun setFlagIntegrationTests() = this
 +    .addSystemProperty("idea.is.integration.test", true)
 +
 +  fun setFatalErrorNotificationEnabled() = this
 +    .addSystemProperty("idea.fatal.error.notification", true)
 +
 +  fun withJvmCrashLogDirectory(jvmCrashLogDirectory: Path) = this
 +    .addLine("-XX:ErrorFile=${jvmCrashLogDirectory.toAbsolutePath()}${File.separator}java_error_in_idea_%p.log", "-XX:ErrorFile=")
 +
 +  fun withHeapDumpOnOutOfMemoryDirectory(directory: Path) = this
 +    .addLine("-XX:HeapDumpPath=${directory.toAbsolutePath()}", "-XX:HeapDumpPath=")
 +
 +  fun withXmx(sizeMb: Int) = this
 +    .addLine("-Xmx" + sizeMb + "m", "-Xmx")
 +
 +  fun withG1GC() = this
 +    .filterKeys { it == "-XX:+UseConcMarkSweepGC" }
 +    .filterKeys { it == "-XX:+UseG1GC" }
 +    .addLine("-XX:+UseG1GC")
 +
 +  /** see [JEP 318](https://openjdk.org/jeps/318) **/
 +  fun withEpsilonGC() = this
 +    .filterKeys { it == "-XX:+UseConcMarkSweepGC" }
 +    .filterKeys { it == "-XX:+UseG1GC" }
 +    .addLine("-XX:+UnlockExperimentalVMOptions")
 +    .addLine("-XX:+UseEpsilonGC")
 +    .addLine("-Xmx16g", "-Xmx")
 +
 +  // a dummy wrapper to simplify expressions
 +  fun id() = this
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ba61fe1679dc7306c416b6805399f2087da3229c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++package com.intellij.ide.starter.models
++
++import com.intellij.ide.starter.system.SystemInfo
++import com.intellij.openapi.util.io.FileUtilRt
++import java.nio.file.Files
++import java.nio.file.Path
++
++/**
++ * Holds path to libvmtrace.so on disk.
++ */
++object VMTrace {
++  val vmTraceFile: Path
++
++  val isSupported: Boolean
++    get() = SystemInfo.isLinux || SystemInfo.isMac
++
++  init {
++    if (isSupported) {
++      val resourceName = when {
++        SystemInfo.isLinux -> "/libvmtrace.so"
++        SystemInfo.isMac && !SystemInfo.isAarch64 -> "/libvmtrace.dylib"
++        SystemInfo.isMac && SystemInfo.isAarch64 -> "/libvmtrace-aarch64.dylib"
++        else -> throw UnsupportedOperationException("Unsupported platform for libvmtrace")
++      }
++
++      vmTraceFile = Files.createTempFile("libvmtrace", "." + FileUtilRt.getExtension(resourceName))
++
++      val vmTraceBytes = VMOptions::class.java.getResourceAsStream(resourceName)!!
++        .use { it.readAllBytes() }
++      Files.write(vmTraceFile, vmTraceBytes)
++    }
++    else {
++      vmTraceFile = Path.of("unsupported-platform-libvmtrace")
++    }
++  }
++}
index dbbe2185c142d37e1fc4b8260a2956e52638ffa4,0000000000000000000000000000000000000000..4df318ac1f8f8e28714317bfc36d6f1ab6259e1c
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,57 @@@
-   open val testHomePath:Path = intelliJOutDirectory.resolve("perf-startup").createDirectories()
 +package com.intellij.ide.starter.path
 +
 +import com.intellij.ide.starter.ci.CIServer
 +import com.intellij.ide.starter.di.di
 +import com.intellij.ide.starter.utils.FileSystem.getDirectoryTreePresentableSizes
 +import com.intellij.ide.starter.utils.getDiskInfo
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import java.nio.file.Files
 +import java.nio.file.Path
 +import java.nio.file.Paths
 +import kotlin.io.path.createDirectories
 +import kotlin.io.path.div
 +
 +abstract class GlobalPaths(val checkoutDir: Path) {
 +  val intelliJOutDirectory: Path = checkoutDir.toAbsolutePath() / "out"
 +  val artifactsDirectory: Path = intelliJOutDirectory / "artifacts"
 +
 +  /**
 +   * Local => out
 +   * CI => out/tests
 +   */
 +  val compiledRootDirectory: Path = when (di.direct.instance<CIServer>().isBuildRunningOnCI) {
 +    true -> intelliJOutDirectory / "tests"
 +    false -> intelliJOutDirectory // Local run
 +  }
 +
-     !System.getProperty("agent.persistent.cache").isNullOrEmpty()
++  open val testHomePath: Path = intelliJOutDirectory.resolve("perf-startup").createDirectories()
 +
 +  val installersDirectory = (testHomePath / "installers").createDirectories()
 +
 +  val testsDirectory = (testHomePath / "tests").createDirectories()
 +
 +  private val cacheDirectory: Path = if (di.direct.instance<CIServer>().isBuildRunningOnCI &&
++                                         !System.getProperty("agent.persistent.cache").isNullOrEmpty()
 +  ) {
 +    (Paths.get(System.getProperty("agent.persistent.cache"), "perf-tests-cache")).createDirectories()
 +  }
 +  else {
 +    (testHomePath / "cache").createDirectories()
 +  }
 +
 +  fun getCacheDirectoryFor(entity: String): Path = (cacheDirectory / entity).createDirectories()
 +
 +  fun getDiskUsageDiagnostics(): String {
 +    return buildString {
 +      appendLine("Disk usage by integration tests (home $testHomePath)")
 +      appendLine(Files.getFileStore(testHomePath).getDiskInfo())
 +      appendLine()
 +      appendLine(testHomePath.getDirectoryTreePresentableSizes(3))
 +      if (cacheDirectory != testHomePath / "cache") {
 +        appendLine("Agent persistent cache directory disk usage $cacheDirectory")
 +        appendLine(cacheDirectory.getDirectoryTreePresentableSizes(2))
 +      }
 +    }
 +  }
 +}
index e465aa8ab5a5b9bf16cc9896465b98f4c236d647,0000000000000000000000000000000000000000..5f8cfbd0ab28e94b85a2a49afa58eb407ca21b78
mode 100644,000000..100644
--- /dev/null
@@@ -1,135 -1,0 +1,136 @@@
-     } else {
 +package com.intellij.ide.starter.plugins
 +
 +import com.intellij.ide.starter.di.di
 +import com.intellij.ide.starter.ide.IDETestContext
 +import com.intellij.ide.starter.ide.InstalledIde
 +import com.intellij.ide.starter.path.GlobalPaths
 +import com.intellij.ide.starter.utils.FileSystem
 +import com.intellij.ide.starter.utils.HttpClient
 +import com.intellij.ide.starter.utils.logError
 +import com.intellij.ide.starter.utils.logOutput
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import java.nio.file.Path
 +import java.util.jar.JarFile
 +import kotlin.io.path.div
 +import kotlin.io.path.exists
 +import kotlin.io.path.readLines
 +import kotlin.io.path.writeLines
 +
 +open class PluginConfigurator(val testContext: IDETestContext) {
 +  val disabledPluginsPath: Path
 +    get() = testContext.paths.configDir / "disabled_plugins.txt"
 +
 +  fun setupPluginFromPath(pathToPluginArchive: Path) = apply {
 +    FileSystem.unpack(pathToPluginArchive, testContext.paths.pluginsDir)
 +  }
 +
 +  fun setupPluginFromURL(urlToPluginZipFile: String) = apply {
 +    val pluginRootDir = di.direct.instance<GlobalPaths>().getCacheDirectoryFor("plugins")
 +    val pluginZip: Path = pluginRootDir / testContext.ide.build / urlToPluginZipFile.substringAfterLast("/")
 +
 +    HttpClient.download(urlToPluginZipFile, pluginZip)
 +    FileSystem.unpack(pluginZip, testContext.paths.pluginsDir)
 +  }
 +
 +  fun setupPluginFromPluginManager(
 +    pluginId: String,
 +    ide: InstalledIde,
 +    channel: String? = null,
 +  ) = apply {
 +    logOutput("Setting up plugin: $pluginId ...")
 +
 +    val fileName = pluginId.replace(".", "-") + ".zip"
 +    val downloadedPlugin = di.direct.instance<GlobalPaths>().getCacheDirectoryFor("plugins") / testContext.ide.build / fileName
 +    if (!downloadedPlugin.toFile().exists()) {
 +      val url = buildString {
 +        append("https://plugins.jetbrains.com/pluginManager/")
 +        append("?action=download")
 +        append("&id=${pluginId.replace(" ", "%20")}")
 +        append("&noStatistic=false")
 +        append("&build=${ide.productCode}-${ide.build}")
 +        channel?.let {
 +          append("&channel=$it")
 +        }
 +      }
 +      if (HttpClient.download(url, downloadedPlugin, retries = 1)) {
 +        FileSystem.unpack(downloadedPlugin, testContext.paths.pluginsDir)
 +      }
 +      else {
 +        logError("Plugin $pluginId downloading failed, skipping")
 +        return@apply
 +      }
++    }
++    else {
 +      FileSystem.unpack(downloadedPlugin, testContext.paths.pluginsDir)
 +    }
 +    logOutput("Plugin $pluginId setup finished")
 +  }
 +
 +  fun disablePlugins(vararg pluginIds: String) = disablePlugins(pluginIds.toSet())
 +
 +  fun disablePlugins(pluginIds: Set<String>) = also {
 +    disabledPluginsPath.writeLines(disabledPluginIds + pluginIds)
 +  }
 +
 +  fun enablePlugins(vararg pluginIds: String) = enablePlugins(pluginIds.toSet())
 +
 +  fun enablePlugins(pluginIds: Set<String>) = also {
 +    disabledPluginsPath.writeLines(disabledPluginIds - pluginIds)
 +  }
 +
 +  val disabledPluginIds: Set<String>
 +    get() {
 +      val file = disabledPluginsPath
 +      return if (file.exists()) file.readLines().toSet() else emptySet()
 +    }
 +
 +
 +  fun findPluginXmlByPluginIdInAGivenDir(pluginId: String, bundledPluginsDir: Path): Boolean {
 +    val jarFiles = bundledPluginsDir.toFile().walk().filter { it.name.endsWith(".jar") }.toList()
 +    jarFiles.forEach {
 +      val jarFile = JarFile(it)
 +      val entry = jarFile.getJarEntry("META-INF/plugin.xml")
 +      if (entry != null) {
 +        val inputStream = jarFile.getInputStream(entry)
 +        val text: String = inputStream.bufferedReader(Charsets.UTF_8).use { reader -> reader.readText() }
 +        if (text.contains(" <id>$pluginId</id>")) {
 +          return true
 +        }
 +      }
 +    }
 +    return false
 +  }
 +
 +
 +  fun getPluginInstalledState(pluginId: String): PluginInstalledState {
 +    if (disabledPluginsPath.toFile().exists() && pluginId in disabledPluginIds) {
 +      return PluginInstalledState.DISABLED
 +    }
 +
 +    val installedPluginDir = testContext.paths.pluginsDir
 +    if (findPluginXmlByPluginIdInAGivenDir(pluginId, installedPluginDir)) {
 +      return PluginInstalledState.INSTALLED
 +    }
 +
 +    val bundledPluginsDir = testContext.ide.bundledPluginsDir
 +    if (bundledPluginsDir == null) {
 +      logOutput("Cannot ensure a plugin '$pluginId' is installed in ${testContext.ide}. Consider it is installed.")
 +      return PluginInstalledState.INSTALLED
 +    }
 +
 +    if (findPluginXmlByPluginIdInAGivenDir(pluginId, bundledPluginsDir)) {
 +      return PluginInstalledState.BUNDLED_TO_IDE
 +    }
 +    return PluginInstalledState.NOT_INSTALLED
 +  }
 +
 +  fun assertPluginIsInstalled(pluginId: String): PluginConfigurator {
 +    when (getPluginInstalledState(pluginId)) {
 +      PluginInstalledState.DISABLED -> error("Plugin '$pluginId' must not be listed in the disabled plugins file ${disabledPluginsPath}")
 +      PluginInstalledState.NOT_INSTALLED -> error("Plugin '$pluginId' must be installed")
 +      PluginInstalledState.BUNDLED_TO_IDE -> return this
 +      PluginInstalledState.INSTALLED -> return this
 +    }
 +  }
 +}
index 56710095106ae5a1539864988fea92eab55f4440,0000000000000000000000000000000000000000..28a86e01c8dec9ad7f17950f75b13f4d587e2bfd
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,101 @@@
- package com.intellij.ide.starter.exec
++package com.intellij.ide.starter.process.exec
 +
- import com.intellij.ide.starter.exec.ExecOutputRedirect.*
++import com.intellij.ide.starter.process.exec.ExecOutputRedirect.*
 +import com.intellij.ide.starter.utils.logOutput
 +import java.io.File
 +import java.io.PrintWriter
 +import kotlin.io.path.createDirectories
 +
 +/**
 + * Specifies how a child process' stdout or stderr must be redirected in the current process:
 + * - [NoRedirect]
 + * - [ToFile]
 + * - [ToStdOut]
 + */
 +sealed class ExecOutputRedirect {
 +
 +  open fun open() = Unit
 +
 +  open fun close() = Unit
 +
 +  open fun redirectLine(line: String) = Unit
 +
 +  abstract fun read(): String
 +
 +  abstract override fun toString(): String
 +
 +  protected fun reportOnStdoutIfNecessary(line: String) {
 +    // Propagate the IDE debugger attach service message.
 +    if (line.contains("Listening for transport dt_socket")) {
 +      println(line)
 +    }
 +  }
 +
 +  object NoRedirect : ExecOutputRedirect() {
 +    override fun redirectLine(line: String) {
 +      reportOnStdoutIfNecessary(line)
 +    }
 +
 +    override fun read() = ""
 +
 +    override fun toString() = "ignored"
 +  }
 +
 +  data class ToFile(val outputFile: File) : ExecOutputRedirect() {
 +
 +    private lateinit var writer: PrintWriter
 +
 +    override fun open() {
 +      outputFile.apply {
 +        toPath().parent.createDirectories()
 +        createNewFile()
 +      }
 +      writer = outputFile.printWriter()
 +    }
 +
 +    override fun close() {
 +      writer.close()
 +    }
 +
 +    override fun redirectLine(line: String) {
 +      reportOnStdoutIfNecessary(line)
 +      writer.println(line)
 +    }
 +
 +    override fun read(): String {
 +      if (!outputFile.exists()) {
 +        logOutput("File $outputFile doesn't exist")
 +        return ""
 +      }
 +
 +      return outputFile.readText()
 +    }
 +
 +    override fun toString() = "file $outputFile"
 +  }
 +
 +  data class ToStdOut(val prefix: String) : ExecOutputRedirect() {
 +    override fun redirectLine(line: String) {
 +      reportOnStdoutIfNecessary(line)
 +      logOutput("  $prefix $line")
 +    }
 +
 +    override fun read() = ""
 +
 +    override fun toString() = "stdout"
 +  }
 +
 +  class ToString : ExecOutputRedirect() {
 +
 +    private val stringBuilder = StringBuilder()
 +
 +    override fun redirectLine(line: String) {
 +      reportOnStdoutIfNecessary(line)
 +      stringBuilder.appendLine(line)
 +    }
 +
 +    override fun read() = stringBuilder.toString()
 +
 +    override fun toString() = "string"
 +  }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3bada487a59dd092bf2fdf293ee1bc2d29114b84
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++package com.intellij.ide.starter.process.exec
++
++import kotlin.time.Duration
++
++class ExecTimeoutException(private val processName: String,
++                           private val timeout: Duration) : RuntimeException() {
++  override val message
++    get() = "Failed to wait for the process `$processName` to complete in $timeout"
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b5197adefdd38b0b101676c162c317d1535c2bb4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,228 @@@
++package com.intellij.ide.starter.process.exec
++
++import com.intellij.ide.starter.coroutine.supervisorScope
++import com.intellij.ide.starter.utils.catchAll
++import com.intellij.ide.starter.utils.logError
++import com.intellij.ide.starter.utils.logOutput
++import kotlinx.coroutines.*
++import kotlinx.coroutines.future.await
++import java.io.IOException
++import java.lang.Runnable
++import java.nio.file.Files
++import java.nio.file.Path
++import java.util.concurrent.TimeUnit
++import kotlin.concurrent.thread
++import kotlin.io.path.exists
++import kotlin.io.path.readText
++import kotlin.time.Duration
++import kotlin.time.Duration.Companion.minutes
++import kotlin.time.Duration.Companion.seconds
++
++class ProcessExecutor(val presentableName: String,
++                      val workDir: Path?,
++                      val timeout: Duration = 10.minutes,
++                      val environmentVariables: Map<String, String> = System.getenv(),
++                      val args: List<String>,
++                      val errorDiagnosticFiles: List<Path> = emptyList(),
++                      val stdoutRedirect: ExecOutputRedirect = ExecOutputRedirect.NoRedirect,
++                      val stderrRedirect: ExecOutputRedirect = ExecOutputRedirect.NoRedirect,
++                      val onProcessCreated: suspend (Process, Long) -> Unit = { _, _ -> },
++                      val onBeforeKilled: suspend (Process, Long) -> Unit = { _, _ -> },
++                      val stdInBytes: ByteArray = byteArrayOf(),
++                      val onlyEnrichExistedEnvVariables: Boolean = false) {
++
++  private fun redirectProcessOutput(
++    process: Process,
++    outOrErrStream: Boolean,
++    redirectOutput: ExecOutputRedirect
++  ): Thread {
++    val inputStream = if (outOrErrStream) process.inputStream else process.errorStream
++    return thread(start = true, isDaemon = true, name = "Redirect " + (if (outOrErrStream) "stdout" else "stderr")) {
++      val reader = inputStream.bufferedReader()
++      redirectOutput.open()
++      try {
++        while (true) {
++          val line = try {
++            reader.readLine() ?: break
++          }
++          catch (e: IOException) {
++            break
++          }
++          redirectOutput.redirectLine(line)
++        }
++      }
++      finally {
++        redirectOutput.close()
++      }
++    }
++  }
++
++  private fun redirectProcessInput(process: Process, inputBytes: ByteArray): Thread? {
++    if (inputBytes.isEmpty()) {
++      catchAll { process.outputStream.close() }
++      return null
++    }
++
++    return thread(start = true, isDaemon = true, name = "Redirect input") {
++      catchAll {
++        process.outputStream.use {
++          it.write(inputBytes)
++        }
++      }
++    }
++  }
++
++  private fun ProcessBuilder.actualizeEnvVariables(environmentVariables: Map<String, String> = System.getenv(),
++                                                   onlyEnrichExistedEnvVariables: Boolean = false): ProcessBuilder {
++    val processEnvironment = environment()
++    if (processEnvironment == environmentVariables) return this
++
++    environmentVariables.filter { it.value == null }.forEach { logError("Env variable: ${it.key} has null value ${it.value}") }
++    val notNullValues = environmentVariables.filter { it.value != null }
++
++    // env variables enrichment
++    processEnvironment.putAll(notNullValues)
++
++    if (!onlyEnrichExistedEnvVariables) {
++      val missingKeys = processEnvironment.keys - notNullValues.keys
++      missingKeys.forEach { key -> processEnvironment.remove(key) }
++    }
++
++    return this
++  }
++
++  private fun killProcessGracefully(process: ProcessHandle) {
++    process.destroy()
++    runBlocking { withTimeout(20.seconds) { process.onExit().await() } }
++    process.destroyForcibly()
++  }
++
++  private fun analyzeProcessExit(process: Process) {
++    val code = process.exitValue()
++    if (code != 0) {
++      val linesLimit = 100
++
++      logOutput("  ... failed external process `$presentableName` with exit code $code")
++      val message = buildString {
++        appendLine("External process `$presentableName` failed with code $code")
++        for (diagnosticFile in errorDiagnosticFiles.filter { it.exists() && Files.size(it) > 0 }) {
++          appendLine(diagnosticFile.fileName.toString())
++          appendLine(diagnosticFile.readText().lines().joinToString(System.lineSeparator()) { "  $it" })
++        }
++
++        stderrRedirect.read().lines().apply {
++          take(linesLimit).dropWhile { it.trim().isBlank() }.let { lines ->
++            if (lines.isNotEmpty()) {
++              appendLine("  FIRST $linesLimit lines of the standard error stream")
++              lines.forEach { appendLine("    $it") }
++            }
++          }
++
++          if (size > linesLimit) {
++            appendLine("...")
++
++            takeLast(linesLimit).dropWhile { it.trim().isBlank() }.let { lines ->
++              if (lines.isNotEmpty()) {
++                appendLine("  LAST $linesLimit lines of the standard error stream")
++                lines.forEach { appendLine("    $it") }
++              }
++            }
++          }
++        }
++
++        stdoutRedirect.read().lines().takeLast(linesLimit).dropWhile { it.trim().isEmpty() }.let { lines ->
++          if (lines.isNotEmpty()) {
++            appendLine("  LAST $linesLimit lines of the standard output stream")
++            lines.forEach { appendLine("    $it") }
++          }
++        }
++      }
++      error(message)
++    }
++
++    logOutput("  ... successfully finished external process for `$presentableName` with exit code 0")
++  }
++
++  /**
++   * Creates new process and wait for it's completion
++   */
++  @Throws(ExecTimeoutException::class)
++  fun start() {
++    logOutput(buildString {
++      appendLine("Running external process for `$presentableName`")
++      appendLine("  Working directory: $workDir")
++      appendLine("  Arguments: [${args.joinToString()}]")
++      appendLine("  STDOUT will be redirected to: $stdoutRedirect")
++      appendLine("  STDERR will be redirected to: $stderrRedirect")
++      append("  STDIN is empty: " + stdInBytes.isEmpty())
++    })
++
++    require(args.isNotEmpty()) { "Arguments must be not empty to start external process" }
++
++    val processBuilder = ProcessBuilder()
++      .directory(workDir?.toFile())
++      .command(*args.toTypedArray())
++      .redirectInput(ProcessBuilder.Redirect.PIPE)
++      .redirectOutput(ProcessBuilder.Redirect.PIPE)
++      .redirectError(ProcessBuilder.Redirect.PIPE)
++      .actualizeEnvVariables(environmentVariables, onlyEnrichExistedEnvVariables)
++
++    logOutput(
++      """
++      Process: `$presentableName`
++      Arguments: ${args.joinToString(separator = " ")}
++      Environment variables: [${processBuilder.environment().entries.joinToString { "${it.key}=${it.value}" }}]
++    """.trimIndent())
++    val process = processBuilder.start()
++
++    val processId = process.pid()
++    val onProcessCreatedJob: Job = supervisorScope.launch {
++      logOutput("  ... started external process `$presentableName` with process ID = $processId")
++      onProcessCreated(process, processId)
++    }
++
++    val inputThread = redirectProcessInput(process, stdInBytes)
++    val stdoutThread = redirectProcessOutput(process, true, stdoutRedirect)
++    val stderrThread = redirectProcessOutput(process, false, stderrRedirect)
++    val ioThreads = listOfNotNull(inputThread, stdoutThread, stderrThread)
++
++    fun killProcess() {
++      catchAll { runBlocking { onProcessCreatedJob.cancelAndJoin() } }
++      catchAll { runBlocking { withTimeout(1.minutes) { onBeforeKilled(process, processId) } } }
++      process.descendants().forEach { catchAll { killProcessGracefully(it) } }
++      catchAll { killProcessGracefully(process.toHandle()) }
++      catchAll { ioThreads.forEach { it.interrupt() } }
++    }
++
++    val stopper = Runnable {
++      logOutput(
++        "   ... terminating process `$presentableName` by request from external process (either SIGTERM or SIGKILL is caught) ...")
++      killProcess()
++    }
++
++    val stopperThread = Thread(stopper, "process-shutdown-hook")
++    try {
++      Runtime.getRuntime().addShutdownHook(stopperThread)
++    }
++    catch (e: IllegalStateException) {
++      logError("Process: $presentableName. Shutdown hook cannot be added because: ${e.message}")
++    }
++
++    try {
++      if (!runCatching { process.waitFor(timeout.inWholeSeconds, TimeUnit.SECONDS) }.getOrDefault(false)) {
++        stopperThread.apply {
++          start()
++          join(20.seconds.inWholeMilliseconds)
++        }
++        throw ExecTimeoutException(args.joinToString(" "), timeout)
++      }
++    }
++    finally {
++      catchAll { Runtime.getRuntime().removeShutdownHook(stopperThread) }
++    }
++
++    ioThreads.forEach { catchAll { it.join() } }
++
++    analyzeProcessExit(process)
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51865d971788ee82330fb3f7b0d178aa67796d7e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++package com.intellij.ide.starter.process.exec
++
++import com.intellij.ide.starter.system.SystemInfo
++import com.intellij.ide.starter.utils.logOutput
++import java.nio.file.Path
++import kotlin.io.path.div
++import kotlin.time.Duration.Companion.minutes
++
++
++fun executeScript(fileNameToExecute: String, projectDirPath: Path) {
++  val stdout = ExecOutputRedirect.ToString()
++  val stderr = ExecOutputRedirect.ToString()
++
++  ProcessExecutor(
++    presentableName = "Executing of $fileNameToExecute",
++    workDir = projectDirPath,
++    timeout = 20.minutes,
++    args = listOf(fileNameToExecute),
++    stdoutRedirect = stdout,
++    stderrRedirect = stderr
++  ).start()
++
++  val commit = stdout.read().trim()
++  val error = stderr.read().trim()
++
++  logOutput("Stdout of command execution $commit")
++  logOutput("Stderr of command execution $error")
++}
++
++fun execGradlew(pathToProject: Path, args: List<String>) {
++  val stdout = ExecOutputRedirect.ToString()
++  val stderr = ExecOutputRedirect.ToString()
++
++  val command = when (SystemInfo.isWindows) {
++    true -> (pathToProject / "gradlew.bat").toString()
++    false -> "./gradlew"
++  }
++
++  if (!SystemInfo.isWindows) {
++    ProcessExecutor(
++      presentableName = "chmod gradlew",
++      workDir = pathToProject,
++      timeout = 1.minutes,
++      args = listOf("chmod", "+x", "gradlew"),
++      stdoutRedirect = stdout,
++      stderrRedirect = stderr
++    ).start()
++  }
++
++  ProcessExecutor(
++    presentableName = "Gradle Format",
++    workDir = pathToProject,
++    timeout = 1.minutes,
++    args = listOf(command) + args,
++    stdoutRedirect = stdout,
++    stderrRedirect = stderr
++  ).start()
++}
index 501a36b222a1a177d7516bafd372e9055d090955,0000000000000000000000000000000000000000..36476111717062b590e1d80b52d00b24a9a4abe6
mode 100644,000000..100644
--- /dev/null
@@@ -1,255 -1,0 +1,261 @@@
- import com.intellij.ide.starter.exec.ExecOutputRedirect
- import com.intellij.ide.starter.exec.exec
 +package com.intellij.ide.starter.process
 +
 +import com.intellij.ide.starter.di.di
-   exec("ps",
-        di.direct.instance<GlobalPaths>().testsDirectory,
-        timeout = 1.minutes,
-        args = listOf("ps", "-ax"),
-        stdoutRedirect = stdoutRedirect)
 +import com.intellij.ide.starter.path.GlobalPaths
++import com.intellij.ide.starter.process.exec.ExecOutputRedirect
++import com.intellij.ide.starter.process.exec.ProcessExecutor
 +import com.intellij.ide.starter.system.SystemInfo
 +import com.intellij.ide.starter.utils.catchAll
 +import com.intellij.ide.starter.utils.logOutput
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import java.nio.file.Path
 +import kotlin.io.path.isRegularFile
 +import kotlin.time.Duration.Companion.minutes
 +import kotlin.time.Duration.Companion.seconds
 +
 +// TODO: consider using https://github.com/oshi/oshi for acquiring process info
 +
 +/**
 + * TeamCity may not kill processes started during the build (TW-69045).
 + * They stay alive and consume resources after tests.
 + * This lead to OOM and other errors during tests, for example,
 + * IDEA-256265: shared-indexes tests on Linux suspiciously fail with 137 (killed by OOM)
 + */
 +fun killOutdatedProcessesOnUnix(commandsToSearch: Iterable<String> = listOf("/perf-startup/")) {
 +  if (SystemInfo.isWindows) {
 +    logOutput("Current system is Windows. No logic for analysis of outdated processes is yet implemented.")
 +    return
 +  }
 +
 +  val processes = arrayListOf<ProcessMetaInfo>()
 +
 +  if (SystemInfo.isLinux) catchAll { processes += dumpListOfProcessesOnLinux() }
 +  else catchAll { processes += dumpListOfProcessesOnMacOS() }
 +
 +  val processIdsToKill = processes.filter { process ->
 +    commandsToSearch.any { process.command.contains(it) }
 +  }.map { it.pid }
 +
 +  logOutput("These Unix processes must be killed before the next test run: [$processIdsToKill]")
 +  for (pid in processIdsToKill) {
 +    catchAll { killProcessOnUnix(pid) }
 +  }
 +}
 +
 +fun dumpListOfProcessesOnMacOS(): List<MacOsProcessMetaInfo> {
 +  check(SystemInfo.isMac)
 +  val stdoutRedirect = ExecOutputRedirect.ToString()
-   exec("ps",
-        di.direct.instance<GlobalPaths>().testsDirectory,
-        timeout = 1.minutes,
-        args = listOf("ps", "-aux"),
-        stdoutRedirect = stdoutRedirect)
++  ProcessExecutor("ps",
++                  di.direct.instance<GlobalPaths>().testsDirectory,
++                  timeout = 1.minutes,
++                  args = listOf("ps", "-ax"),
++                  stdoutRedirect = stdoutRedirect
++  ).start()
++
 +  val processLines = stdoutRedirect.read().lines().drop(1).map { it.trim() }.filterNot { it.isBlank() }
 +  //PID TTY           TIME CMD
 +  //  1 ??         0:43.67 /sbin/launchd
 +  val processes = arrayListOf<MacOsProcessMetaInfo>()
 +  for (line in processLines) {
 +    var rest = line
 +    fun nextString(): String {
 +      val result = rest.substringBefore(" ").trim()
 +      rest = rest.substringAfter(" ").dropWhile { it == ' ' }
 +      return result
 +    }
 +
 +    val pid = nextString().toInt()
 +    nextString() //TTY
 +    nextString() //TIME
 +    val command = rest
 +    processes += MacOsProcessMetaInfo(pid, command)
 +  }
 +  return processes
 +}
 +
 +fun dumpListOfProcessesOnLinux(): List<LinuxProcessMetaInfo> {
 +  check(SystemInfo.isLinux)
 +  val stdoutRedirect = ExecOutputRedirect.ToString()
-   exec(
++  ProcessExecutor("ps",
++                  di.direct.instance<GlobalPaths>().testsDirectory,
++                  timeout = 1.minutes,
++                  args = listOf("ps", "-aux"),
++                  stdoutRedirect = stdoutRedirect
++  ).start()
++
 +  val processLines = stdoutRedirect.read().lines().drop(1).map { it.trim() }.filterNot { it.isBlank() }
 +  //USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
 +  //root       823  0.0  0.0 1576524 8128 ?        Ssl  дек01   0:08 /usr/bin/containerd
 +  val processes = arrayListOf<LinuxProcessMetaInfo>()
 +  for (line in processLines) {
 +    var rest = line
 +    fun nextString(): String {
 +      val result = rest.substringBefore(" ").trim()
 +      rest = rest.substringAfter(" ").dropWhile { it == ' ' }
 +      return result
 +    }
 +    nextString() //user
 +    val pid = nextString().toInt()
 +    nextString() //cpu
 +    nextString() //mem
 +    val vsz = nextString().toInt()
 +    val rss = nextString().toInt()
 +    nextString() //tty
 +    nextString() //stat
 +    nextString() //start
 +    nextString() //time
 +    val command = rest
 +    processes += LinuxProcessMetaInfo(pid, vsz, rss, command)
 +  }
 +  return processes
 +}
 +
 +private fun killProcessOnUnix(pid: Int) {
 +  check(SystemInfo.isUnix)
 +  logOutput("Killing process $pid")
 +
-   )
++  ProcessExecutor(
 +    "kill-process-$pid",
 +    di.direct.instance<GlobalPaths>().testsDirectory,
 +    timeout = 1.minutes,
 +    args = listOf("kill", "-9", pid.toString()),
 +    stdoutRedirect = ExecOutputRedirect.ToStdOut("[kill-$pid-out]"),
 +    stderrRedirect = ExecOutputRedirect.ToStdOut("[kill-$pid-err]")
-   exec(
++  ).start()
 +}
 +
 +/**
 + * Workaround for IDEA-251643.
 + * On Linux we run IDE using `xvfb-run` tool wrapper.
 + * Thus we must guess the original java process ID for capturing the thread dumps.
 + * TODO: try to use java.lang.ProcessHandle to get the parent process ID.
 + */
 +fun getJavaProcessId(javaHome: Path, workDir: Path, originalProcessId: Long, originalProcess: Process): Long {
 +  if (!SystemInfo.isLinux) {
 +    return originalProcessId
 +  }
 +  logOutput("Guessing java process ID on Linux (pid of the java process wrapper - $originalProcessId)")
 +
 +  val stdout = ExecOutputRedirect.ToString()
 +  val stderr = ExecOutputRedirect.ToString()
-   )
++  ProcessExecutor(
 +    "jcmd-run",
 +    workDir,
 +    timeout = 1.minutes,
 +    args = listOf(javaHome.resolve("bin/jcmd").toAbsolutePath().toString()),
 +    stdoutRedirect = stdout,
 +    stderrRedirect = stderr
-   exec(
++  ).start()
++
 +  val mergedOutput = stdout.read() + "\n" + stderr.read()
 +  val candidates = arrayListOf<Long>()
 +  val candidatesFromProcessHandle = arrayListOf<Long>()
 +  logOutput("List all java processes IDs:")
 +  for (line in mergedOutput.lines().map { it.trim() }.filterNot { it.isEmpty() }) {
 +    logOutput(line)
 +    /*
 +    An example of a process line:
 +
 +    1578401 com.intellij.idea.Main /home/sergey.patrikeev/Documents/intellij/out/perf-startup/tests/IU-211.1852/ijx-jdk-empty/verify-shared-index/temp/projects/idea-startup-performance-project-test-03/idea-startup-performance-project-test-03
 +
 +    An example from TC:
 +    intellij project:
 +    81413 com.intellij.idea.Main /opt/teamcity-agent/work/71b862de01f59e23
 +
 +    another project:
 +    84318 com.intellij.idea.Main /opt/teamcity-agent/temp/buildTmp/startupPerformanceTests5985285665047908961/perf-startup/tests/IU-installer-from-file/spring_boot/indexing_oldProjectModel/projects/projects/spring-boot-master/spring-boot-master
 +
 +    An example from TC TestsDynamicBundledPluginsStableLinux
 +    1879942 com.intellij.idea.Main /opt/teamcity-agent/temp/buildTmp/startupPerformanceTests4436006118811351792/perf-startup/cache/projects/unpacked/javaproject_1.0.0/java-design-patterns-master
 +    */
 +
 +    //TODO(Monitor case /opt/teamcity-agent/work/)
 +    val pid = line.substringBefore(" ", "").toLongOrNull() ?: continue
 +    if (line.contains("com.intellij.idea.Main") && (line.contains("/perf-startup/tests/") || line.contains(
 +        "/perf-startup/cache/") || line.contains("/opt/teamcity-agent/work/"))) {
 +      candidates.add(pid)
 +    }
 +  }
 +
 +  originalProcess.toHandle().descendants().forEach { desc ->
 +    if (desc.info().command().get().contains("java")) {
 +      logOutput("Candidate from ProcessHandle process: ${desc.pid()}")
 +      logOutput("command: ${desc.info().command()}")
 +      candidatesFromProcessHandle.add(desc.pid())
 +    }
 +  }
 +
 +  if (candidates.isEmpty() && candidatesFromProcessHandle.isNotEmpty()) {
 +    logOutput("Candidates from jcmd are missing, will be used first one from ProcessHandle instead: " + candidatesFromProcessHandle.first())
 +    candidates.add(candidatesFromProcessHandle.first())
 +  }
 +
 +  if (candidates.isNotEmpty()) {
 +    logOutput("Found the following java process ID candidates: " + candidates.joinToString())
 +    if (originalProcessId in candidates) {
 +      return originalProcessId
 +    }
 +    return candidates.first()
 +  }
 +  else {
 +    return originalProcessId
 +  }
 +}
 +
 +fun collectJavaThreadDump(
 +  javaHome: Path,
 +  workDir: Path,
 +  javaProcessId: Long,
 +  dumpFile: Path,
 +  includeStdout: Boolean = true
 +) {
 +  val ext = if (SystemInfo.isWindows) ".exe" else ""
 +  val jstackPath = listOf(
 +    javaHome.resolve("bin/jstack$ext"),
 +    javaHome.parent.resolve("bin/jstack$ext")
 +  ).map { it.toAbsolutePath() }.firstOrNull { it.isRegularFile() } ?: error("Failed to locate jstack under $javaHome")
 +
 +  val command = listOf(jstackPath.toAbsolutePath().toString(), "-l", javaProcessId.toString())
 +
-   )
++  ProcessExecutor(
 +    "jstack",
 +    workDir,
 +    timeout = 1.minutes,
 +    args = command,
 +    stdoutRedirect = ExecOutputRedirect.ToFile(dumpFile.toFile()),
 +    stderrRedirect = ExecOutputRedirect.ToStdOut("[jstack-err]")
-   exec(
++  ).start()
 +
 +  if (includeStdout) {
 +    logOutput("jstack output:\n${dumpFile.toFile().readLines().joinToString("\n")}")
 +  }
 +}
 +
 +fun destroyGradleDaemonProcessIfExists() {
 +  val stdout = ExecOutputRedirect.ToString()
-   )
++  ProcessExecutor(
 +    "get jps process",
 +    workDir = null,
 +    timeout = 30.seconds,
 +    args = listOf("jps", "-l"),
 +    stdoutRedirect = stdout
++  ).start()
++
 +  logOutput("List of java processes: " + stdout.read())
 +
 +  if (stdout.read().contains("GradleDaemon")) {
 +    val readLines = stdout.read().split('\n')
 +    readLines.forEach {
 +      if (it.contains("GradleDaemon")) {
 +        logOutput("Killing GradleDaemon process")
 +        val processId = it.split(" ").first().toLong()
 +
 +        // get up-to date process list on every iteration
 +        ProcessHandle.allProcesses()
 +          .filter { ph -> ph.isAlive && ph.pid() == processId }
 +          .forEach { ph -> ph.destroy() }
 +      }
 +    }
 +  }
 +}
index 25f7aa3384e18c91e17b971994ad67e410494c1b,0000000000000000000000000000000000000000..ba5f4ef008471567a859e379a2598384135ea46f
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,68 @@@
-           var testName: String
 +package com.intellij.ide.starter.report
 +
 +import com.intellij.ide.starter.ci.CIServer
 +import com.intellij.ide.starter.di.di
++import com.intellij.ide.starter.runner.CurrentTestMethod
 +import com.intellij.ide.starter.utils.convertToHashCodeWithOnlyLetters
 +import com.intellij.ide.starter.utils.generifyErrorMessage
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import java.io.File
 +import java.nio.file.Path
 +import kotlin.io.path.isDirectory
 +import kotlin.io.path.listDirectoryEntries
 +
 +object ErrorReporter {
 +  private const val MAX_TEST_NAME_LENGTH = 250
 +
++  private fun getTestMethodName(): String {
++    val method = di.direct.instance<CurrentTestMethod>().get()
++    return if (method == null) "" else "${method.declaringClass.name}.${method.name}"
++  }
++
 +  /**
 +   * Sort things out from errors directories, written by performance testing plugin
 +   * Take a look at [com.jetbrains.performancePlugin.ProjectLoaded.reportErrorsFromMessagePool]
 +   */
 +  fun reportErrorsAsFailedTests(scriptErrorsDir: Path, contextName: String): List<Pair<File, File>> {
++    val testMethodName = getTestMethodName().ifEmpty { contextName }
++
 +    return if (scriptErrorsDir.isDirectory()) {
 +      val errorsDirectories = scriptErrorsDir.listDirectoryEntries()
 +
 +      errorsDirectories.map { errorDir ->
 +        val messageFile = errorDir.resolve("message.txt").toFile()
 +        val stacktraceFile = errorDir.resolve("stacktrace.txt").toFile()
 +
 +        if (messageFile.exists() && stacktraceFile.exists()) {
 +          val messageText = generifyErrorMessage(messageFile.readText().trim())
 +          val stackTraceContent = stacktraceFile.readText().trim()
 +
-                 Test: $contextName
++          val testName: String
 +
 +          val onlyLettersHash = convertToHashCodeWithOnlyLetters(generifyErrorMessage(stackTraceContent).hashCode())
 +
 +          if (stackTraceContent.startsWith(messageText)) {
 +            val maxLength = (MAX_TEST_NAME_LENGTH - onlyLettersHash.length).coerceAtMost(stackTraceContent.length)
 +            val extractedTestName = stackTraceContent.substring(0, maxLength).trim()
 +            testName = "($onlyLettersHash $extractedTestName)"
 +          }
 +          else {
 +            testName = "($onlyLettersHash ${messageText.substring(0, MAX_TEST_NAME_LENGTH.coerceAtMost(messageText.length)).trim()})"
 +          }
 +
 +          val stackTrace = """
++                Test: $testMethodName
 +                
 +                $stackTraceContent
 +              """.trimIndent().trimMargin().trim()
 +
 +          di.direct.instance<CIServer>().reportTestFailure(generifyErrorMessage(testName), messageText, stackTrace)
 +        }
 +
 +        Pair(messageFile, stacktraceFile)
 +      }
 +    }
 +    else listOf()
 +  }
 +}
index 18ef93f695bd59317d59c2238e09cc5b85b45e7d,0000000000000000000000000000000000000000..bffe0121b0c76ac622630d57848f80d2c7056007
mode 100644,000000..100644
--- /dev/null
@@@ -1,15 -1,0 +1,15 @@@
-       QodanaClient.report(TestContextToQodanaSarifMapper.map(ideStartResult))
 +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
 +package com.intellij.ide.starter.report.publisher.impl
 +
 +import com.intellij.ide.starter.models.IDEStartResult
 +import com.intellij.ide.starter.report.publisher.ReportPublisher
 +import com.intellij.ide.starter.report.qodana.QodanaClient
 +import com.intellij.ide.starter.report.sarif.TestContextToQodanaSarifMapper
 +
 +object QodanaTestResultPublisher : ReportPublisher {
 +
 +  override fun publish(ideStartResult: IDEStartResult) {
++    QodanaClient.report(TestContextToQodanaSarifMapper.map(ideStartResult))
 +  }
 +
 +}
index 39f6619c32f011c658eb96f8ad9bb11435edde43,0000000000000000000000000000000000000000..bfd4e3d12a35e034df2b31ece21eaab335d500aa
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,48 @@@
 +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
 +package com.intellij.ide.starter.report.sarif
 +
 +import com.jetbrains.qodana.sarif.model.*
 +
 +fun sarifReport(sarif: SarifReport, init: SarifReport.() -> Unit): SarifReport {
 +  sarif.init()
 +  return sarif
 +}
 +
 +fun sarifRun(run: Run, init: Run.() -> Unit): Run {
 +  run.init()
 +  return run
 +}
 +
 +fun driver(driver: ToolComponent, init: ToolComponent.() -> Unit): ToolComponent {
 +  driver.init()
 +  return driver
 +}
 +
 +fun taxa(init: ReportingDescriptor.() -> Unit): ReportingDescriptor {
 +  val taxa = ReportingDescriptor()
 +  taxa.init()
 +  return taxa
 +}
++
 +fun invocation(init: Invocation.() -> Unit): Invocation {
 +  val invocation = Invocation()
 +  invocation.init()
 +  return invocation
 +}
 +
 +fun result(init: Result.() -> Unit): Result {
 +  val result = Result()
 +  result.init()
 +  return result
 +}
 +
 +fun result(result: Result, init: Result.() -> Unit): Result {
 +  result.init()
 +  return result
 +}
 +
 +fun versionControlProvenance(init: VersionControlDetails.() -> Unit): VersionControlDetails {
 +  val versionControl = VersionControlDetails()
 +  versionControl.init()
 +  return versionControl
 +}
index 28a46e721eefccac36ead3b6baf513bf82632670,0000000000000000000000000000000000000000..864f82cb726aa998649822ad9f3bce43c2730066
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,42 @@@
-     if(defaultReportPath==null) throw RuntimeException("Default report doesn' exits")
 +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
 +package com.intellij.ide.starter.report.sarif
 +
 +import com.intellij.ide.starter.models.IDEStartResult
 +import com.jetbrains.qodana.sarif.SarifUtil
 +import com.jetbrains.qodana.sarif.model.SarifReport
 +import java.nio.file.Path
 +
 +object TestContextToQodanaSarifMapper {
 +
 +  fun map(ideStartResult: IDEStartResult): SarifReport {
 +
 +    val defaultReportPath = this::class.java.classLoader.getResource("sarif/qodana.sarif.json")?.path
++    if (defaultReportPath == null) throw RuntimeException("Default report doesn' exits")
 +    val sarifReport = SarifUtil.readReport(Path.of(defaultReportPath))
 +
 +    return sarifReport(sarifReport) {
 +      sarifRun(runs[0]) {
 +        driver(tool.driver) {
 +
 +          taxa.add(taxa {
 +            id = ideStartResult.context.testName
 +            name = ideStartResult.context.testName
 +          })
 +        }
 +
 +        invocations.add(invocation {
 +          exitCode = if (ideStartResult.failureError == null) 0 else 1
 +          executionSuccessful = ideStartResult.failureError == null
 +        })
 +
 +        versionControlProvenance.add(versionControlProvenance {
 +
 +        })
 +
 +        results.add(result {
 +          ruleId = "Perf test"
 +        })
 +      }
 +    }
 +  }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef974fb85938e2d78439e20592c01601fe2ee47b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++package com.intellij.ide.starter.runner
++
++import java.lang.reflect.Method
++import java.util.concurrent.atomic.AtomicReference
++
++/**
++ * Container, that pass around test method reference
++ */
++object CurrentTestMethod {
++  private lateinit var testMethod: AtomicReference<Method>
++
++  fun set(method: Method) {
++    if (this::testMethod.isInitialized) {
++      testMethod.set(method)
++    }
++    else {
++      testMethod = AtomicReference(method)
++    }
++  }
++
++  fun get(): Method? {
++    return if (this::testMethod.isInitialized) {
++      testMethod.get()
++    }
++    else null
++  }
++}
index ff2b25d3a8babdb6ec351859bc0bf5c9ac5a6db6,0000000000000000000000000000000000000000..6ad5a8ac10826ea8d8578a34d9efcc469dd529b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,329 -1,0 +1,346 @@@
- import com.intellij.ide.starter.exec.ExecOutputRedirect
- import com.intellij.ide.starter.exec.ExecTimeoutException
- import com.intellij.ide.starter.exec.exec
 +package com.intellij.ide.starter.runner
 +
 +import com.intellij.ide.starter.bus.EventState
 +import com.intellij.ide.starter.bus.StarterBus
 +import com.intellij.ide.starter.di.di
- import kotlin.concurrent.thread
- import kotlin.io.path.*
 +import com.intellij.ide.starter.ide.CodeInjector
 +import com.intellij.ide.starter.ide.IDETestContext
 +import com.intellij.ide.starter.ide.command.MarshallableCommand
 +import com.intellij.ide.starter.models.IDEStartResult
 +import com.intellij.ide.starter.models.VMOptions
 +import com.intellij.ide.starter.models.andThen
 +import com.intellij.ide.starter.process.collectJavaThreadDump
 +import com.intellij.ide.starter.process.destroyGradleDaemonProcessIfExists
++import com.intellij.ide.starter.process.exec.ExecOutputRedirect
++import com.intellij.ide.starter.process.exec.ExecTimeoutException
++import com.intellij.ide.starter.process.exec.ProcessExecutor
 +import com.intellij.ide.starter.process.getJavaProcessId
 +import com.intellij.ide.starter.profiler.ProfilerInjector
 +import com.intellij.ide.starter.profiler.ProfilerType
 +import com.intellij.ide.starter.report.ErrorReporter
 +import com.intellij.ide.starter.system.SystemInfo
 +import com.intellij.ide.starter.utils.*
++import kotlinx.coroutines.delay
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import java.io.Closeable
 +import java.io.File
 +import java.nio.file.Files
 +import java.nio.file.Path
 +import java.nio.file.Paths
-   val traceStacksEvery: Duration = 10.minutes,
++import kotlin.io.path.absolutePathString
++import kotlin.io.path.createDirectories
++import kotlin.io.path.div
++import kotlin.io.path.listDirectoryEntries
 +import kotlin.time.Duration
 +import kotlin.time.Duration.Companion.minutes
 +import kotlin.time.Duration.Companion.seconds
 +import kotlin.time.measureTime
 +
 +interface IDERunCloseContext {
 +  val wasRunSuccessful: Boolean
 +}
 +
 +data class IDERunContext(
 +  val testContext: IDETestContext,
 +  val patchVMOptions: VMOptions.() -> VMOptions = { this },
 +  val commandLine: IDECommandLine? = null,
 +  val commands: Iterable<MarshallableCommand> = listOf(),
 +  val codeBuilder: (CodeInjector.() -> Unit)? = null,
 +  val runTimeout: Duration = 10.minutes,
-   fun uploadProfileResultsToTeamCity(profilerSnapshotsDir: Path, artifactName: String) =
++  val dumpThreadInterval: Duration = 10.minutes,
 +  val useStartupScript: Boolean = true,
 +  val closeHandlers: List<IDERunCloseContext.() -> Unit> = listOf(),
 +  val verboseOutput: Boolean = false,
 +  val launchName: String = "",
 +  val expectedKill: Boolean = false,
 +  val collectNativeThreads: Boolean = false
 +) {
 +  val contextName: String
 +    get() = if (launchName.isNotEmpty()) {
 +      "${testContext.testName}/$launchName"
 +    }
 +    else {
 +      testContext.testName
 +    }
 +
 +  fun verbose() = copy(verboseOutput = true)
 +
 +  @Suppress("unused")
 +  fun withVMOptions(patchVMOptions: VMOptions.() -> VMOptions) = addVMOptionsPatch(patchVMOptions)
 +
 +  fun addVMOptionsPatch(patchVMOptions: VMOptions.() -> VMOptions) = copy(
 +    patchVMOptions = this.patchVMOptions.andThen(patchVMOptions)
 +  )
 +
 +  fun addCompletionHandler(handler: IDERunCloseContext.() -> Unit) = this.copy(closeHandlers = closeHandlers + handler)
 +
-   fun installProfiler(): IDERunContext {
++  fun uploadProfilerResultsToCIServer(profilerSnapshotsDir: Path, artifactName: String) =
 +    this.addCompletionHandler {
 +      testContext.publishArtifact(source = profilerSnapshotsDir, artifactName = artifactName)
 +    }
 +
-   // TODO: refactor this
++  private fun installProfiler(): IDERunContext {
 +    return when (val profilerType = testContext.profilerType) {
 +      ProfilerType.ASYNC, ProfilerType.YOURKIT -> {
 +        val profiler = di.direct.instance<ProfilerInjector>(tag = profilerType)
 +        logOutput("Injecting profiler ${profiler.type.kind}")
 +        profiler.injectProfiler(this)
 +      }
 +      ProfilerType.NONE -> {
 +        logOutput("No profiler is specified. Skipping profiler setup")
 +        return this
 +      }
 +    }
 +  }
 +
-     var successfulRun = true
++  // TODO: refactor this https://youtrack.jetbrains.com/issue/AT-18/Simplify-refactor-code-for-starting-IDE-in-IdeRunContext
 +  private fun prepareToRunIDE(): IDEStartResult {
 +    StarterBus.post(IdeLaunchEvent(EventState.BEFORE, this))
 +
 +    deleteSavedAppStateOnMac()
 +    val paths = testContext.paths
 +    val logsDir = paths.logsDir.createDirectories()
 +    paths.snapshotsDir.createDirectories()
 +    val jvmCrashLogDirectory = logsDir.resolve("jvm-crash").createDirectories()
 +    val heapDumpOnOomDirectory = logsDir.resolve("heap-dump").createDirectories()
 +    val disabledPlugins = paths.configDir.resolve("disabled_plugins.txt")
 +    if (disabledPlugins.toFile().exists()) {
 +      logOutput("The list of disabled plugins: " + disabledPlugins.toFile().readText())
 +    }
 +
 +    val stdout = if (verboseOutput) ExecOutputRedirect.ToStdOut("[ide-${contextName}-out]") else ExecOutputRedirect.ToString()
 +    val stderr = ExecOutputRedirect.ToStdOut("[ide-${contextName}-err]")
 +
-         exec(
-           presentablePurpose = "run-ide-$contextName",
++    var isRunSuccessful = true
 +    val host by lazy { di.direct.instance<CodeInjector>() }
 +
 +    try {
 +      val codeBuilder = codeBuilder
 +      if (codeBuilder != null) {
 +        host.codeBuilder()
 +      }
 +
 +      val finalOptions = testContext.ide.originalVMOptions
 +        .disableStartupDialogs()
 +        .usingStartupFramework()
 +        .setFatalErrorNotificationEnabled()
 +        .setFlagIntegrationTests()
 +        .takeScreenshotIfFailure(logsDir)
 +        .withJvmCrashLogDirectory(jvmCrashLogDirectory)
 +        .withHeapDumpOnOutOfMemoryDirectory(heapDumpOnOomDirectory)
 +        .let { testContext.testCase.vmOptionsFix(it) }
 +        .let { testContext.patchVMOptions(it) }
 +        .patchVMOptions()
 +        .let {
 +          if (!useStartupScript) {
 +            require(commands.count() > 0) { "script builder is not allowed when useStartupScript is disabled" }
 +            it
 +          }
 +          else
 +            it.installTestScript(contextName, paths, commands)
 +        }
 +
 +      if (codeBuilder != null) {
 +        host.setup(testContext)
 +      }
 +
 +      testContext.setProviderMemoryOnlyOnLinux()
 +
 +      val jdkHome: Path by lazy {
 +        try {
 +          testContext.ide.resolveAndDownloadTheSameJDK()
 +        }
 +        catch (e: Exception) {
 +          logError("Failed to download the same JDK as in ${testContext.ide.build}")
 +          logError(e.stackTraceToString())
 +
 +          val defaultJavaHome = resolveInstalledJdk11()
 +          logOutput("JDK is not found in ${testContext.ide.build}. Fallback to default java: $defaultJavaHome")
 +          defaultJavaHome
 +        }
 +      }
 +
 +      val startConfig = testContext.ide.startConfig(finalOptions, logsDir)
 +      if (startConfig is Closeable) {
 +        addCompletionHandler { startConfig.close() }
 +      }
 +
 +      val commandLineArgs = when (val cmd = commandLine ?: IDECommandLine.OpenTestCaseProject) {
 +        is IDECommandLine.Args -> cmd.args
 +        is IDECommandLine.OpenTestCaseProject -> listOf(testContext.resolvedProjectHome.toAbsolutePath().toString())
 +      }
 +
 +      val finalEnvVariables = startConfig.environmentVariables + finalOptions.env
 +      val extendedEnvVariablesWithJavaHome = finalEnvVariables.toMutableMap()
 +      extendedEnvVariablesWithJavaHome.putIfAbsent("JAVA_HOME", jdkHome.absolutePathString())
 +      val finalArgs = startConfig.commandLine + commandLineArgs
 +      logOutput(buildString {
 +        appendLine("Starting IDE for ${contextName} with timeout $runTimeout")
 +        appendLine("  Command line: [" + finalArgs.joinToString() + "]")
 +        appendLine("  VM Options: [" + finalOptions.toString().lineSequence().map { it.trim() }.joinToString(" ") + "]")
 +        appendLine("  On Java : [" + System.getProperty("java.home") + "]")
 +      })
 +
 +      File(finalArgs.first()).setExecutable(true)
 +
 +      val executionTime = measureTime {
-             thread(name = "jstack-${testContext.testName}", isDaemon = true) {
-               var cnt = 0
-               while (process.isAlive) {
-                 Thread.sleep(traceStacksEvery.inWholeMilliseconds)
-                 if (!process.isAlive) break
-                 val dumpFile = logsDir.resolve("threadDump-${++cnt}-${System.currentTimeMillis()}" + ".txt")
-                 logOutput("Dumping threads to $dumpFile")
-                 logOutput(Runtime.getRuntime().getRuntimeInfo())
-                 catchAll { collectJavaThreadDump(jdkHome, startConfig.workDir, javaProcessId, dumpFile, false) }
-               }
++        ProcessExecutor(
++          presentableName = "run-ide-$contextName",
 +          workDir = startConfig.workDir,
 +          environmentVariables = extendedEnvVariablesWithJavaHome,
 +          timeout = runTimeout,
 +          args = finalArgs,
 +          errorDiagnosticFiles = startConfig.errorDiagnosticFiles,
 +          stdoutRedirect = stdout,
 +          stderrRedirect = stderr,
 +          onProcessCreated = { process, pid ->
 +            val javaProcessId by lazy { getJavaProcessId(jdkHome, startConfig.workDir, pid, process) }
-                 Thread.sleep(15.seconds.inWholeMilliseconds)
++            val monitoringThreadDumpDir = logsDir.resolve("monitoring-thread-dumps").createDirectories()
++
++            var cnt = 0
++            while (process.isAlive) {
++              delay(dumpThreadInterval)
++              if (!process.isAlive) break
++
++              val dumpFile = monitoringThreadDumpDir.resolve("threadDump-${++cnt}-${System.currentTimeMillis()}" + ".txt")
++              logOutput("Dumping threads to $dumpFile")
++              catchAll { collectJavaThreadDump(jdkHome, startConfig.workDir, javaProcessId, dumpFile, false) }
 +            }
 +          },
 +          onBeforeKilled = { process, pid ->
 +            if (!expectedKill) {
 +              val javaProcessId by lazy { getJavaProcessId(jdkHome, startConfig.workDir, pid, process) }
 +              if (collectNativeThreads) {
 +                val fileToStoreNativeThreads = logsDir.resolve("native-thread-dumps.txt")
 +                startProfileNativeThreads(javaProcessId.toString())
-         )
++                delay(15.seconds)
 +                stopProfileNativeThreads(javaProcessId.toString(), fileToStoreNativeThreads.toAbsolutePath().toString())
 +              }
 +              val dumpFile = logsDir.resolve("threadDump-before-kill-${System.currentTimeMillis()}" + ".txt")
 +              catchAll { collectJavaThreadDump(jdkHome, startConfig.workDir, javaProcessId, dumpFile) }
 +            }
 +            takeScreenshot(logsDir)
 +          }
-       successfulRun = false
++        ).start()
 +      }
 +
 +      logOutput("IDE run $contextName completed in $executionTime")
 +
 +      require(FileSystem.countFiles(paths.configDir) > 3) {
 +        "IDE must have created files under config directory at ${paths.configDir}. Were .vmoptions included correctly?"
 +      }
 +
 +      require(FileSystem.countFiles(paths.systemDir) > 1) {
 +        "IDE must have created files under system directory at ${paths.systemDir}. Were .vmoptions included correctly?"
 +      }
 +
 +      val vmOptionsDiff = startConfig.vmOptionsDiff()
 +
 +      if (vmOptionsDiff != null && !vmOptionsDiff.isEmpty) {
 +        logOutput("VMOptions were changed:")
 +        logOutput("new lines:")
 +        vmOptionsDiff.newLines.forEach { logOutput("  $it") }
 +        logOutput("removed lines:")
 +        vmOptionsDiff.missingLines.forEach { logOutput("  $it") }
 +        logOutput()
 +      }
 +
 +      return IDEStartResult(runContext = this, executionTime = executionTime, vmOptionsDiff = vmOptionsDiff, logsDir = logsDir)
 +    }
 +    catch (t: Throwable) {
-         val (artifactPath, artifactName) = if (successfulRun) contextName to "logs" else "run/$contextName" to "crash"
-         testContext.publishArtifact(logsDir, artifactPath, formatArtifactName(artifactName, testContext.testName))
++      isRunSuccessful = false
 +      if (t is ExecTimeoutException && !expectedKill) {
 +        error("Timeout of IDE run $contextName for $runTimeout")
 +      }
 +      else {
 +        logOutput("IDE run for $contextName has been expected to be killed after $runTimeout")
 +      }
 +
 +      val failureCauseFile = testContext.paths.logsDir.resolve("failure_cause.txt")
 +      val errorMessage = if (Files.exists(failureCauseFile)) {
 +        Files.readString(failureCauseFile)
 +      }
 +      else {
 +        t.message ?: t.javaClass.name
 +      }
 +      if (!expectedKill) {
 +        throw Exception(errorMessage, t)
 +      }
 +      else {
 +        return IDEStartResult(runContext = this, executionTime = runTimeout, logsDir = logsDir)
 +      }
 +    }
 +    finally {
 +
 +      try {
 +        if (SystemInfo.isWindows) {
 +          destroyGradleDaemonProcessIfExists()
 +        }
 +
 +        listOf(heapDumpOnOomDirectory, jvmCrashLogDirectory).filter { dir ->
 +          dir.listDirectoryEntries().isEmpty()
 +        }.forEach { it.toFile().deleteRecursively() }
 +
 +        ErrorReporter.reportErrorsAsFailedTests(logsDir / "script-errors", contextName)
-           override val wasRunSuccessful: Boolean = successfulRun
++        publishArtifacts(isRunSuccessful)
++
 +        if (codeBuilder != null) {
 +          host.tearDown(testContext)
 +        }
 +
 +        val closeContext = object : IDERunCloseContext {
-     return installProfiler()
-       .prepareToRunIDE()
++          override val wasRunSuccessful: Boolean = isRunSuccessful
 +        }
 +
 +        closeHandlers.forEach {
 +          try {
 +            it.invoke(closeContext)
 +          }
 +          catch (t: Throwable) {
 +            logOutput("Failed to complete close step. ${t.message}.\n" + t)
 +            t.printStackTrace(System.err)
 +          }
 +        }
 +      }
 +      finally {
 +        StarterBus.post(IdeLaunchEvent(EventState.AFTER, this))
 +      }
 +    }
 +  }
 +
++  private fun publishArtifacts(isRunSuccessful: Boolean) {
++    // publish artifacts to directory with a test in any case
++    testContext.publishArtifact(
++      source = testContext.paths.logsDir,
++      artifactPath = contextName,
++      artifactName = formatArtifactName("logs", testContext.testName)
++    )
++
++    if (!isRunSuccessful)
++      testContext.publishArtifact(
++        source = testContext.paths.logsDir,
++        artifactPath = "_crashes/$contextName",
++        artifactName = formatArtifactName("crash", testContext.testName)
++      )
++  }
++
 +  fun runIDE(): IDEStartResult {
++    return installProfiler().prepareToRunIDE()
 +  }
 +
 +  private fun deleteSavedAppStateOnMac() {
 +    if (SystemInfo.isMac) {
 +      val filesToBeDeleted = listOf(
 +        "com.jetbrains.${testContext.testCase.ideInfo.installerProductName}-EAP.savedState",
 +        "com.jetbrains.${testContext.testCase.ideInfo.installerProductName}.savedState"
 +      )
 +      val home = System.getProperty("user.home")
 +      val savedAppStateDir = Paths.get(home).resolve("Library").resolve("Saved Application State")
 +      savedAppStateDir.toFile()
 +        .walkTopDown().maxDepth(1)
 +        .filter { file -> filesToBeDeleted.any { fileToBeDeleted -> file.name == fileToBeDeleted } }
 +        .forEach { it.deleteRecursively() }
 +    }
 +  }
 +}
index 055c0b82f5f0534d318464d05054add7eeee546b,0000000000000000000000000000000000000000..f087228f2ffadc1c820c1517e34c847ca392f4dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,112 @@@
-   val allContexts: MutableList<IDETestContext>
 +package com.intellij.ide.starter.runner
 +
++import com.intellij.ide.starter.bus.EventState
++import com.intellij.ide.starter.bus.StarterBus
 +import com.intellij.ide.starter.ci.CIServer
 +import com.intellij.ide.starter.di.di
 +import com.intellij.ide.starter.ide.*
 +import com.intellij.ide.starter.models.IdeInfo
 +import com.intellij.ide.starter.models.TestCase
 +import com.intellij.ide.starter.path.GlobalPaths
 +import com.intellij.ide.starter.path.IDEDataPaths
 +import com.intellij.ide.starter.plugins.PluginInstalledState
 +import com.intellij.ide.starter.utils.catchAll
 +import com.intellij.ide.starter.utils.logOutput
 +import org.kodein.di.direct
 +import org.kodein.di.factory
 +import org.kodein.di.instance
 +import java.io.Closeable
 +import kotlin.io.path.div
 +
 +/**
 + * [ciServer] - use [NoCIServer] for only local run. Otherwise - pass implementation of CIServer
 + */
 +interface TestContainer<T> : Closeable {
 +  val ciServer: CIServer
 +  var useLatestDownloadedIdeBuild: Boolean
-     for (context in allContexts) {
-       catchAll { context.paths.close() }
-     }
++  var testContext: IDETestContext
 +  val setupHooks: MutableList<IDETestContext.() -> IDETestContext>
 +
 +  override fun close() {
-   fun initializeTestRunner(testName: String, testCase: TestCase): IDETestContext {
-     check(allContexts.none { it.testName == testName }) { "Test $testName is already initialized. Use another name." }
++    catchAll { testContext.paths.close() }
 +
 +    logOutput("TestContainer $this disposed")
 +  }
 +
 +  /**
 +   * Allows to apply the common configuration to all created IDETestContext instances
 +   */
 +  fun withSetupHook(hook: IDETestContext.() -> IDETestContext): T = apply {
 +    setupHooks += hook
 +  } as T
 +
 +  /**
 +   * Makes the test use the latest available locally IDE build for testing.
 +   */
 +  fun useLatestDownloadedIdeBuild(): T = apply {
 +    assert(!ciServer.isBuildRunningOnCI)
 +    useLatestDownloadedIdeBuild = true
 +  } as T
 +
++  /**
++   * @return <Build Number, InstalledIde>
++   */
 +  fun resolveIDE(ideInfo: IdeInfo): Pair<String, InstalledIde> {
 +    return di.direct.factory<IdeInfo, IdeInstallator>().invoke(ideInfo).install(ideInfo)
 +  }
 +
 +  fun installPerformanceTestingPluginIfMissing(context: IDETestContext) {
 +    val performancePluginId = "com.jetbrains.performancePlugin"
 +
 +    context.pluginConfigurator.apply {
 +      val pluginState = getPluginInstalledState(performancePluginId)
 +      if (pluginState != PluginInstalledState.INSTALLED && pluginState != PluginInstalledState.BUNDLED_TO_IDE)
 +        setupPluginFromPluginManager(performancePluginId, ide = context.ide)
 +    }
 +  }
 +
 +  /** Starting point to run your test */
++  fun initializeTestContext(testName: String, testCase: TestCase): IDETestContext {
 +    logOutput("Resolving IDE build for $testName...")
-     val context = IDETestContext(paths, ide, testCase, testName, projectHome, patchVMOptions = { this }, ciServer = ciServer)
-     allContexts += context
 +    val (buildNumber, ide) = resolveIDE(testCase.ideInfo)
 +
 +    require(ide.productCode == testCase.ideInfo.productCode) { "Product code of $ide must be the same as for $testCase" }
 +
 +    val testDirectory = (di.direct.instance<GlobalPaths>().testsDirectory / "${testCase.ideInfo.productCode}-$buildNumber") / testName
 +
 +    val paths = IDEDataPaths.createPaths(testName, testDirectory, testCase.useInMemoryFileSystem)
 +    logOutput("Using IDE paths for $testName: $paths")
 +    logOutput("IDE to run for $testName: $ide")
 +
 +    val projectHome = testCase.projectInfo?.downloadAndUnpackProject()
-     val baseContext = when (testCase.ideInfo == IdeProductProvider.AI) {
-       true -> context
++    testContext = IDETestContext(paths, ide, testCase, testName, projectHome, patchVMOptions = { this }, ciServer = ciServer)
 +
-       false -> context
++    testContext = when (testCase.ideInfo == IdeProductProvider.AI) {
++      true -> testContext
 +        .addVMOptionsPatch {
 +          overrideDirectories(paths)
 +            .withEnv("STUDIO_VM_OPTIONS", ide.patchedVMOptionsFile.toString())
 +        }
-     return setupHooks
-       .fold(baseContext.updateGeneralSettings()) { acc, hook -> acc.hook() }
++      false -> testContext
 +        .disableInstantIdeShutdown()
 +        .disableFusSendingOnIdeClose()
 +        .disableJcef()
 +        .disableLinuxNativeMenuForce()
 +        .withGtk2OnLinux()
 +        .disableGitLogIndexing()
 +        .enableSlowOperationsInEdtInTests()
 +        .collectOpenTelemetry()
 +        .addVMOptionsPatch {
 +          overrideDirectories(paths)
 +        }
 +    }
 +
++    val contextWithAppliedHooks = setupHooks
++      .fold(testContext.updateGeneralSettings()) { acc, hook -> acc.hook() }
 +      .apply { installPerformanceTestingPluginIfMissing(this) }
++
++    StarterBus.post(TestContextInitializedEvent(EventState.AFTER, contextWithAppliedHooks))
++
++    return contextWithAppliedHooks
 +  }
 +}
index 63bc87873398750c6dd1458c2f162e2ba9969a4b,0000000000000000000000000000000000000000..15ef6ea0f201bc260db3a8a39861ff635bcdda3e
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,15 @@@
-   override val allContexts: MutableList<IDETestContext> = mutableListOf(),
 +package com.intellij.ide.starter.runner
 +
 +import com.intellij.ide.starter.ci.CIServer
 +import com.intellij.ide.starter.di.di
 +import com.intellij.ide.starter.ide.IDETestContext
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +
 +class TestContainerImpl(
 +  override val ciServer: CIServer = di.direct.instance(),
 +  override var useLatestDownloadedIdeBuild: Boolean = false,
- ) : TestContainer<TestContainerImpl>
 +  override val setupHooks: MutableList<IDETestContext.() -> IDETestContext> = mutableListOf()
++) : TestContainer<TestContainerImpl> {
++  override lateinit var testContext: IDETestContext
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..405040e7e4f4717bf9743b72f1f08343ab3e57c0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++package com.intellij.ide.starter.runner
++
++import com.intellij.ide.starter.bus.Event
++import com.intellij.ide.starter.bus.EventState
++import com.intellij.ide.starter.ide.IDETestContext
++
++class TestContextInitializedEvent(state: EventState, testContext: IDETestContext) : Event<IDETestContext>(state, testContext)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4465721174ae9f6cd6c2d41ecbd126c14081c5b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++package com.intellij.ide.starter.runner
++
++import com.intellij.ide.starter.ide.IDETestContext
++import com.intellij.ide.starter.utils.catchAll
++import com.intellij.ide.starter.utils.logOutput
++import com.intellij.util.io.exists
++import java.util.*
++
++class TestWatcherActions {
++  private val _onFailureActions: MutableList<(IDETestContext) -> Unit> = Collections.synchronizedList(mutableListOf())
++  val onFailureActions: List<(IDETestContext) -> Unit>
++    get() = synchronized(_onFailureActions) { _onFailureActions.toList() }
++
++  private val _onFinishedActions: MutableList<(IDETestContext) -> Unit> = Collections.synchronizedList(mutableListOf())
++  val onFinishedActions: List<(IDETestContext) -> Unit>
++    get() = synchronized(_onFinishedActions) { _onFinishedActions.toList() }
++
++  companion object {
++    /** Archive and add to test artifact entire ide's `system` dir */
++    fun getSystemDirAsArtifactAction(): (IDETestContext) -> Unit = { testContext ->
++      catchAll {
++        logOutput("Archive with system directory created and will be published to artifacts")
++        testContext.publishArtifact(source = testContext.paths.systemDir, artifactName = "testSystemDirSnapshot.zip")
++
++        val ideaDirPath = testContext.resolvedProjectHome.resolve(".idea")
++
++        if (ideaDirPath.exists()) {
++          logOutput("Archive with .idea dir created and will be published to artifacts")
++          testContext.publishArtifact(source = ideaDirPath, artifactName = ".idea.zip")
++        }
++      }
++    }
++  }
++
++  fun addOnFailureAction(action: (IDETestContext) -> Unit): TestWatcherActions {
++    synchronized(_onFailureActions) { _onFailureActions.add(action) }
++    return this
++  }
++
++  fun addOnFinishedAction(action: (IDETestContext) -> Unit): TestWatcherActions {
++    synchronized(_onFinishedActions) { _onFinishedActions.add(action) }
++    return this
++  }
++}
index 1b535d5948d50e9290f5011a7072e497e1ea166a,0000000000000000000000000000000000000000..2af632579d2cfbb0a9d53bc9f04e4b065383610e
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,122 @@@
-     val targetJdkHome = when (predicate) {
-       JdkPredicate.forWSL() -> {
-         if (SystemInfo.isWindows && WslDistributionManager.getInstance().installedDistributions.isNotEmpty()) {
-           val wslDistribution = WslDistributionManager.getInstance().installedDistributions[0]
-           Path.of(wslDistribution.getWindowsPath("/tmp/jdks/${jdk.installFolderName}"))
-         }
-         else {
-           throw WslDistributionNotFoundException()
-         }
 +package com.intellij.ide.starter.sdk
 +
 +import com.intellij.execution.wsl.WslDistributionManager
 +import com.intellij.ide.starter.di.di
 +import com.intellij.ide.starter.path.GlobalPaths
 +import com.intellij.ide.starter.system.SystemInfo
 +import com.intellij.ide.starter.utils.logOutput
 +import com.intellij.ide.starter.utils.withRetry
 +import com.intellij.ide.starter.wsl.WslDistributionNotFoundException
 +import com.intellij.openapi.projectRoots.impl.jdkDownloader.JdkInstaller
 +import com.intellij.openapi.projectRoots.impl.jdkDownloader.JdkItem
 +import com.intellij.openapi.projectRoots.impl.jdkDownloader.JdkListDownloader
 +import com.intellij.openapi.projectRoots.impl.jdkDownloader.JdkPredicate
 +import com.intellij.openapi.util.io.FileUtil
 +import com.intellij.util.io.delete
 +import com.intellij.util.io.isDirectory
 +import com.intellij.util.io.readText
 +import com.intellij.util.io.write
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import java.io.File
 +import java.nio.file.Files
 +import java.nio.file.Path
 +import kotlin.io.path.isRegularFile
 +
 +/**
 + * NOTE: this class uses IntelliJ IDEA components, please make sure
 + * you call it from a proper test-case, e.g. [com.intellij.testFramework.LightPlatformTestCase]
 + */
 +object JdkDownloaderFacade {
 +
 +  val jdk8 get() = jdkDownloader(JdkVersion.JDK_8.toString())
 +  val jdk11 get() = jdkDownloader(JdkVersion.JDK_11.toString())
 +  val jdk15 get() = jdkDownloader(JdkVersion.JDK_15.toString())
 +  val jdk17 get() = jdkDownloader(JdkVersion.JDK_17.toString())
 +
 +  fun jdkDownloader(
 +    version: String,
 +    jdks: Iterable<JdkDownloadItem> = allJdks,
 +  ): JdkDownloadItem {
 +    val jdkName = when (SystemInfo.OS_ARCH) {
 +      "aarch64" -> "azul"
 +      else -> "corretto"
 +    }
 +
 +    return jdks.single {
 +      it.jdk.sharedIndexAliases.contains("$jdkName-$version")
 +    }
 +  }
 +
 +  val allJdks by lazy {
 +    listJDKs(JdkPredicate.forCurrentProcess())
 +  }
 +
 +  val allJdksForWSL by lazy {
 +    listJDKs(JdkPredicate.forWSL())
 +  }
 +
 +  private fun listJDKs(predicate: JdkPredicate): List<JdkDownloadItem> {
 +    val allJDKs = JdkListDownloader.getInstance().downloadModelForJdkInstaller(null, predicate)
 +    logOutput("Total JDKs: ${allJDKs.map { it.fullPresentationText }}")
 +
 +    val allVersions = allJDKs.map { it.jdkVersion }.toSortedSet()
 +    logOutput("JDK versions: $allVersions")
 +
 +    return allJDKs
 +      .asSequence()
 +      .map { jdk ->
 +        JdkDownloadItem(jdk) {
 +          downloadJdkItem(jdk, predicate)
 +        }
 +      }.toList()
 +  }
 +
 +  private fun downloadJdkItem(jdk: JdkItem, predicate: JdkPredicate): JdkItemPaths {
-       else -> di.direct.instance<GlobalPaths>().getCacheDirectoryFor("jdks").resolve(jdk.installFolderName)
++    val targetJdkHome: Path
++
++    // hack for wsl on windows
++    if (predicate == JdkPredicate.forWSL() && SystemInfo.isWindows && WslDistributionManager.getInstance().installedDistributions.isNotEmpty()) {
++      try {
++        val wslDistribution = WslDistributionManager.getInstance().installedDistributions[0]
++        targetJdkHome = Path.of(wslDistribution.getWindowsPath("/tmp/jdks/${jdk.installFolderName}"))
 +      }
++      catch (_: Exception) {
++        throw WslDistributionNotFoundException(predicate)
++      }
++    }
++    else {
++      targetJdkHome = di.direct.instance<GlobalPaths>().getCacheDirectoryFor("jdks").resolve(jdk.installFolderName)
 +    }
++
 +    val targetHomeMarker = targetJdkHome.resolve("home.link")
 +    logOutput("Checking JDK at $targetJdkHome")
 +
 +    if (!targetHomeMarker.isRegularFile() || !targetJdkHome.isDirectory() || (runCatching {
 +        Files.walk(targetJdkHome).count()
 +      }.getOrNull() ?: 0) < 42) {
 +
 +      withRetry(retries = 5) {
 +        logOutput("Downloading JDK at $targetJdkHome")
 +        targetJdkHome.delete(true)
 +
 +        val jdkInstaller = JdkInstaller.getInstance()
 +        val request = jdkInstaller.prepareJdkInstallationDirect(jdk, targetPath = targetJdkHome)
 +        jdkInstaller.installJdk(request, null, null)
 +        targetHomeMarker.write(request.javaHome.toRealPath().toString())
 +      }
 +    }
 +    val javaHome = File(targetHomeMarker.readText())
 +    val binJava = "bin/java" + when {
 +      (SystemInfo.isWindows && predicate != JdkPredicate.forWSL()) -> ".exe"
 +      else -> ""
 +    }
 +
 +    require(javaHome.resolve(binJava).isFile) {
 +      FileUtil.delete(targetJdkHome)
 +      "corrupted JDK home: $targetJdkHome (now deleted)"
 +    }
 +
 +    return JdkItemPaths(homePath = javaHome.toPath(), installPath = targetJdkHome)
 +  }
 +}
index 088fe93798f25700623abe1fee042f614795be02,0000000000000000000000000000000000000000..245e84ec7790d409bde2295f180d7b143994fc78
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,37 @@@
 +package com.intellij.ide.starter.system
 +
 +import java.util.*
 +
 +object SystemInfo {
 +  val OS_NAME: String = System.getProperty("os.name")
 +  val OS_VERSION = System.getProperty("os.version").lowercase()
 +  val OS_ARCH: String = System.getProperty("os.arch")
 +  val JAVA_VERSION: String = System.getProperty("java.version")
 +  val JAVA_RUNTIME_VERSION = getRtVersion(JAVA_VERSION)
 +  val JAVA_VENDOR: String = System.getProperty("java.vm.vendor", "Unknown")
 +
 +  private val OS_NAME_LOWERCASED = OS_NAME.lowercase(Locale.ENGLISH)
 +  val isWindows = OS_NAME_LOWERCASED.startsWith("windows")
 +  val isMac = OS_NAME_LOWERCASED.startsWith("mac")
 +  val isLinux = OS_NAME_LOWERCASED.startsWith("linux")
 +  val isFreeBSD = OS_NAME_LOWERCASED.startsWith("freebsd")
 +  val isSolaris = OS_NAME_LOWERCASED.startsWith("sunos")
 +  val isUnix = !isWindows
 +  val isXWindow = isUnix && !isMac
 +
++  val isAarch64: Boolean = OS_ARCH == "aarch64"
++
 +  private fun getRtVersion(fallback: String): String? {
 +    val rtVersion = System.getProperty("java.runtime.version")
 +    return if (Character.isDigit(rtVersion[0])) rtVersion else fallback
 +  }
 +
 +  fun getOsNameAndVersion(): String = (if (isMac) "macOS" else OS_NAME) + ' ' + OS_VERSION
 +
 +  fun getOsType(): OsType = when {
 +    isMac -> OsType.MacOS
 +    isWindows -> OsType.Windows
 +    isLinux -> OsType.Linux
 +    else -> OsType.Other
 +  }
 +}
index 01b40ba4c8e6b8947c5db1f880587719e29fca9a,0000000000000000000000000000000000000000..92089e05b45472d21748fd7386a93c010933b5fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,251 -1,0 +1,251 @@@
- import com.intellij.ide.starter.exec.ExecOutputRedirect
- import com.intellij.ide.starter.exec.exec
 +package com.intellij.ide.starter.utils
 +
 +import com.intellij.ide.starter.di.di
-         exec(
-           presentablePurpose = "extract-tar",
 +import com.intellij.ide.starter.path.GlobalPaths
++import com.intellij.ide.starter.process.exec.ExecOutputRedirect
++import com.intellij.ide.starter.process.exec.ProcessExecutor
 +import com.intellij.ide.starter.system.SystemInfo
 +import org.kodein.di.instance
 +import org.rauschig.jarchivelib.ArchiveFormat
 +import org.rauschig.jarchivelib.ArchiverFactory
 +import org.rauschig.jarchivelib.CompressionType
 +import java.io.File
 +import java.io.IOException
 +import java.io.OutputStream
 +import java.nio.file.Files
 +import java.nio.file.Path
 +import java.util.zip.GZIPOutputStream
 +import java.util.zip.ZipFile
 +import kotlin.io.path.*
 +import kotlin.time.Duration.Companion.minutes
 +
 +object FileSystem {
 +  fun String.cleanPathFromSlashes(replaceWith: String = ""): String = this
 +    .replace("\"", replaceWith)
 +    .replace("/", replaceWith)
 +
 +  fun validatePath(path: Path, additionalString: String = "") {
 +    if (SystemInfo.isWindows) {
 +      val pathToValidate = when (additionalString.isNotEmpty()) {
 +        true -> path.resolve(additionalString).toString()
 +        false -> path.toString()
 +      }
 +      check(pathToValidate.length < 260) {
 +        "$pathToValidate >= 260 symbols on Windows may lead to unexpected problems"
 +      }
 +    }
 +  }
 +
 +  fun countFiles(path: Path) = Files.walk(path).use { it.count() }
 +
 +  fun compressToZip(sourceToCompress: Path, outputArchive: Path) {
 +    if (sourceToCompress.extension == "zip") {
 +      logOutput("Looks like $sourceToCompress already compressed to zip file")
 +      return
 +    }
 +
 +    if (outputArchive.exists())
 +      outputArchive.toFile().deleteRecursively()
 +
 +    val outputArchiveParentDir = outputArchive.parent.apply { createDirectories() }
 +
 +    val archiver = ArchiverFactory.createArchiver(ArchiveFormat.ZIP)
 +    archiver.create(outputArchive.nameWithoutExtension, outputArchiveParentDir.toFile(), sourceToCompress.toFile())
 +  }
 +
 +  fun unpackZip(zipFile: Path, targetDir: Path, map: (name: String) -> String? = { it }) {
 +    try {
 +      targetDir.createDirectories()
 +
 +      ZipFile(zipFile.toFile()).use { zip ->
 +        for (entry in zip.entries()) {
 +          if (entry.isDirectory) {
 +            targetDir.resolve(entry.name).toFile().mkdirs()
 +            continue
 +          }
 +          val file = targetDir.resolve((map(entry.name) ?: continue))
 +          file.parent.createDirectories()
 +          file.outputStream().use { zip.getInputStream(entry).use { entryStream -> entryStream.copyTo(it) } }
 +          file.toFile().setLastModified(entry.lastModifiedTime.toMillis())
 +        }
 +      }
 +    }
 +    catch (e: Throwable) {
 +      targetDir.toFile().deleteRecursively()
 +      zipFile.deleteIfExists()
 +      throw IOException("Failed to unpack $zipFile to $targetDir. ${e.message}", e)
 +    }
 +  }
 +
 +  fun unpackIfMissing(archive: Path, targetDir: Path) {
 +    if (Files.isDirectory(targetDir) && Files.newDirectoryStream(targetDir).use { it.iterator().hasNext() }) {
 +      return
 +    }
 +
 +    unpack(archive, targetDir)
 +  }
 +
 +  fun unpack(archive: Path, targetDir: Path) {
 +    logOutput("Extracting $archive to $targetDir")
 +    //project archive may be empty
 +    Files.createDirectories(targetDir)
 +    val name = archive.fileName.toString()
 +
 +    try {
 +      when {
 +        name.endsWith(".zip") || name.endsWith(".ijx") -> unpackZip(archive, targetDir)
 +        name.endsWith(".tar.gz") -> unpackTarGz(archive, targetDir)
 +        else -> error("Archive $name is not supported")
 +      }
 +    }
 +    catch (e: IOException) {
 +      if (e.message?.contains("No space left on device") == true) {
 +        val paths by di.instance<GlobalPaths>()
 +        paths.getDiskUsageDiagnostics()
 +
 +        throw IOException(buildString {
 +          appendLine("No space left while extracting $archive to $targetDir")
 +          appendLine(Files.getFileStore(targetDir).getDiskInfo())
 +          appendLine(paths.getDiskUsageDiagnostics())
 +        })
 +      }
 +
 +      throw e
 +    }
 +  }
 +
 +  fun unpackTarGz(tarFile: File, targetDir: File) {
 +    targetDir.deleteRecursively()
 +    unpackTarGz(tarFile.toPath(), targetDir.toPath())
 +  }
 +
 +  fun unpackTarGz(tarFile: Path, targetDir: Path) {
 +    require(tarFile.fileName.toString().endsWith(".tar.gz")) { "File $tarFile must be tar.gz archive" }
 +
 +    try {
 +      if (SystemInfo.isWindows) {
 +        val archiver = ArchiverFactory.createArchiver(ArchiveFormat.TAR, CompressionType.GZIP)
 +        archiver.extract(tarFile.toFile(), targetDir.toFile())
 +      }
 +      else if (SystemInfo.isLinux || SystemInfo.isMac) {
 +        Files.createDirectories(targetDir)
-         )
++        ProcessExecutor(
++          presentableName = "extract-tar",
 +          workDir = targetDir,
 +          timeout = 10.minutes,
 +          stderrRedirect = ExecOutputRedirect.ToStdOut("tar"),
 +          args = listOf("tar", "-z", "-x", "-f", tarFile.toAbsolutePath().toString(), "-C", targetDir.toAbsolutePath().toString())
++        ).start()
 +      }
 +    }
 +    catch (e: Exception) {
 +      targetDir.toFile().deleteRecursively()
 +      tarFile.deleteIfExists()
 +      throw Exception("Failed to unpack $tarFile. ${e.message}. File and unpack targets are removed.", e)
 +    }
 +  }
 +
 +  fun Path.zippedLength(): Long {
 +    if (!isRegularFile()) {
 +      return 0
 +    }
 +
 +    val output = object : OutputStream() {
 +      var count = 0L
 +      override fun write(b: Int) {
 +        count++
 +      }
 +
 +      override fun write(b: ByteArray) {
 +        count += b.size
 +      }
 +
 +      override fun write(b: ByteArray, off: Int, len: Int) {
 +        count += len
 +      }
 +    }
 +
 +    GZIPOutputStream(output).use { zipStream ->
 +      this.inputStream().use { it.copyTo(zipStream, 1024 * 1024) }
 +    }
 +
 +    return output.count
 +  }
 +
 +  fun Path.getFileOrDirectoryPresentableSize(): String {
 +    val size: Long
 +    if (this.toFile().isFile) {
 +      size = this.toFile().length()
 +    }
 +    else {
 +      size = Files.walk(this).mapToLong { p: Path ->
 +        if (p.toFile().isFile) {
 +          p.toFile().length()
 +        }
 +        else 0
 +      }.sum()
 +    }
 +    return size.formatSize()
 +  }
 +
 +  fun Path.getDirectoryTreePresentableSizes(depth: Int = 1): String {
 +    val thisPath = this
 +    return buildString {
 +      Files.walk(thisPath, depth).use { dirStream ->
 +        dirStream.forEach { child ->
 +          if (child == thisPath) {
 +            appendLine("Total size: ${thisPath.getFileOrDirectoryPresentableSize()}")
 +          }
 +          else {
 +            val indent = "  ".repeat(thisPath.relativize(child).nameCount)
 +            appendLine("$indent${thisPath.relativize(child)}: " + child.getFileOrDirectoryPresentableSize())
 +          }
 +        }
 +      }
 +    }
 +  }
 +
 +  fun Path.isFileUpToDate(): Boolean {
 +    if (!this.isRegularFile()) {
 +      logOutput("File $this does not exist")
 +      return false
 +    }
 +    if (this.fileSize() <= 0) {
 +      logOutput("File $this is empty")
 +      return false
 +    }
 +    else {
 +      return this.isUpToDate()
 +    }
 +  }
 +
 +  fun Path.isDirUpToDate(): Boolean {
 +    if (!this.isDirectory()) {
 +      logOutput("Path $this does not exist")
 +      return false
 +    }
 +
 +    if (this.fileSize() <= 0) {
 +      logOutput("Project dir $this is empty")
 +      return false
 +    }
 +    else {
 +      return this.isUpToDate()
 +    }
 +  }
 +
 +  fun Path.isUpToDate(): Boolean {
 +    val lastModified = this.toFile().lastModified()
 +    val currentTime = System.currentTimeMillis()
 +
 +    // less then a day ago
 +    val upToDate = currentTime - lastModified < 24 * 60 * 60 * 1000
 +    if (upToDate) {
 +      logOutput("$this is up to date")
 +    }
 +    else {
 +      logOutput("$this is not up to date")
 +    }
 +    return upToDate
 +  }
 +}
index de0978122c641c5aa14149ae0ca1d12ded91b7a9,0000000000000000000000000000000000000000..3030d0729c0c85d70a53c27bb78e980b2d6829ec
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,65 @@@
- import com.intellij.ide.starter.exec.ExecOutputRedirect
- import com.intellij.ide.starter.exec.exec
 +package com.intellij.ide.starter.utils
 +
-     exec(
++import com.intellij.ide.starter.process.exec.ExecOutputRedirect
++import com.intellij.ide.starter.process.exec.ProcessExecutor
 +import java.io.IOException
 +import java.nio.file.Path
 +import java.nio.file.Paths
 +import kotlin.io.path.Path
 +import kotlin.time.Duration.Companion.minutes
 +
 +object Git {
 +  val branch by lazy { getShortBranchName() }
 +  val localBranch by lazy { getLocalGitBranch() }
 +
 +  @Throws(IOException::class, InterruptedException::class)
 +  private fun getLocalGitBranch(): String {
 +    val stdout = ExecOutputRedirect.ToString()
-     )
++
++    ProcessExecutor(
 +      "git-local-branch-get",
 +      workDir = null, timeout = 1.minutes,
 +      args = listOf("git", "rev-parse", "--abbrev-ref", "HEAD"),
 +      stdoutRedirect = stdout
-       exec(
++    ).start()
++
 +    return stdout.read().trim()
 +  }
 +
 +  private fun getShortBranchName(): String {
 +    val master = "master"
 +    return runCatching {
 +      when (val branch = getLocalGitBranch().substringBefore(".")) {
 +        master -> return branch
 +        else -> when (branch.toIntOrNull()) {
 +          null -> return master
 +          else -> return "IjPlatform$branch"
 +        }
 +      }
 +    }.getOrElse { master }
 +  }
 +
 +  fun getRepoRoot(): Path {
 +    val stdout = ExecOutputRedirect.ToString()
 +
 +    try {
-       )
++      ProcessExecutor(
 +        "git-repo-root-get",
 +        workDir = null, timeout = 1.minutes,
 +        args = listOf("git", "rev-parse", "--show-toplevel", "HEAD"),
 +        stdoutRedirect = stdout
++      ).start()
 +    }
 +    catch (e: Exception) {
 +      val workDir = Paths.get("").toAbsolutePath()
 +      logError("There is a problem in detecting git repo root. Trying to acquire working dir path: '$workDir'")
 +      return workDir
 +    }
 +
 +    // Takes first line from output like this:
 +    // /opt/REPO/intellij
 +    // 1916dc2bef46b51cfb02ad9f7e87d12aa1aa9fdc
 +    return Path(stdout.read().split(System.lineSeparator()).first().trim()).toAbsolutePath()
 +  }
 +}
 +
index e8703ddbcfc145da95763cd91a1f7f878141402a,0000000000000000000000000000000000000000..fbee9d5ae2f19a938fbf324afcf9e9b4f6336c7f
mode 100644,000000..100644
--- /dev/null
@@@ -1,37 -1,0 +1,38 @@@
 +package com.intellij.ide.starter.utils
 +
 +import java.util.*
 +import kotlin.io.path.Path
 +import kotlin.io.path.name
 +
 +
 +/**
 + * Format: testMethodName => test-method-name
 + */
 +fun String.hyphenateTestName(): String {
 +
 +  fun hyphenateString(input: String) = input
++    .replace(Regex("( )+"), "-")
 +    .replace(" ", "-").trim()
 +    .replaceFirstChar { it.lowercase(Locale.getDefault()) }.toCharArray()
 +    .map {
 +      if (it.isUpperCase()) "-${it.lowercaseChar()}"
 +      else it
 +    }
 +    .joinToString(separator = "")
 +
 +  val hyphenatedPath = try {
 +    val originalPath = Path(this)
 +
 +    var convertedPath = Path("")
 +    (0 until originalPath.nameCount).map { pathNameIndex ->
 +      convertedPath = convertedPath.resolve(hyphenateString(originalPath.getName(pathNameIndex).name))
 +    }
 +
 +    convertedPath.toString()
 +  }
 +  catch (_: Exception) {
 +    return hyphenateString(this)
 +  }
 +
 +  return hyphenatedPath
 +}
index 946da7d1dcc67c9f95c7c0b9bc746fd3aacdd008,0000000000000000000000000000000000000000..715cb558ba78293dcbd14c9c05f243d33d1bb220
mode 100644,000000..100644
--- /dev/null
@@@ -1,276 -1,0 +1,288 @@@
- import com.intellij.ide.starter.exec.ExecOutputRedirect
- import com.intellij.ide.starter.exec.exec
 +package com.intellij.ide.starter.utils
 +
 +import com.intellij.ide.starter.di.di
- inline fun catchAll(action: () -> Unit) {
 +import com.intellij.ide.starter.path.GlobalPaths
++import com.intellij.ide.starter.process.exec.ExecOutputRedirect
++import com.intellij.ide.starter.process.exec.ProcessExecutor
 +import com.intellij.ide.starter.system.SystemInfo
 +import org.kodein.di.direct
 +import org.kodein.di.instance
 +import java.io.*
 +import java.lang.Long.numberOfLeadingZeros
 +import java.nio.charset.Charset
 +import java.nio.file.FileStore
 +import java.nio.file.Files
 +import java.nio.file.Path
 +import java.time.LocalDateTime
 +import java.time.format.DateTimeFormatter
 +import kotlin.io.path.*
 +import kotlin.time.Duration.Companion.minutes
 +import kotlin.time.Duration.Companion.seconds
 +
 +fun formatArtifactName(artifactType: String, testName: String): String {
 +  val testNameFormatted = testName.replace("/", "-").replace(" ", "")
 +  val time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
 +  return "$artifactType-$testNameFormatted-$time"
 +}
 +
 +fun getThrowableText(t: Throwable): String {
 +  val writer = StringWriter()
 +  t.printStackTrace(PrintWriter(writer))
 +  return writer.buffer.toString()
 +}
 +
-     action()
++/**
++ * In case of success - return T
++ * In case of error - print error to stderr and return null
++ */
++inline fun <T> catchAll(action: () -> T): T? {
 +  try {
-   exec(
-     presentablePurpose = prefix,
++    return action()
 +  }
 +  catch (t: Throwable) {
 +    logError("CatchAll swallowed error: ${t.message}")
 +    logError(getThrowableText(t))
++    return null
 +  }
 +}
 +
 +fun FileStore.getDiskInfo(): String = buildString {
 +  appendLine("Disk info of ${name()}")
 +  appendLine("  Total space: " + totalSpace.formatSize())
 +  appendLine("  Unallocated space: " + unallocatedSpace.formatSize())
 +  appendLine("  Usable space: " + usableSpace.formatSize())
 +}
 +
 +fun Runtime.getRuntimeInfo(): String = buildString {
 +  appendLine("Memory info")
 +  appendLine("  Total memory: " + totalMemory().formatSize())
 +  appendLine("  Free memory: " + freeMemory().formatSize())
 +  appendLine("  Max memory: " + maxMemory().formatSize())
 +}
 +
 +/**
 + * Invoke cmd: java [arg1 arg2 ... argN]
 + */
 +fun execJavaCmd(javaHome: Path, args: Iterable<String> = listOf()): List<String> {
 +  val ext = if (SystemInfo.isWindows) ".exe" else ""
 +  val realJavaHomePath = if (javaHome.isSymbolicLink()) javaHome.readSymbolicLink() else javaHome
 +
 +  val java = realJavaHomePath.toAbsolutePath().resolve("bin/java$ext")
 +  require(java.isRegularFile()) { "Java is not found under $java" }
 +
 +  val prefix = "exec-java-cmd"
 +  val stdout = ExecOutputRedirect.ToString()
 +  val stderr = ExecOutputRedirect.ToString()
 +
 +  val processArguments = listOf(java.toString()).plus(args)
 +
-   )
++  ProcessExecutor(
++    presentableName = prefix,
 +    workDir = javaHome,
 +    timeout = 1.minutes,
 +    args = processArguments,
 +    stdoutRedirect = stdout,
 +    stderrRedirect = stderr
-   exec(
-     presentablePurpose = "take-screenshot",
++  ).start()
 +
 +  val mergedOutput = listOf(stdout, stderr)
 +    .flatMap { it.read().split(System.lineSeparator()) }
 +    .map { it.trim() }
 +    .filter { it.isNotBlank() }
 +
 +  logOutput(
 +    """
 +    Result of calling $processArguments:
 +    ${mergedOutput.joinToString(System.lineSeparator())}
 +    """)
 +  return mergedOutput
 +}
 +
 +/**
 + * Invoke java -version
 + *
 + * @return
 + * openjdk version "17.0.1" 2021-10-19 LTS
 + * OpenJDK Runtime Environment Corretto-17.0.1.12.1 (build 17.0.1+12-LTS)
 + * OpenJDK 64-Bit Server VM Corretto-17.0.1.12.1 (build 17.0.1+12-LTS, mixed mode, sharing)
 + */
 +fun callJavaVersion(javaHome: Path): String = execJavaCmd(javaHome, listOf("-version")).joinToString(System.lineSeparator())
 +
 +fun isX64Jdk(javaHome: Path): Boolean {
 +  val archProperty = execJavaCmd(javaHome, listOf("-XshowSettings:all", "-version")).firstOrNull { it.startsWith("sun.arch.data.model") }
 +  if (archProperty.isNullOrBlank())
 +    throw IllegalAccessException("Couldn't get architecture property sun.arch.data.model value from JDK")
 +
 +  return archProperty.trim().endsWith("64")
 +}
 +
 +fun resolveInstalledJdk11(): Path {
 +  val jdkEnv = listOf("JDK_11_X64", "JAVA_HOME").firstNotNullOfOrNull { System.getenv(it) } ?: System.getProperty("java.home")
 +  val javaHome = jdkEnv?.let {
 +    val path = Path(it)
 +    if (path.isSymbolicLink()) path.readSymbolicLink()
 +    else path
 +  }
 +
 +  if (javaHome == null || !javaHome.exists())
 +    throw IllegalArgumentException("Java Home $javaHome is null, empty or doesn't exist. Specify JAVA_HOME to point to JDK 11 x64")
 +
 +  require(javaHome.isDirectory() && Files.walk(javaHome)
 +    .use { it.count() } > 10) {
 +    "Java Home $javaHome is not found or empty!"
 +  }
 +
 +  require(isX64Jdk(javaHome)) { "JDK at path $javaHome should support x64 architecture" }
 +  return javaHome
 +}
 +
 +fun String.withIndent(indent: String = "  "): String = lineSequence().map { "$indent$it" }.joinToString(System.lineSeparator())
 +
 +private fun quoteArg(arg: String): String {
 +
 +  val specials = " #'\"\n\r\t\u000c"
 +  if (!specials.any { arg.contains(it) }) {
 +    return arg
 +  }
 +
 +  val sb = StringBuilder(arg.length * 2)
 +  for (element in arg) {
 +    when (element) {
 +      ' ', '#', '\'' -> sb.append('"').append(element).append('"')
 +      '"' -> sb.append("\"\\\"\"")
 +      '\n' -> sb.append("\"\\n\"")
 +      '\r' -> sb.append("\"\\r\"")
 +      '\t' -> sb.append("\"\\t\"")
 +      else -> sb.append(element)
 +    }
 +  }
 +
 +  return sb.toString()
 +}
 +
 +/**
 + * Writes list of Java arguments to the Java Command-Line Argument File
 + * See https://docs.oracle.com/javase/9/tools/java.htm, section "java Command-Line Argument Files"
 + **/
 +fun writeJvmArgsFile(argFile: File,
 +                     args: List<String>,
 +                     lineSeparator: String = System.lineSeparator(),
 +                     charset: Charset = Charsets.UTF_8) {
 +  BufferedWriter(OutputStreamWriter(FileOutputStream(argFile), charset)).use { writer ->
 +    for (arg in args) {
 +      writer.write(quoteArg(arg))
 +      writer.write(lineSeparator)
 +    }
 +  }
 +}
 +
 +fun takeScreenshot(logsDir: Path) {
 +  val toolsDir = di.direct.instance<GlobalPaths>().getCacheDirectoryFor("tools")
 +  val toolName = "TakeScreenshot"
 +  val screenshotTool = toolsDir / toolName
 +  if (!File(screenshotTool.toString()).exists()) {
 +    val archivePath = toolsDir / "$toolName.zip"
 +    HttpClient.download("https://repo.labs.intellij.net/phpstorm/tools/TakeScreenshot-1.02.zip", archivePath)
 +    FileSystem.unpack(archivePath, screenshotTool)
 +  }
 +  val screenshotFile = logsDir.resolve("screenshot_beforeKill.jpg")
 +
 +  val toolPath = screenshotTool.resolve("$toolName.jar")
 +  val javaPath = ProcessHandle.current().info().command().orElseThrow().toString()
-   )
++  ProcessExecutor(
++    presentableName = "take-screenshot",
 +    workDir = toolsDir,
 +    timeout = 15.seconds,
 +    args = mutableListOf(javaPath, "-jar", toolPath.absolutePathString(), screenshotFile.toString()),
 +    environmentVariables = mapOf("DISPLAY" to ":88"),
 +    onlyEnrichExistedEnvVariables = true
-     val toolName = "async-profiler-2.7-macos"
++  ).start()
 +
 +  if (screenshotFile.exists()) {
 +    logOutput("Screenshot saved in $screenshotFile")
 +  }
 +  else {
 +    error("Couldn't take screenshot")
 +  }
 +}
 +
 +fun startProfileNativeThreads(pid: String) {
 +  if (!SystemInfo.isWindows) {
 +    val toolsDir = di.direct.instance<GlobalPaths>().getCacheDirectoryFor("tools")
-     exec(
-       presentablePurpose = "start-profile",
++    val toolName = when {
++      SystemInfo.isMac -> "async-profiler-2.7-macos"
++      SystemInfo.isLinux -> "async-profiler-2.7-linux-x64"
++      else -> error("Not supported OS")
++    }
 +    val profiler = toolsDir / toolName
 +    downloadAsyncProfilerIfNeeded(profiler, toolsDir)
 +    givePermissionsToExecutables(profiler)
-     )
++
++    ProcessExecutor(
++      presentableName = "start-profile",
 +      workDir = profiler,
 +      timeout = 15.seconds,
 +      args = mutableListOf("./profiler.sh", "start", pid)
-   exec(
-     presentablePurpose = "give-permissions-to-jattach",
++    ).start()
 +  }
 +}
 +
 +private fun givePermissionsToExecutables(profiler: Path) {
-   )
-   exec(
-     presentablePurpose = "give-permissions-to-profiler",
++  ProcessExecutor(
++    presentableName = "give-permissions-to-jattach",
 +    workDir = profiler.resolve("build"),
 +    timeout = 10.seconds,
 +    args = mutableListOf("chmod", "+x", "jattach")
-   )
++  ).start()
++
++  ProcessExecutor(
++    presentableName = "give-permissions-to-profiler",
 +    workDir = profiler,
 +    timeout = 10.seconds,
 +    args = mutableListOf("chmod", "+x", "profiler.sh")
-     exec(
-       presentablePurpose = "stop-profile",
++  ).start()
 +}
 +
 +fun stopProfileNativeThreads(pid: String, fileToStoreInfo: String) {
 +  if (!SystemInfo.isWindows) {
 +    val toolsDir = di.direct.instance<GlobalPaths>().getCacheDirectoryFor("tools")
 +    val toolName = "async-profiler-2.7-macos"
 +    val profiler = toolsDir / toolName
-     )
++
++    ProcessExecutor(
++      presentableName = "stop-profile",
 +      workDir = profiler,
 +      timeout = 15.seconds,
 +      args = mutableListOf("./profiler.sh", "stop", pid, "-f", fileToStoreInfo)
++    ).start()
 +  }
 +}
 +
 +private fun downloadAsyncProfilerIfNeeded(profiler: Path, toolsDir: Path) {
 +  if (!File(profiler.toString()).exists()) {
 +    val profilerFileName = when {
 +      SystemInfo.isMac -> "async-profiler-2.7-macos.zip"
 +      SystemInfo.isLinux -> "async-profiler-2.7-linux-x64.tar.gz"
 +      else -> error("Current OS is not supported")
 +    }
 +    val archivePath = toolsDir / profilerFileName
 +    HttpClient.download("https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.7/$profilerFileName",
 +                        archivePath)
 +    FileSystem.unpack(archivePath, toolsDir)
 +  }
 +}
 +
 +fun Long.formatSize(): String {
 +  if (this < 1024) return "$this B"
 +  val z = (63 - numberOfLeadingZeros(this)) / 10
 +  return String.format("%.1f %sB", this.toDouble() / (1L shl z * 10), " KMGTPE"[z])
 +}
 +
 +fun pathInsideJarFile(
 +  jarFile: Path,
 +  pathInsideJar: String
 +): String = jarFile.toAbsolutePath().toString().trimEnd('/') + "!/" + pathInsideJar
 +
 +data class FindUsagesCallParameters(val pathToFile: String, val offset: String, val element: String) {
 +  override fun toString() = "$pathToFile $element, $offset)"
 +}
index 0d4cbd391aadd5038af453f5d10e6fbfb4553a2c,0000000000000000000000000000000000000000..bd09e2f0405869f11a54c95f8fd79a2417fe816b
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,38 @@@
- class WslDistributionNotFoundException() : Exception() {
 +package com.intellij.ide.starter.wsl
 +
 +import com.intellij.execution.wsl.WslDistributionManager
 +import com.intellij.ide.starter.system.SystemInfo
 +import com.intellij.ide.starter.utils.logOutput
++import com.intellij.openapi.projectRoots.impl.jdkDownloader.JdkPredicate
 +
++class WslDistributionNotFoundException(val jdkPredicate: JdkPredicate? = null) : Exception() {
 +  override val message: String
 +    get() {
 +      val red = "\u001b[31m"
 +      val reset = "\u001b[0m"
 +      logOutput("\n\n\n\n\n")
 +      logOutput("=======================================================================================")
 +      logOutput("****                                                                               ****")
 +      logOutput("****   $red   WARNING         WARNING         WARNING      $reset                            ****")
 +      logOutput("****                                                                               ****")
 +      logOutput("****      You are trying to execute tests with WSL                                 ****")
 +      logOutput("****      You have no WSL installed distributions                                  ****")
 +      logOutput("****      Please follow the installation guide to install it:                      ****")
 +      logOutput("****         English: https://docs.microsoft.com/en-us/windows/wsl/install-win10   ****")
 +      logOutput("****         Russian: https://docs.microsoft.com/ru-ru/windows/wsl/install-win10   ****")
 +      logOutput("****                                                                               ****")
 +      logOutput("****                                                                               ****")
 +      logOutput("****    $red  WARNING         WARNING         WARNING     $reset                             ****")
 +      logOutput("****                                                                               ****")
 +      logOutput("=======================================================================================")
 +      logOutput("\n\n\n\n\n")
 +
 +      return buildString {
 +        appendLine("\n")
 +        appendLine("Can't run test on WSL")
 +        appendLine("Current OS: ${SystemInfo.OS_NAME} ${SystemInfo.OS_VERSION}")
 +        appendLine("WSL distributions installed: ${WslDistributionManager.getInstance().installedDistributions}")
++        jdkPredicate?.let { appendLine(jdkPredicate) }
 +      }
 +    }
 +}