PY-19015: Check env has commands, skip fixers if has no
authorIlya.Kazakevich <Ilya.Kazakevich@jetbrains.com>
Tue, 28 Jun 2016 22:47:16 +0000 (01:47 +0300)
committerIlya.Kazakevich <Ilya.Kazakevich@jetbrains.com>
Tue, 28 Jun 2016 22:47:16 +0000 (01:47 +0300)
Since prev. approach was based on idea that any env has some tests and failed on project from PY-19015, I now use unofficial API to inherit Session and track env start and end. Skip support is also added. See _MySession from _jb_tox_runner.py for more info. This approach is hacky, but all other APIs are too lame

python/helpers/pycharm/_jb_tox_runner.py
python/testData/toxtest/toxOneInterpreter/tox.ini [new file with mode: 0644]
python/testSrc/com/jetbrains/env/PyToxTest.java

index c99b5590c79b392db65b1ebbf2c23585f784acfc..2b82041edb7919d34bb18325a12aab73dcd0c5f4 100644 (file)
@@ -5,16 +5,81 @@ It supports any runner, but well-known runners (py.test and unittest) are switch
 better support
 """
 import os
-import sys
 
 from tox import config as tox_config, session as tox_session
-from tox.session import Reporter
 
 from tcmessages import TeamcityServiceMessages
+from tox import exception
+
+teamcity = TeamcityServiceMessages()
 
 helpers_dir = str(os.path.split(__file__)[0])
 
 
+class _MySession(tox_session.Session):
+    """
+    Session is extended to overwrite "setupenv" as "env begin" ans "_summary" as "end of all"
+    Hooks API is not enough to cover each case, reporter is not enough as well
+    Session inheritance is the only way to go, even it is not stable and should be checked
+    against each version
+    """
+
+    def __init__(self, *args, **kwargs):
+        tox_session.Session.__init__(self, *args, **kwargs)
+        self.current_env = None
+
+    def setupenv(self, venv):
+        """
+        Launched before each setup.
+        It means prev env (if any) just finished and new is going to be created
+        :param venv: current virtual env
+        """
+        self._finish_current_env_if_need()
+        self.current_env = venv
+        teamcity.testSuiteStarted(venv.name, location="tox_env://" + str(venv.name))
+        return tox_session.Session.setupenv(self, venv)
+
+    def _finish_current_env_if_need(self):
+        """
+        Finishes currently running env. reporting its state
+        """
+        if not self.current_env:
+            return
+
+        status = self.current_env.status
+        if isinstance(status, exception.InterpreterNotFound):
+            if self.config.option.skip_missing_interpreters:
+                self._reportSuiteStateLeaf("SKIP", status)
+            else:
+                self._reportSuiteStateLeaf("ERROR", status)
+        elif status == "platform mismatch":
+            self._reportSuiteStateLeaf("SKIP", status)
+        elif status and status == "ignored failed command":
+            print("  %s: %s" % (self.current_env.name, str(status)))
+        elif status and status != "skipped tests":
+            self._reportSuiteStateLeaf("ERROR", status)
+        teamcity.testStdOut(self.current_env.name, "\n")
+        teamcity.testSuiteFinished(self.current_env.name)
+        self.current_env = None
+
+    def _reportSuiteStateLeaf(self, state, message):
+        """
+        Since platform does not support empty suite, we need to output something.
+        :param state: SKIP or ERROR (suite result)
+        """
+        teamcity.testStarted(state, "tox_env://" + str(self.current_env.name))
+        if state == "SKIP":
+            teamcity.testIgnored(state, str(message))
+        else:
+            teamcity.testFailed(state, str(message))
+
+    def _summary(self):
+        """
+        To be called after whole suite.
+        """
+        self._finish_current_env_if_need()
+
+
 class _Unit2(object):
     def fix(self, command, dir_to_run, bin):
         if command[0] == "unit2":
@@ -39,45 +104,8 @@ class _Nose(object):
         return [bin, os.path.join(helpers_dir, "noserunner.py"), dir_to_run] + command[1:]
 
 
-
 _RUNNERS = [_Unit2(), _PyTest(), _Nose()]
 
-teamcity = TeamcityServiceMessages()
-
-
-class _Reporter(Reporter):
-    def logaction_start(self, action):
-        super(_Reporter, self).logaction_start(action)
-        if action.activity == "getenv":
-            teamcity.output.write("\n")
-            teamcity.testSuiteStarted(action.id, location="tox_env://" + str(action.id))
-            self.current_suite = action.id
-
-    def logaction_finish(self, action):
-        super(_Reporter, self).logaction_finish(action)
-        if action.activity == "runtests":
-            teamcity.testSuiteFinished(action.id)
-            teamcity.output.write("\n")
-
-    def error(self, msg):
-        super(_Reporter, self).error(msg)
-        name = teamcity.current_test_name()
-        if name:
-            if name != teamcity.topmost_suite:
-                teamcity.testFailed(name, msg)
-            else:
-                teamcity.testFailed("ERROR", msg)
-                teamcity.testSuiteFinished(name)
-        else:
-            sys.stderr.write(msg)
-
-    def skip(self, msg):
-        super(_Reporter, self).skip(msg)
-        name = teamcity.current_test_name()
-        if name:
-            teamcity.testFinished(name)
-
-
 config = tox_config.parseconfig()
 for env, tmp_config in config.envconfigs.items():
     if not tmp_config.setenv:
@@ -90,11 +118,12 @@ for env, tmp_config in config.envconfigs.items():
         _env = config.envconfigs[env]
         dir_to_run = str(_env.changedir)
         for i, command in enumerate(commands):
-            fixed_command = fixer.fix(command, dir_to_run, str(_env.envpython))
-            if fixed_command:
-                commands[i] = fixed_command
+            if command:
+                fixed_command = fixer.fix(command, dir_to_run, str(_env.envpython))
+                if fixed_command:
+                    commands[i] = fixed_command
     tmp_config.commands = commands
 
-session = tox_session.Session(config, Report=_Reporter)
+session = _MySession(config)
 teamcity.testMatrixEntered()
 session.runcommand()
diff --git a/python/testData/toxtest/toxOneInterpreter/tox.ini b/python/testData/toxtest/toxOneInterpreter/tox.ini
new file mode 100644 (file)
index 0000000..a752b15
--- /dev/null
@@ -0,0 +1,7 @@
+[tox]
+skipsdist=True
+envlist = py26, py27, py34, py32
+skip_missing_interpreters = True
+[testenv:py27]
+commands=python --version
+whitelist_externals  = python
\ No newline at end of file
index 3c84d6ce9225c5b92cf012e3f10ea4f3bae48417..cf8eb44b615e9596d378d13b2d2c27d677226ab6 100644 (file)
@@ -112,6 +112,25 @@ public final class PyToxTest extends PyEnvTestCase {
     );
   }
 
+  /**
+   * Checks empty envs for all but 2.7
+   */
+  @Test
+  public void textToxOneInterpreter() throws Exception {
+    runPythonTest(new MyPyProcessWithConsoleTestTask("/toxtest/toxOneInterpreter/", 0,
+                                                     new MyTestProcessRunner(),
+                                                     Arrays.asList(
+                                                       Pair.create("py26", new InterpreterExpectations("", true)),
+                                                       Pair.create("py27", new InterpreterExpectations("ython 2.7", true)),
+                                                       Pair.create("py32", new InterpreterExpectations("", true)),
+                                                       Pair.create("py34", new InterpreterExpectations("", true))
+                                                     )
+                  )
+    );
+
+
+  }
+
   /**
    * Big test which should run on any interpreter and check its output
    */
@@ -183,6 +202,7 @@ public final class PyToxTest extends PyEnvTestCase {
           // Interpreter failed to run
           final String testOutput = getTestOutput(interpreterSuite.getChildren().get(0));
           if (testOutput.contains("InterpreterNotFound")) {
+            // Skipped with out of "skip_missing_interpreters = True"
             Logger.getInstance(PyToxTest.class).warn(String.format("Interpreter %s does not exit", interpreterName));
             skippedInterpreters.add(interpreterName); // Interpreter does not exit
             continue;
@@ -195,6 +215,13 @@ public final class PyToxTest extends PyEnvTestCase {
           continue;
         }
 
+        if (interpreterSuite.getChildren().size() == 1 && interpreterSuite.getChildren().get(0).getName().endsWith("SKIP")) {
+          // The only reason it may be skipped is it does not exist and skip_missing_interpreters = True
+          final String output = getTestOutput(interpreterSuite);
+          Assert.assertThat("Test marked skipped but not because interpreter not found", output, Matchers.containsString("InterpreterNotFound"));
+        }
+
+
         // Interpretr run success,
         //At least one interpreter tests should passed
         Assert.assertThat(String.format("No test passed, should %s at least", myMinimumSuccessTestCount),