/* * Copyright 2000-2010 JetBrains s.r.o. * * 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. */ package jetbrains.buildServer.agent.rakerunner.utils; import java.io.FileFilter; import java.util.regex.Matcher; import java.util.regex.Pattern; import jetbrains.buildServer.RunBuildException; import jetbrains.buildServer.agent.rakerunner.RakeTasksBuildService; import jetbrains.buildServer.agent.rakerunner.RubySdk; import jetbrains.buildServer.rakerunner.RakeRunnerBundle; import jetbrains.buildServer.util.VersionComparatorUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.Map; import org.jetbrains.annotations.Nullable; import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName; import static jetbrains.buildServer.rakerunner.RakeRunnerConstants.TEST_UNIT_GEM_VERSION_PROPERTY; /** * @author Roman.Chernyatchik */ public class RubySdkUtil { @NonNls public static final String AUTORUNNER_SCRIPT_PATH = "test/unit/autorunner.rb"; @NonNls public static final String TESTRUNNERMEDIATOR_SCRIPT_PATH = "test/unit/ui/testrunnermediator.rb"; private static final String TEST_UNIT_GEM_SUFFIX = "test-unit-"; private static final Pattern VERSION_PATTERN = Pattern.compile("([0-9]+(\\.[0-9A-z]+)*)"); @NotNull public static String getSDKTestUnitAutoRunnerScriptPath(@NotNull final RubySdk sdk, @NotNull final Map buildParameters) throws RakeTasksBuildService.MyBuildFailureException { return findTestUnitScript(sdk, buildParameters, AUTORUNNER_SCRIPT_PATH); } @NotNull public static String getSDKTestUnitTestRunnerMediatorScriptPath(@NotNull final RubySdk sdk, @NotNull final Map buildParameters) throws RakeTasksBuildService.MyBuildFailureException { return findTestUnitScript(sdk, buildParameters, TESTRUNNERMEDIATOR_SCRIPT_PATH); } /** * Finds script with given relative path in test-unit gem or Test::Unit framework bundled in SDK * @param buildParameters Build params * @param scriptPath Path of given script * @return Full path of given script * @throws jetbrains.buildServer.agent.rakerunner.RakeTasksBuildService.MyBuildFailureException If script will not be found * @throws RunBuildException Other error */ private static String findTestUnitScript(@NotNull final RubySdk sdk, final Map buildParameters, final String scriptPath) throws RakeTasksBuildService.MyBuildFailureException { // At first let's try to find script in "test-unit" gem // then in sdk load path String testUnitGemVersion = null; String testUnitGemPath = null; final String customTestUnitGemVersionProperty = buildParameters.get(TEST_UNIT_GEM_VERSION_PROPERTY); final String customTestUnitGemVersion = !TextUtil.isEmpty(customTestUnitGemVersionProperty) ? customTestUnitGemVersionProperty.trim() : null ; // look for "test-unit" gems in gem paths for (String gemPath : sdk.getGemPaths()) { if (customTestUnitGemVersion != null && testUnitGemPath != null) { // test-unit gem was found in previous gems path break; } final String gemsFolderPath = toSystemIndependentName(gemPath + File.separatorChar + "gems"); // gem path file may not exist if (!FileUtil.checkIfDirExists(gemsFolderPath)) { continue; } // collect test-unit gems final File gemsFolder = new File(gemsFolderPath); final File[] testUnitGems = gemsFolder.listFiles(new FileFilter() { public boolean accept(final File file) { // accept only test-unit gems return file.getName().startsWith(TEST_UNIT_GEM_SUFFIX) && file.isDirectory(); } }); // find gem with highest version for (File gem : testUnitGems) { final String dirtyVersion = gem.getName().substring(TEST_UNIT_GEM_SUFFIX.length()); final Matcher matcher = VERSION_PATTERN.matcher(dirtyVersion); final String version; if (!matcher.find()) { final String msg = "Cannot determine gem version: " + TEST_UNIT_GEM_SUFFIX + testUnitGemVersion + "'(" + testUnitGemPath + ") gem. Please submit a feature request."; throw new RakeTasksBuildService.MyBuildFailureException(msg, RakeRunnerBundle.RUNNER_ERROR_TITLE_PROBLEMS_IN_CONF_ON_AGENT); } version = matcher.group(); if (customTestUnitGemVersion != null) { // custom version if (version.equals(customTestUnitGemVersion)) { testUnitGemVersion = version; testUnitGemPath = gem.getPath(); break; } } else { // determine latest version if (testUnitGemVersion == null || VersionComparatorUtil.compare(testUnitGemVersion, version) < 0) { testUnitGemVersion = version; testUnitGemPath = gem.getPath(); } } } } if (testUnitGemPath != null) { final String fullScriptPath = testUnitGemPath + File.separatorChar + "lib" + File.separatorChar + scriptPath; if (FileUtil.checkIfExists(fullScriptPath)) { return fullScriptPath; } else { // Error: Script wasn't found in test-unit gem final String msg = "Rake runner isn't compatible with your'" + TEST_UNIT_GEM_SUFFIX + testUnitGemVersion + "'(" + testUnitGemPath + ") gem. Please submit a feature request."; throw new RakeTasksBuildService.MyBuildFailureException(msg, RakeRunnerBundle.RUNNER_ERROR_TITLE_PROBLEMS_IN_CONF_ON_AGENT); } } final String[] loadPaths = sdk.getLoadPath(); for (String path : loadPaths) { final String fullScriptPath = toSystemIndependentName(path + File.separatorChar + scriptPath); if (FileUtil.checkIfExists(fullScriptPath)) { return fullScriptPath; } } // If stderr isn't empty / JAVA_HOME error final RubyScriptRunner.Output gemPathsLog = sdk.getGemPathsFetchLog(); failIfWithErrors(gemPathsLog); final RubyScriptRunner.Output loadPathsLog = sdk.getLoadPathsFetchLog(); failIfWithErrors(loadPathsLog); // General error message final boolean isRuby19 = sdk.isRuby19(); final String msg = "File '" + scriptPath + "' wasn't found in Gem paths and in $LOAD_PATH of Ruby SDK with interpreter: '" + sdk.getPresentableName() + "'\n" + (isRuby19 ? "Rake runner detected that your are using Ruby 1.9. So please install 'test-unit' gem because simplified Test::Unit framework, which is bundled in Ruby 1.9, doesn't support pluggable test reporters.\n" : "") + "\n" + "Gem paths:\n" + gemPathsLog.getStdout() + "\n" + "\n" + "$LOAD_PATH:\n" + loadPathsLog.getStdout(); throw new RakeTasksBuildService.MyBuildFailureException(msg, RakeRunnerBundle.RUNNER_ERROR_TITLE_PROBLEMS_IN_CONF_ON_AGENT); } private static void failIfWithErrors(final RubyScriptRunner.Output result) throws RakeTasksBuildService.MyBuildFailureException { // script wasn't found in LOAD_PATH: if (!TextUtil.isEmpty(result.getStderr())) { throw new RakeTasksBuildService.MyBuildFailureException(result.getStdout() + "\n" + result.getStderr(), RakeRunnerBundle.RUNNER_ERROR_TITLE_PROBLEMS_IN_CONF_ON_AGENT); } if (result.getStdout().contains("JAVA_HOME")) { throw new RakeTasksBuildService.MyBuildFailureException(result.getStdout(), RakeRunnerBundle.RUNNER_ERROR_TITLE_JRUBY_PROBLEMS_IN_CONF_ON_AGENT); } } public static RubyScriptRunner.Output executeScriptFromSource(@NotNull final RubySdk sdk, @Nullable final Map buildConfEnvironment, final String scriptSource, final String... rubyArgs) { return RubyScriptRunner.runScriptFromSource(sdk, rubyArgs, scriptSource, new String[0], buildConfEnvironment); } }