cc2f85d2000c9d0d6e107eda785b7828da12505a
[idea/community.git] / python / testSrc / com / jetbrains / env / PyEnvTaskRunner.java
1 package com.jetbrains.env;
2
3 import com.google.common.collect.Lists;
4 import com.google.common.collect.Sets;
5 import com.intellij.openapi.diagnostic.Logger;
6 import com.intellij.openapi.projectRoots.Sdk;
7 import com.intellij.openapi.vfs.VfsUtil;
8 import com.intellij.openapi.vfs.VirtualFile;
9 import com.jetbrains.python.psi.LanguageLevel;
10 import com.jetbrains.python.sdk.InvalidSdkException;
11 import com.jetbrains.python.sdk.PythonSdkType;
12 import com.jetbrains.python.sdkTools.PyTestSdkTools;
13 import com.jetbrains.python.sdkTools.SdkCreationType;
14 import org.jetbrains.annotations.NotNull;
15 import org.jetbrains.annotations.Nullable;
16
17 import java.io.IOException;
18 import java.net.URL;
19 import java.util.List;
20 import java.util.Set;
21
22 /**
23  * @author traff
24  */
25 public class PyEnvTaskRunner {
26   private static final Logger LOG = Logger.getInstance(PyEnvTaskRunner.class);
27   private final List<String> myRoots;
28
29   public PyEnvTaskRunner(List<String> roots) {
30     myRoots = roots;
31   }
32
33   // todo: doc
34   public void runTask(PyTestTask testTask, String testName, @NotNull final String... tagsRequiedByTest) {
35     boolean wasExecuted = false;
36
37     List<String> passedRoots = Lists.newArrayList();
38
39     for (String root : myRoots) {
40       LOG.warn(String.format("Running on root %s", root));
41
42       final Set<String> requredTags = Sets.union(testTask.getTags(), Sets.newHashSet(tagsRequiedByTest));
43       final boolean suitableForTask = isSuitableForTask(PyEnvTestCase.loadEnvTags(root), requredTags);
44       final boolean shouldRun = shouldRun(root, testTask);
45       if (!suitableForTask || !shouldRun) {
46         LOG.warn(String.format("Skipping %s (compatible with tags: %s, should run:%s)", root, suitableForTask, shouldRun));
47         continue;
48       }
49
50       try {
51         testTask.setUp(testName);
52         wasExecuted = true;
53         if (isJython(root)) {
54           testTask.useLongTimeout();
55         }
56         else {
57           testTask.useNormalTimeout();
58         }
59         final String executable = getExecutable(root, testTask);
60         if (executable == null) {
61           throw new RuntimeException("Cannot find Python interpreter in " + root);
62         }
63         final Sdk sdk = createSdkByExecutable(executable);
64
65         /**
66          * Skipping test if {@link PyTestTask} reports it does not support this language level
67          */
68         final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
69         if (testTask.isLanguageLevelSupported(languageLevel)) {
70           testTask.runTestOn(executable);
71           passedRoots.add(root);
72         }
73         else {
74           LOG.warn(String.format("Skipping root %s", root));
75         }
76       }
77       catch (final Throwable e) {
78         // Direct output of enteredTheMatrix may break idea or TC since can't distinguish test output from real test result
79         // Exception is thrown anyway, so we escape message before logging
80         if (e.getMessage().contains("enteredTheMatrix")) {
81           // .error( may lead to new exception with out of stacktrace.
82           LOG.warn(PyEnvTestCase.escapeTestMessage(e.getMessage()));
83         }
84         else {
85           LOG.error(e);
86         }
87         throw new RuntimeException(
88           PyEnvTestCase.joinStrings(passedRoots, "Tests passed environments: ") + "Test failed on " + getEnvType() + " environment " + root,
89           e);
90       }
91       finally {
92         try {
93           testTask.tearDown();
94         }
95         catch (Exception e) {
96           throw new RuntimeException("Couldn't tear down task", e);
97         }
98       }
99     }
100
101     if (!wasExecuted) {
102       throw new RuntimeException("test" +
103                                  testName +
104                                  " was not executed.\n" +
105                                  PyEnvTestCase.joinStrings(myRoots, "All roots: ") +
106                                  "\n" +
107                                  PyEnvTestCase.joinStrings(testTask.getTags(), "Required tags in tags.txt in root: "));
108     }
109   }
110
111   /**
112    * Create SDK by path to python exectuable
113    *
114    * @param executable path executable
115    * @return sdk or null if there is no sdk on this path
116    * @throws InvalidSdkException bad sdk
117    */
118   @Nullable
119   private static Sdk createSdkByExecutable(@NotNull final String executable) throws InvalidSdkException, IOException {
120     final URL rootUrl = new URL(String.format("file:///%s", executable));
121     final VirtualFile url = VfsUtil.findFileByURL(rootUrl);
122     if (url == null) {
123       return null;
124     }
125     return PyTestSdkTools.createTempSdk(url, SdkCreationType.EMPTY_SDK, null);
126   }
127
128   protected boolean shouldRun(String root, PyTestTask task) {
129     return true;
130   }
131
132   protected String getExecutable(String root, PyTestTask testTask) {
133     return PythonSdkType.getPythonExecutable(root);
134   }
135
136   protected String getEnvType() {
137     return "local";
138   }
139
140   private static boolean isSuitableForTask(List<String> availableTags, @NotNull final Set<String> requiredTags) {
141     return isSuitableForTags(availableTags, requiredTags);
142   }
143
144   public static boolean isSuitableForTags(List<String> envTags, Set<String> taskTags) {
145     Set<String> necessaryTags = Sets.newHashSet(taskTags);
146
147     for (String tag : envTags) {
148       necessaryTags.remove(tag.trim());
149     }
150
151     for (String tag : taskTags) {
152       if (tag.startsWith("-")) { //do not run on envs with that tag
153         if (envTags.contains(tag.substring(1))) {
154           return false;
155         }
156         necessaryTags.remove(tag);
157       }
158     }
159
160     return necessaryTags.isEmpty();
161   }
162
163
164   public static boolean isJython(@NotNull String sdkHome) {
165     return sdkHome.toLowerCase().contains("jython");
166   }
167 }