private static String getLauncherTemplate(FlexBuildConfiguration bc) throws IOException {
   String templateName;
   if (bc.isPureAs()) {
     templateName = "LauncherTemplateAs.as";
   } else if (bc.getNature().isMobilePlatform()
       || bc.getDependencies().getComponentSet() == ComponentSet.SparkOnly) {
     templateName = "LauncherTemplateSpark.mxml";
   } else {
     templateName = "LauncherTemplateMx.mxml";
   }
   final URL resource =
       FlexUnitPrecompileTask.class.getResource("/unittestingsupport/" + templateName);
   return ResourceUtil.loadText(resource);
 }
  public void cacheBC(
      final Module module, final FlexBuildConfiguration bc, final List<VirtualFile> configFiles) {
    Collection<BCInfo> infosForModule = myCache.get(module);
    if (infosForModule == null) {
      infosForModule = new ArrayList<BCInfo>();
      myCache.put(module, infosForModule);
    } else {
      final BCInfo existingInfo = findCacheForBC(infosForModule, bc);
      if (existingInfo != null) {
        infosForModule.remove(existingInfo);
      }
    }

    final VirtualFile outputFile =
        FlexCompilationManager.refreshAndFindFileInWriteAction(bc.getActualOutputFilePath());
    if (outputFile == null) return;

    final BCInfo bcInfo =
        new BCInfo(Factory.getCopy(bc), ModuleRootManager.getInstance(module).getSourceRootUrls());
    infosForModule.add(bcInfo);

    bcInfo.addFileDependency(outputFile.getPath());

    final String workDirPath = FlexUtils.getFlexCompilerWorkDirPath(module.getProject(), null);
    for (VirtualFile configFile : configFiles) {
      addFileDependencies(bcInfo, configFile, workDirPath);
    }

    if (bc.isTempBCForCompilation()
        && !bc.getCompilerOptions().getAdditionalConfigFilePath().isEmpty()) {
      bcInfo.addFileDependency(bc.getCompilerOptions().getAdditionalConfigFilePath());
    }

    final BuildConfigurationNature nature = bc.getNature();
    if (nature.isApp() && !nature.isWebPlatform()) {
      if (nature.isDesktopPlatform()) {
        if (!bc.getAirDesktopPackagingOptions().isUseGeneratedDescriptor()) {
          bcInfo.addFileDependency(bc.getAirDesktopPackagingOptions().getCustomDescriptorPath());
        }
      } else {
        if (bc.getAndroidPackagingOptions().isEnabled()
            && !bc.getAndroidPackagingOptions().isUseGeneratedDescriptor()) {
          bcInfo.addFileDependency(bc.getAndroidPackagingOptions().getCustomDescriptorPath());
        }
        if (bc.getIosPackagingOptions().isEnabled()
            && !bc.getIosPackagingOptions().isUseGeneratedDescriptor()) {
          bcInfo.addFileDependency(bc.getIosPackagingOptions().getCustomDescriptorPath());
        }
      }
    }
  }
  public boolean execute(CompileContext context) {
    final RunConfiguration runConfiguration =
        CompileStepBeforeRun.getRunConfiguration(context.getCompileScope());
    if (!(runConfiguration instanceof FlexUnitRunConfiguration)) {
      return true;
    }

    final Ref<Boolean> isDumb = new Ref<>(false);
    final RuntimeConfigurationException validationError =
        ApplicationManager.getApplication()
            .runReadAction(
                new NullableComputable<RuntimeConfigurationException>() {
                  public RuntimeConfigurationException compute() {
                    if (DumbService.getInstance(myProject).isDumb()) {
                      isDumb.set(true);
                      return null;
                    }
                    try {
                      runConfiguration.checkConfiguration();
                      return null;
                    } catch (RuntimeConfigurationException e) {
                      return e;
                    }
                  }
                });

    if (isDumb.get()) {
      context.addMessage(
          CompilerMessageCategory.ERROR,
          FlexBundle.message("dumb.mode.flex.unit.warning"),
          null,
          -1,
          -1);
      return false;
    }

    if (validationError != null) {
      context.addMessage(
          CompilerMessageCategory.ERROR,
          FlexBundle.message("configuration.not.valid", validationError.getMessage()),
          null,
          -1,
          -1);
      return false;
    }

    int flexUnitPort = ServerConnectionBase.getFreePort(FLEX_UNIT_PORT_START, PORTS_ATTEMPT_NUMBER);
    if (flexUnitPort == -1) {
      context.addMessage(
          CompilerMessageCategory.ERROR, FlexBundle.message("no.free.port"), null, -1, -1);
      return false;
    }

    final int socketPolicyPort;
    if (SystemInfo.isWindows
        && ServerConnectionBase.tryPort(SwfPolicyFileConnection.DEFAULT_PORT)) {
      socketPolicyPort = SwfPolicyFileConnection.DEFAULT_PORT;
    } else {
      socketPolicyPort =
          ServerConnectionBase.getFreePort(SWC_POLICY_PORT_START, PORTS_ATTEMPT_NUMBER);
    }

    if (socketPolicyPort == -1) {
      context.addMessage(
          CompilerMessageCategory.ERROR, FlexBundle.message("no.free.port"), null, -1, -1);
      return false;
    }

    final FlexUnitRunnerParameters params =
        ((FlexUnitRunConfiguration) runConfiguration).getRunnerParameters();
    params.setPort(flexUnitPort);
    params.setSocketPolicyPort(socketPolicyPort);

    final Ref<Module> moduleRef = new Ref<>();
    final Ref<FlexBuildConfiguration> bcRef = new Ref<>();
    final Ref<FlexUnitSupport> supportRef = new Ref<>();

    ApplicationManager.getApplication()
        .runReadAction(
            () -> {
              if (DumbService.getInstance(myProject).isDumb()) return;

              try {
                final Pair<Module, FlexBuildConfiguration> moduleAndBC =
                    params.checkAndGetModuleAndBC(myProject);
                moduleRef.set(moduleAndBC.first);
                bcRef.set(moduleAndBC.second);
                supportRef.set(FlexUnitSupport.getSupport(moduleAndBC.second, moduleAndBC.first));
              } catch (RuntimeConfigurationError e) {
                // already checked above, can't happen
                throw new RuntimeException(e);
              }
            });

    final Module module = moduleRef.get();
    final FlexBuildConfiguration bc = bcRef.get();
    final FlexUnitSupport support = supportRef.get();

    if (bc == null || support == null) {
      context.addMessage(
          CompilerMessageCategory.ERROR,
          FlexBundle.message("dumb.mode.flex.unit.warning"),
          null,
          -1,
          -1);
      return false;
    }

    final GlobalSearchScope moduleScope = GlobalSearchScope.moduleScope(module);

    StringBuilder imports = new StringBuilder();
    StringBuilder code = new StringBuilder();

    final boolean flexUnit4;
    switch (params.getScope()) {
      case Class:
        {
          final Ref<Boolean> isFlexUnit1Suite = new Ref<>();
          final Ref<Boolean> isSuite = new Ref<>();
          Set<String> customRunners =
              ApplicationManager.getApplication()
                  .runReadAction(
                      new NullableComputable<Set<String>>() {
                        public Set<String> compute() {
                          if (DumbService.getInstance(myProject).isDumb()) return null;
                          Set<String> result = new THashSet<>();
                          final JSClass clazz =
                              (JSClass)
                                  ActionScriptClassResolver.findClassByQNameStatic(
                                      params.getClassName(), moduleScope);
                          collectCustomRunners(result, clazz, support, null);
                          isFlexUnit1Suite.set(support.isFlexUnit1SuiteSubclass(clazz));
                          isSuite.set(support.isSuite(clazz));
                          return result;
                        }
                      });

          if (customRunners == null) {
            context.addMessage(
                CompilerMessageCategory.ERROR,
                FlexBundle.message("dumb.mode.flex.unit.warning"),
                null,
                -1,
                -1);
            return false;
          }
          // FlexUnit4 can't run FlexUnit1 TestSuite subclasses, fallback to FlexUnit1 runner
          flexUnit4 = support.flexUnit4Present && !isFlexUnit1Suite.get();
          generateImportCode(imports, params.getClassName(), customRunners);
          generateTestClassCode(code, params.getClassName(), customRunners, isSuite.get());
        }
        break;

      case Method:
        {
          Set<String> customRunners =
              ApplicationManager.getApplication()
                  .runReadAction(
                      new NullableComputable<Set<String>>() {
                        public Set<String> compute() {
                          if (DumbService.getInstance(myProject).isDumb()) return null;
                          Set<String> result = new THashSet<>();
                          final JSClass clazz =
                              (JSClass)
                                  ActionScriptClassResolver.findClassByQNameStatic(
                                      params.getClassName(), moduleScope);
                          collectCustomRunners(result, clazz, support, null);
                          return result;
                        }
                      });
          if (customRunners == null) {
            context.addMessage(
                CompilerMessageCategory.ERROR,
                FlexBundle.message("dumb.mode.flex.unit.warning"),
                null,
                -1,
                -1);
            return false;
          }

          flexUnit4 = support.flexUnit4Present;
          generateImportCode(imports, params.getClassName(), customRunners);
          generateTestMethodCode(
              code, params.getClassName(), params.getMethodName(), customRunners);
        }
        break;

      case Package:
        {
          final Collection<Pair<String, Set<String>>> classes =
              ApplicationManager.getApplication()
                  .runReadAction(
                      new NullableComputable<Collection<Pair<String, Set<String>>>>() {
                        public Collection<Pair<String, Set<String>>> compute() {
                          if (DumbService.getInstance(myProject).isDumb()) return null;

                          final Collection<Pair<String, Set<String>>> result = new ArrayList<>();
                          JSPackageIndex.processElementsInScopeRecursive(
                              params.getPackageName(),
                              new JSPackageIndex.PackageQualifiedElementsProcessor() {
                                public boolean process(
                                    String qualifiedName,
                                    JSPackageIndexInfo.Kind kind,
                                    boolean isPublic) {
                                  if (kind == JSPackageIndexInfo.Kind.CLASS) {
                                    PsiElement clazz =
                                        ActionScriptClassResolver.findClassByQNameStatic(
                                            qualifiedName, moduleScope);
                                    if (clazz instanceof JSClass
                                        && support.isTestClass((JSClass) clazz, false)) {
                                      Set<String> customRunners = new THashSet<>();
                                      collectCustomRunners(
                                          customRunners, (JSClass) clazz, support, null);
                                      result.add(
                                          Pair.create(
                                              ((JSClass) clazz).getQualifiedName(), customRunners));
                                    }
                                  }
                                  return true;
                                }
                              },
                              moduleScope,
                              myProject);
                          return result;
                        }
                      });

          if (classes == null) {
            context.addMessage(
                CompilerMessageCategory.ERROR,
                FlexBundle.message("dumb.mode.flex.unit.warning"),
                null,
                -1,
                -1);
            return false;
          }

          if (classes.isEmpty()) {
            String message =
                MessageFormat.format("No tests found in package ''{0}''", params.getPackageName());
            context.addMessage(CompilerMessageCategory.WARNING, message, null, -1, -1);
            return false;
          }

          flexUnit4 = support.flexUnit4Present;
          for (Pair<String, Set<String>> classAndRunner : classes) {
            generateImportCode(imports, classAndRunner.first, classAndRunner.second);
            generateTestClassCode(code, classAndRunner.first, classAndRunner.second, false);
          }
        }
        break;
      default:
        flexUnit4 = false;
        assert false : "Unknown scope: " + params.getScope();
    }

    if (!flexUnit4 && bc.isPureAs()) {
      context.addMessage(
          CompilerMessageCategory.ERROR,
          FlexBundle.message("cant.execute.flexunit1.for.pure.as.bc"),
          null,
          -1,
          -1);
    }

    String launcherText;
    try {
      launcherText = getLauncherTemplate(bc);
    } catch (IOException e) {
      context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
      return false;
    }

    final boolean desktop = bc.getTargetPlatform() == TargetPlatform.Desktop;
    if (desktop) {
      generateImportCode(imports, "flash.desktop.NativeApplication");
    }

    launcherText = replace(launcherText, "/*imports*/", imports.toString());
    launcherText =
        replace(
            launcherText,
            "/*test_runner*/",
            flexUnit4
                ? FlexCommonUtils.FLEXUNIT_4_TEST_RUNNER
                : FlexCommonUtils.FLEXUNIT_1_TEST_RUNNER);
    launcherText = replace(launcherText, "/*code*/", code.toString());
    launcherText = replace(launcherText, "/*port*/", String.valueOf(flexUnitPort));
    launcherText = replace(launcherText, "/*socketPolicyPort*/", String.valueOf(socketPolicyPort));
    launcherText = replace(launcherText, "/*module*/", module.getName());
    if (!bc.isPureAs()) {
      final FlexUnitRunnerParameters.OutputLogLevel logLevel = params.getOutputLogLevel();
      launcherText = replace(launcherText, "/*isLogEnabled*/", logLevel != null ? "1" : "0");
      launcherText =
          replace(
              launcherText,
              "/*logLevel*/",
              logLevel != null
                  ? logLevel.getFlexConstant()
                  : FlexUnitRunnerParameters.OutputLogLevel.All.getFlexConstant());
    }

    final File tmpDir = new File(getPathToFlexUnitTempDirectory(myProject));
    boolean ok = true;
    if (tmpDir.isFile()) ok &= FileUtil.delete(tmpDir);
    if (!tmpDir.isDirectory()) ok &= tmpDir.mkdirs();
    if (!ok) {
      final String message =
          UIBundle.message(
              "create.new.folder.could.not.create.folder.error.message",
              FileUtil.toSystemDependentName(tmpDir.getPath()));
      context.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1);
      return false;
    }

    final String fileName =
        FlexCommonUtils.FLEX_UNIT_LAUNCHER
            + FlexCommonUtils.getFlexUnitLauncherExtension(bc.getNature());
    final File launcherFile = new File(tmpDir, fileName);
    FileUtil.delete(launcherFile);

    try {
      FileUtil.writeToFile(launcherFile, launcherText);
    } catch (IOException e) {
      context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
      return false;
    }

    context.putUserData(FILES_TO_DELETE, Collections.singletonList(launcherFile.getPath()));
    return true;
  }