diff options
Diffstat (limited to 'python/testSrc/com/jetbrains')
37 files changed, 4062 insertions, 32 deletions
diff --git a/python/testSrc/com/jetbrains/env/PyEnvSufficiencyTest.java b/python/testSrc/com/jetbrains/env/PyEnvSufficiencyTest.java new file mode 100644 index 000000000000..83846a1bb5f5 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/PyEnvSufficiencyTest.java @@ -0,0 +1,51 @@ +package com.jetbrains.env; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.testFramework.UsefulTestCase; + +import java.util.List; +import java.util.Set; + +/** + * @author traff + */ +public class PyEnvSufficiencyTest extends PyEnvTestCase { + private static final List<String> BASE_TAGS = + ImmutableList.<String>builder().add("python3", "django", "jython", "ipython", "ipython011", "ipython012", "nose", "pytest").build(); + + public void testSufficiency() { + if (UsefulTestCase.IS_UNDER_TEAMCITY && IS_ENV_CONFIGURATION) { + Set<String> tags = Sets.newHashSet(); + List<String> roots = getPythonRoots(); + if (roots.size() == 0) { + return; // not on env agent + } + for (String root : roots) { + tags.addAll(loadEnvTags(root)); + } + + List<String> missing = Lists.newArrayList(); + for (String tag : necessaryTags()) { + if (!tags.contains(tag)) { + missing.add(tag); + } + } + + + assertEmpty("Agent is missing environments: " + StringUtil.join(missing, ", "), missing); + } + } + + private static List<String> necessaryTags() { + if (SystemInfo.isWindows) { + return ImmutableList.<String>builder().addAll(BASE_TAGS).add("iron").build(); + } + else { + return ImmutableList.<String>builder().addAll(BASE_TAGS).add("packaging").build(); + } + } +} diff --git a/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java b/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java new file mode 100644 index 000000000000..baabd5a5e5e1 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/PyEnvTaskRunner.java @@ -0,0 +1,112 @@ +package com.jetbrains.env; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.jetbrains.python.sdk.PythonSdkType; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Set; + +/** + * @author traff + */ +public class PyEnvTaskRunner { + private final List<String> myRoots; + + public PyEnvTaskRunner(List<String> roots) { + myRoots = roots; + } + + public void runTask(PyTestTask testTask, String testName) { + boolean wasExecuted = false; + + List<String> passedRoots = Lists.newArrayList(); + + for (String root : myRoots) { + + if (!isSuitableForTask(PyEnvTestCase.loadEnvTags(root), testTask) || !shouldRun(root, testTask)) { + continue; + } + + try { + testTask.setUp(testName); + wasExecuted = true; + if (isJython(root)) { + testTask.useLongTimeout(); + } + else { + testTask.useNormalTimeout(); + } + final String executable = getExecutable(root, testTask); + if (executable == null) { + throw new RuntimeException("Cannot find Python interpreter in " + root); + } + testTask.runTestOn(executable); + passedRoots.add(root); + } + catch (Throwable e) { + throw new RuntimeException( + PyEnvTestCase.joinStrings(passedRoots, "Tests passed environments: ") + "Test failed on " + getEnvType() + " environment " + root, + e); + } + finally { + try { + testTask.tearDown(); + } + catch (Exception e) { + throw new RuntimeException("Couldn't tear down task", e); + } + } + } + + if (!wasExecuted) { + throw new RuntimeException("test" + + testName + + " was not executed.\n" + + PyEnvTestCase.joinStrings(myRoots, "All roots: ") + + "\n" + + PyEnvTestCase.joinStrings(testTask.getTags(), "Required tags in tags.txt in root: ")); + } + } + + protected boolean shouldRun(String root, PyTestTask task) { + return true; + } + + protected String getExecutable(String root, PyTestTask testTask) { + return PythonSdkType.getPythonExecutable(root); + } + + protected String getEnvType() { + return "local"; + } + + private static boolean isSuitableForTask(List<String> tags, PyTestTask task) { + return isSuitableForTags(tags, task.getTags()); + } + + public static boolean isSuitableForTags(List<String> envTags, Set<String> taskTags) { + Set<String> necessaryTags = Sets.newHashSet(taskTags); + + for (String tag : envTags) { + necessaryTags.remove(tag.trim()); + } + + for (String tag : taskTags) { + if (tag.startsWith("-")) { //do not run on envs with that tag + if (envTags.contains(tag.substring(1))) { + return false; + } + necessaryTags.remove(tag); + } + } + + return necessaryTags.isEmpty(); + } + + + protected static boolean isJython(@NotNull String sdkHome) { + return sdkHome.toLowerCase().contains("jython"); + } +} diff --git a/python/testSrc/com/jetbrains/env/PyEnvTestCase.java b/python/testSrc/com/jetbrains/env/PyEnvTestCase.java new file mode 100644 index 000000000000..853a5504de63 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/PyEnvTestCase.java @@ -0,0 +1,201 @@ +package com.jetbrains.env; + +import com.google.common.collect.Lists; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.util.SystemProperties; +import com.intellij.util.ui.UIUtil; +import com.jetbrains.python.fixtures.PyTestCase; +import org.hamcrest.Matchers; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Assume; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +/** + * @author traff + */ +public abstract class PyEnvTestCase extends UsefulTestCase { + private static final Logger LOG = Logger.getInstance(PyEnvTestCase.class.getName()); + + private static final String TAGS_FILE = "tags.txt"; + private static final String PYCHARM_PYTHON_ENVS = "PYCHARM_PYTHON_ENVS"; + private static final String PYCHARM_PYTHON_VIRTUAL_ENVS = "PYCHARM_PYTHON_VIRTUAL_ENVS"; + + protected static final boolean IS_ENV_CONFIGURATION = System.getProperty("pycharm.env") != null; + + + public static final boolean RUN_REMOTE = SystemProperties.getBooleanProperty("pycharm.run_remote", false); + + public static final boolean RUN_LOCAL = SystemProperties.getBooleanProperty("pycharm.run_local", true); + + /** + * Tags that should exist between all tags, available on all interpreters for test to run. + * See {@link #PyEnvTestCase(String...)} + */ + @Nullable + private final String[] myRequiredTags; + + /** + * @param requiredTags tags that should exist on some interpreter for this test to run. + * if some of these tags do not exist on any interpreter, all test methods would be skipped using + * {@link org.junit.Assume}. + * See <a href="http://junit.sourceforge.net/javadoc/org/junit/Assume.html">Assume manual</a>. + * Check [IDEA-122939] and [TW-25043] as well. + */ + @SuppressWarnings("JUnitTestCaseWithNonTrivialConstructors") + protected PyEnvTestCase(@NotNull final String... requiredTags) { + myRequiredTags = requiredTags.length > 0 ? requiredTags.clone() : null; + + PyTestCase.initPlatformPrefix(); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + if (myRequiredTags != null) { // Ensure all tags exist between available interpreters + Assume.assumeThat( + "Can't find some tags between all available interpreter, test (all methods) will be skipped", + getAvailableTags(), + Matchers.hasItems(myRequiredTags) + ); + } + } + + /** + * @return all tags available between all interpreters + */ + @NotNull + private static Collection<String> getAvailableTags() { + final Collection<String> allAvailableTags = new HashSet<String>(); + for (final String pythonRoot : getPythonRoots()) { + allAvailableTags.addAll(loadEnvTags(pythonRoot)); + } + return allAvailableTags; + } + + @Override + protected void invokeTestRunnable(@NotNull final Runnable runnable) throws Exception { + if (runInWriteAction()) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + public void run() { + ApplicationManager.getApplication().runWriteAction(runnable); + } + }); + } + else { + runnable.run(); + } + } + + @Override + protected boolean runInDispatchThread() { + return false; + } + + protected boolean runInWriteAction() { + return false; + } + + public void runPythonTest(final PyTestTask testTask) { + runTest(testTask, getTestName(false)); + } + + public void runTest(@NotNull PyTestTask testTask, @NotNull String testName) { + if (notEnvConfiguration()) { + fail("Running under teamcity but not by Env configuration. Skipping."); + return; + } + + List<String> roots = getPythonRoots(); + + if (roots.size() == 0) { + String msg = testName + + ": environments are not defined. Skipping. \nSpecify either " + + PYCHARM_PYTHON_ENVS + + " or " + + PYCHARM_PYTHON_VIRTUAL_ENVS + + " environment variable."; + LOG.warn(msg); + System.out.println(msg); + return; + } + + doRunTests(testTask, testName, roots); + } + + protected void doRunTests(PyTestTask testTask, String testName, List<String> roots) { + if (RUN_LOCAL) { + PyEnvTaskRunner taskRunner = new PyEnvTaskRunner(roots); + + taskRunner.runTask(testTask, testName); + } + } + + + public static boolean notEnvConfiguration() { + return UsefulTestCase.IS_UNDER_TEAMCITY && !IS_ENV_CONFIGURATION; + } + + public static List<String> getPythonRoots() { + List<String> roots = Lists.newArrayList(); + + String envs = System.getenv(PYCHARM_PYTHON_ENVS); + if (envs != null) { + roots.addAll(Lists.newArrayList(envs.split(File.pathSeparator))); + } + + String virtualEnvs = System.getenv(PYCHARM_PYTHON_VIRTUAL_ENVS); + + if (virtualEnvs != null) { + roots.addAll(readVirtualEnvRoots(virtualEnvs)); + } + return roots; + } + + protected static List<String> readVirtualEnvRoots(@NotNull String envs) { + List<String> result = Lists.newArrayList(); + String[] roots = envs.split(File.pathSeparator); + for (String root : roots) { + File virtualEnvRoot = new File(root); + File[] virtualenvs = virtualEnvRoot.listFiles(); + if (virtualenvs != null) { + for (File f : virtualenvs) { + result.add(f.getAbsolutePath()); + } + } else { + LOG.error(root + " is not a directory of doesn't exist"); + } + } + + return result; + } + + public static List<String> loadEnvTags(String env) { + List<String> envTags; + + try { + File parent = new File(env); + if (parent.isFile()) { + parent = parent.getParentFile(); + } + envTags = com.intellij.openapi.util.io.FileUtil.loadLines(new File(parent, TAGS_FILE)); + } + catch (IOException e) { + envTags = Lists.newArrayList(); + } + return envTags; + } + + public static String joinStrings(Collection<String> roots, String rootsName) { + return roots.size() > 0 ? rootsName + StringUtil.join(roots, ", ") + "\n" : ""; + } +} + diff --git a/python/testSrc/com/jetbrains/env/PyExecutionFixtureTestTask.java b/python/testSrc/com/jetbrains/env/PyExecutionFixtureTestTask.java new file mode 100644 index 000000000000..bdb426dde301 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/PyExecutionFixtureTestTask.java @@ -0,0 +1,174 @@ +package com.jetbrains.env; + +import com.google.common.collect.Lists; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.ide.util.projectWizard.EmptyModuleBuilder; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.module.ModuleTypeManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.builders.ModuleFixtureBuilder; +import com.intellij.testFramework.fixtures.*; +import com.intellij.testFramework.fixtures.impl.ModuleFixtureBuilderImpl; +import com.intellij.testFramework.fixtures.impl.ModuleFixtureImpl; +import com.intellij.util.ui.UIUtil; +import com.jetbrains.python.PythonModuleTypeBase; +import com.jetbrains.python.PythonTestUtil; +import com.jetbrains.python.sdk.InvalidSdkException; +import com.jetbrains.python.sdkTools.PyTestSdkTools; +import com.jetbrains.python.sdkTools.SdkCreationType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Assert; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +/** + * @author traff + */ +public abstract class PyExecutionFixtureTestTask extends PyTestTask { + public static final int NORMAL_TIMEOUT = 30000; + public static final int LONG_TIMEOUT = 120000; + protected int myTimeout = NORMAL_TIMEOUT; + protected CodeInsightTestFixture myFixture; + + public Project getProject() { + return myFixture.getProject(); + } + + public void useNormalTimeout() { + myTimeout = NORMAL_TIMEOUT; + } + + public void useLongTimeout() { + myTimeout = LONG_TIMEOUT; + } + + public void setUp(final String testName) throws Exception { + initFixtureBuilder(); + + final TestFixtureBuilder<IdeaProjectTestFixture> fixtureBuilder = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder( + testName); + + myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(fixtureBuilder.getFixture()); + + UIUtil.invokeAndWaitIfNeeded( + new Runnable() { + @Override + public void run() { + ModuleFixtureBuilder moduleFixtureBuilder = fixtureBuilder.addModule(MyModuleFixtureBuilder.class); + moduleFixtureBuilder.addSourceContentRoot(myFixture.getTempDirPath()); + moduleFixtureBuilder.addSourceContentRoot(getTestDataPath()); + final List<String> contentRoots = getContentRoots(); + for (String contentRoot : contentRoots) { + moduleFixtureBuilder.addContentRoot(getTestDataPath() + contentRoot); + } + } + } + ); + + + myFixture.setUp(); + myFixture.setTestDataPath(getTestDataPath()); + } + + protected List<String> getContentRoots() { + return Lists.newArrayList(); + } + + protected String getTestDataPath() { + return PythonTestUtil.getTestDataPath(); + } + + protected void initFixtureBuilder() { + IdeaTestFixtureFactory.getFixtureFactory().registerFixtureBuilder(MyModuleFixtureBuilder.class, MyModuleFixtureBuilderImpl.class); + } + + public void tearDown() throws Exception { + if (myFixture != null) { + myFixture.tearDown(); + myFixture = null; + } + } + + @Nullable + protected LightProjectDescriptor getProjectDescriptor() { + return null; + } + + protected void disposeProcess(ProcessHandler h) throws InterruptedException { + h.destroyProcess(); + if (!waitFor(h)) { + new Throwable("Can't stop process").printStackTrace(); + } + } + + protected boolean waitFor(ProcessHandler p) throws InterruptedException { + return p.waitFor(myTimeout); + } + + protected boolean waitFor(@NotNull Semaphore s) throws InterruptedException { + return waitFor(s, myTimeout); + } + + protected static boolean waitFor(@NotNull Semaphore s, long timeout) throws InterruptedException { + return s.tryAcquire(timeout, TimeUnit.MILLISECONDS); + } + + public static class MyModuleFixtureBuilderImpl extends ModuleFixtureBuilderImpl<ModuleFixture> implements MyModuleFixtureBuilder { + public MyModuleFixtureBuilderImpl(TestFixtureBuilder<? extends IdeaProjectTestFixture> fixtureBuilder) { + super(new PlatformPythonModuleType(), fixtureBuilder); + } + + @Override + protected ModuleFixture instantiateFixture() { + return new ModuleFixtureImpl(this); + } + } + + public static class PlatformPythonModuleType extends PythonModuleTypeBase<EmptyModuleBuilder> { + @NotNull + public static PlatformPythonModuleType getInstance() { + return (PlatformPythonModuleType)ModuleTypeManager.getInstance().findByID(PYTHON_MODULE); + } + + + @NotNull + @Override + public EmptyModuleBuilder createModuleBuilder() { + return new EmptyModuleBuilder() { + @Override + public ModuleType getModuleType() { + return getInstance(); + } + }; + } + } + + + /** + * Creates SDK by its path + * + * @param sdkHome path to sdk (probably obtained by {@link #runTestOn(String)}) + * @param sdkCreationType SDK creation strategy (see {@link com.jetbrains.python.sdkTools.SdkCreationType} doc) + * @return sdk + */ + @NotNull + protected Sdk createTempSdk(@NotNull final String sdkHome, @NotNull final SdkCreationType sdkCreationType) + throws InvalidSdkException, IOException { + final VirtualFile sdkHomeFile = LocalFileSystem.getInstance().findFileByPath(sdkHome); + Assert.assertNotNull("Interpreter file not found: " + sdkHome, sdkHomeFile); + return PyTestSdkTools.createTempSdk(sdkHomeFile, sdkCreationType, myFixture.getModule()); + } + + + public interface MyModuleFixtureBuilder extends ModuleFixtureBuilder<ModuleFixture> { + + } +} diff --git a/python/testSrc/com/jetbrains/env/PyTestTask.java b/python/testSrc/com/jetbrains/env/PyTestTask.java new file mode 100644 index 000000000000..bfc1a688ebfa --- /dev/null +++ b/python/testSrc/com/jetbrains/env/PyTestTask.java @@ -0,0 +1,81 @@ +package com.jetbrains.env; + +import com.google.common.collect.Sets; +import com.intellij.openapi.util.io.FileUtil; + +import java.util.Set; + +/** + * @author traff + */ +public abstract class PyTestTask { + private String myWorkingFolder; + private String myScriptName; + private String myScriptParameters; + + public void setWorkingFolder(String workingFolder) { + myWorkingFolder = workingFolder; + } + + public void setScriptName(String scriptName) { + myScriptName = scriptName; + } + + public void setScriptParameters(String scriptParameters) { + myScriptParameters = scriptParameters; + } + + public void setUp(String testName) throws Exception { + } + + public void tearDown() throws Exception { + } + + /** + * Run test on certain SDK path. + * To create SDK from path, use {@link PyExecutionFixtureTestTask#createTempSdk(String, com.jetbrains.python.sdkTools.SdkCreationType)} + * + * @param sdkHome sdk path + */ + public abstract void runTestOn(String sdkHome) throws Exception; + + public void before() throws Exception { + } + + public void testing() throws Exception { + } + + public void after() throws Exception { + } + + public void useNormalTimeout() { + } + + public void useLongTimeout() { + } + + public String getScriptName() { + return myScriptName; + } + + public String getScriptPath() { + return getFilePath(myScriptName); + } + + public String getFilePath(String scriptName) { + return FileUtil + .toSystemDependentName(myWorkingFolder.endsWith("/") ? myWorkingFolder + scriptName : myWorkingFolder + "/" + scriptName); + } + + public String getScriptParameters() { + return myScriptParameters; + } + + public String getWorkingFolder() { + return myWorkingFolder; + } + + public Set<String> getTags() { + return Sets.newHashSet(); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/IPythonConsoleTest.java b/python/testSrc/com/jetbrains/env/python/IPythonConsoleTest.java new file mode 100644 index 000000000000..a039344451d4 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/IPythonConsoleTest.java @@ -0,0 +1,100 @@ +package com.jetbrains.env.python; + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableSet; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiErrorElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.python.console.PyConsoleTask; +import org.hamcrest.Matchers; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +/** + * @author traff + */ +public class IPythonConsoleTest extends PyEnvTestCase { + public void testQuestion() throws Exception { + runPythonTest(new IPythonTask() { + @Override + public void testing() throws Exception { + exec("import multiprocessing"); + exec("multiprocessing?"); + waitForOutput("Type:", "module"); + } + }); + } + + public void testParsing() throws Exception { + runPythonTest(new IPythonTask() { + @Override + public void testing() throws Exception { + waitForReady(); + addTextToEditor("sys?"); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + PsiFile psi = + PsiDocumentManager.getInstance(getProject()) + .getPsiFile(getConsoleView().getLanguageConsole().getConsoleEditor().getDocument()); + Assert.assertThat("No errors expected", getErrors(psi), Matchers.empty()); + } + }); + } + }); + } + + @NotNull + private static Collection<String> getErrors(PsiFile psi) { //TODO: NotNull? + if (!PsiTreeUtil.hasErrorElements(psi)) { + return Collections.emptyList(); + } + + return Collections2.transform(PsiTreeUtil.findChildrenOfType(psi, PsiErrorElement.class), new Function<PsiErrorElement, String>() { + @Override + public String apply(PsiErrorElement input) { + return input.getErrorDescription(); + } + }); + } + + public void testParsingNoIPython() throws Exception { + runPythonTest(new IPythonTask() { + @Override + public void testing() throws Exception { + waitForReady(); + addTextToEditor("sys?"); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + PsiFile psi = + PsiDocumentManager.getInstance(getProject()).getPsiFile(getConsoleView().getLanguageConsole().getConsoleEditor().getDocument()); + //TreeUtil.ensureParsed(psi.getNode()); + assertTrue(PsiTreeUtil.hasErrorElements(psi)); + } + }); + + } + + @Override + public Set<String> getTags() { + return ImmutableSet.of("-ipython"); + } + }); + } + + private static class IPythonTask extends PyConsoleTask { + @Override + public Set<String> getTags() { + return ImmutableSet.of("ipython"); + } + } +} diff --git a/python/testSrc/com/jetbrains/env/python/PyPackageRequirementsInspectionTest.java b/python/testSrc/com/jetbrains/env/python/PyPackageRequirementsInspectionTest.java new file mode 100644 index 000000000000..8e3b75dd56a0 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/PyPackageRequirementsInspectionTest.java @@ -0,0 +1,83 @@ +package com.jetbrains.env.python; + +import com.google.common.collect.ImmutableSet; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ModuleRootModificationUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.PsiTestUtil; +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.PyExecutionFixtureTestTask; +import com.jetbrains.python.PythonTestUtil; +import com.jetbrains.python.inspections.PyPackageRequirementsInspection; +import com.jetbrains.python.sdkTools.SdkCreationType; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +/** + * @author vlan + */ +public class PyPackageRequirementsInspectionTest extends PyEnvTestCase { + public static final ImmutableSet<String> TAGS = ImmutableSet.of("requirements"); + + public void testPartiallySatisfiedRequirementsTxt() { + doTest("test1.py"); + } + + public void testPartiallySatisfiedSetupPy() { + doTest("test1.py"); + } + + public void testImportsNotInRequirementsTxt() { + doTest("test1.py"); + } + + public void testDuplicateInstallAndTests() { + doTest("test1.py"); + } + + private void doTest(@NotNull final String filename) { + final String dir = getTestName(false); + runPythonTest(new PyExecutionFixtureTestTask() { + @Override + protected String getTestDataPath() { + return PythonTestUtil.getTestDataPath() + "/inspections/PyPackageRequirementsInspection"; + } + + @Override + public void runTestOn(String sdkHome) throws Exception { + myFixture.enableInspections(PyPackageRequirementsInspection.class); + final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.SDK_PACKAGES_ONLY); + final String perSdkDir = Integer.toHexString(System.identityHashCode(sdk)); + final VirtualFile root = myFixture.copyDirectoryToProject(dir, perSdkDir); + assertNotNull(root); + final Module module = myFixture.getModule(); + setupModuleSdk(module, sdk, root); + try { + final VirtualFile file = root.findFileByRelativePath(filename); + assertNotNull(file); + edt(new Runnable() { + @Override + public void run() { + myFixture.testHighlighting(true, true, true, file); + } + }); + } + finally { + PsiTestUtil.removeAllRoots(module, sdk); + } + } + + @Override + public Set<String> getTags() { + return TAGS; + } + }); + } + + private static void setupModuleSdk(@NotNull Module module, @NotNull Sdk sdk, @NotNull VirtualFile root) { + ModuleRootModificationUtil.setModuleSdk(module, sdk); + PsiTestUtil.addContentRoot(module, root); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java b/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java new file mode 100644 index 000000000000..54afb702322b --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java @@ -0,0 +1,164 @@ +package com.jetbrains.env.python; + +import com.google.common.collect.Sets; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.testFramework.UsefulTestCase; +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.PyExecutionFixtureTestTask; +import com.jetbrains.env.PyTestTask; +import com.jetbrains.python.packaging.*; +import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.sdk.PythonSdkType; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor; +import com.jetbrains.python.sdkTools.SdkCreationType; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * @author vlan + */ +public class PyPackagingTest extends PyEnvTestCase { + @Override + public void runPythonTest(PyTestTask testTask) { + if (UsefulTestCase.IS_UNDER_TEAMCITY && SystemInfo.isWindows) { + return; //Don't run under Windows as after deleting from created virtualenvs original interpreter got spoiled + } + + super.runPythonTest(testTask); + } + + public void testGetPackages() { + runPythonTest(new PyPackagingTestTask() { + @Override + public void runTestOn(String sdkHome) throws Exception { + final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK); + List<PyPackage> packages = null; + try { + packages = ((PyPackageManagerImpl)PyPackageManager.getInstance(sdk)).getPackages(); + } + catch (PyExternalProcessException e) { + final int retcode = e.getRetcode(); + if (retcode != PyPackageManagerImpl.ERROR_NO_PIP && retcode != PyPackageManagerImpl.ERROR_NO_SETUPTOOLS) { + fail(String.format("Error for interpreter '%s': %s", sdk.getHomePath(), e.getMessage())); + } + } + if (packages != null) { + assertTrue(packages.size() > 0); + for (PyPackage pkg : packages) { + assertTrue(pkg.getName().length() > 0); + } + } + } + }); + } + + public void testCreateVirtualEnv() { + runPythonTest(new PyPackagingTestTask() { + @Override + public void runTestOn(String sdkHome) throws Exception { + final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK); + try { + final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk); + // virtualenv >= 0.10 supports Python >= 2.6 + if (languageLevel.isOlderThan(LanguageLevel.PYTHON26)) { + return; + } + final File tempDir = FileUtil.createTempDirectory(getTestName(false), null); + final File venvDir = new File(tempDir, "venv"); + final String venvSdkHome = ((PyPackageManagerImpl)PyPackageManagerImpl.getInstance(sdk)).createVirtualEnv(venvDir.toString(), + false); + final Sdk venvSdk = createTempSdk(venvSdkHome, SdkCreationType.EMPTY_SDK); + assertNotNull(venvSdk); + assertTrue(PythonSdkType.isVirtualEnv(venvSdk)); + assertInstanceOf(PythonSdkFlavor.getPlatformIndependentFlavor(venvSdk.getHomePath()), VirtualEnvSdkFlavor.class); + final List<PyPackage> packages = ((PyPackageManagerImpl)PyPackageManagerImpl.getInstance(venvSdk)).getPackages(); + final PyPackage setuptools = findPackage("setuptools", packages); + assertNotNull(setuptools); + assertEquals("setuptools", setuptools.getName()); + assertEquals(PyPackageManagerImpl.SETUPTOOLS_VERSION, setuptools.getVersion()); + final PyPackage pip = findPackage("pip", packages); + assertNotNull(pip); + assertEquals("pip", pip.getName()); + assertEquals(PyPackageManagerImpl.PIP_VERSION, pip.getVersion()); + } + catch (IOException e) { + throw new RuntimeException(e); + } + catch (PyExternalProcessException e) { + throw new RuntimeException(String.format("Error for interpreter '%s': %s", sdk.getHomePath(), e.getMessage()), e); + } + } + }); + } + + public void testInstallPackage() { + runPythonTest(new PyPackagingTestTask() { + + @Override + public void runTestOn(String sdkHome) throws Exception { + final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK); + try { + final File tempDir = FileUtil.createTempDirectory(getTestName(false), null); + final File venvDir = new File(tempDir, "venv"); + final String venvSdkHome = ((PyPackageManagerImpl)PyPackageManager.getInstance(sdk)).createVirtualEnv(venvDir.getPath(), false); + final Sdk venvSdk = createTempSdk(venvSdkHome, SdkCreationType.EMPTY_SDK); + assertNotNull(venvSdk); + final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManager.getInstance(venvSdk); + final List<PyPackage> packages1 = manager.getPackages(); + // TODO: Install Markdown from a local file + manager.install(list(PyRequirement.fromString("Markdown<2.2"), + new PyRequirement("httplib2")), Collections.<String>emptyList()); + final List<PyPackage> packages2 = manager.getPackages(); + final PyPackage markdown2 = findPackage("Markdown", packages2); + assertNotNull(markdown2); + assertTrue(markdown2.isInstalled()); + final PyPackage pip1 = findPackage("pip", packages1); + assertNotNull(pip1); + assertEquals("pip", pip1.getName()); + assertEquals(PyPackageManagerImpl.PIP_VERSION, pip1.getVersion()); + manager.uninstall(list(pip1)); + final List<PyPackage> packages3 = manager.getPackages(); + final PyPackage pip2 = findPackage("pip", packages3); + assertNull(pip2); + } + catch (PyExternalProcessException e) { + new RuntimeException(String.format("Error for interpreter '%s': %s", sdk.getHomePath(), e.getMessage()), e); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } + + @Nullable + private static PyPackage findPackage(String name, List<PyPackage> packages) { + for (PyPackage pkg : packages) { + if (name.equals(pkg.getName())) { + return pkg; + } + } + return null; + } + + private static <T> List<T> list(T... xs) { + return Arrays.asList(xs); + } + + + private abstract static class PyPackagingTestTask extends PyExecutionFixtureTestTask { + @Override + public Set<String> getTags() { + return Sets.newHashSet("packaging"); + } + } +} diff --git a/python/testSrc/com/jetbrains/env/python/PythonConsoleTest.java b/python/testSrc/com/jetbrains/env/python/PythonConsoleTest.java new file mode 100644 index 000000000000..4134b1697851 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/PythonConsoleTest.java @@ -0,0 +1,130 @@ +package com.jetbrains.env.python; + +import com.google.common.collect.Sets; +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.python.console.PyConsoleTask; +import org.junit.Assert; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * @author traff + */ +public class PythonConsoleTest extends PyEnvTestCase { + public void testConsolePrint() throws Exception { + runPythonTest(new PyConsoleTask() { + @Override + public void testing() throws Exception { + exec("x = 96"); + exec("x += 1"); + exec("print(1)"); + exec("print(x)"); + waitForOutput("97"); + } + }); + } + + public void testExecuteMultiline() throws Exception { //PY-4329 + runPythonTest(new PyConsoleTask() { + @Override + public void testing() throws Exception { + exec("if True:\n" + + " x=1\n" + + "y=x+100\n" + + "for i in range(1):\n" + + " print(y)\n"); + waitForOutput("101"); + } + + @Override + public Set<String> getTags() { + return Sets.newHashSet("-jython"); //jython doesn't support multiline execution: http://bugs.jython.org/issue2106 + } + }); + } + + public void testInterruptAsync() throws Exception { + runPythonTest(new PyConsoleTask() { + @Override + public void testing() throws Exception { + exec("import time"); + execNoWait("for i in range(10000):\n" + + " print(i)\n" + + " time.sleep(0.1)"); + waitForOutput("3\n4\n5"); + Assert.assertFalse(canExecuteNow()); + interrupt(); + waitForFinish(); + waitForReady(); + } + + @Override + public Set<String> getTags() { + return Sets.newHashSet("-iron", "-jython"); + } + }); + } + + public void testLineByLineInput() throws Exception { + runPythonTest(new PyConsoleTask() { + @Override + public void testing() throws Exception { + exec("x = 96"); + exec("x +=1"); + exec("if True:"); + exec(" print(x)"); + exec(""); + exec(""); + waitForOutput("97"); + } + }); + } + + + public void testVariablesView() throws Exception { + runPythonTest(new PyConsoleTask() { + @Override + public void testing() throws Exception { + exec("x = 1"); + exec("print(x)"); + waitForOutput("1"); + + assertTrue("Variable has wrong value", + hasValue("x", "1")); + } + }); + } + + public void testCompoundVariable() throws Exception { + runPythonTest(new PyConsoleTask() { + @Override + public void testing() throws Exception { + exec("x = [1, 2, 3]"); + exec("print(x)"); + waitForOutput("[1, 2, 3]"); + + List<String> values = getCompoundValueChildren(getValue("x")); + Collections.sort(values); + assertContainsElements(values, "1", "2", "3", "3"); + } + }); + } + + public void testChangeVariable() throws Exception { + runPythonTest(new PyConsoleTask() { + @Override + public void testing() throws Exception { + exec("x = 1"); + exec("print(x)"); + waitForOutput("1"); + + setValue("x", "2"); + + assertTrue("Variable has wrong value", + hasValue("x", "2")); + } + }); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java new file mode 100644 index 000000000000..4372715371eb --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java @@ -0,0 +1,579 @@ +package com.jetbrains.env.python; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; +import com.intellij.xdebugger.XDebuggerTestUtil; +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.python.debug.PyDebuggerTask; +import com.jetbrains.env.ut.PyUnitTestTask; +import com.jetbrains.python.PythonHelpersLocator; +import com.jetbrains.python.console.pydev.PydevCompletionVariant; +import com.jetbrains.python.debugger.PyDebuggerException; +import com.jetbrains.python.debugger.PyExceptionBreakpointProperties; +import com.jetbrains.python.debugger.PyExceptionBreakpointType; +import com.jetbrains.python.debugger.pydev.ProcessDebugger; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; + +import java.util.List; +import java.util.Set; + +/** + * @author traff + */ + +public class PythonDebuggerTest extends PyEnvTestCase { + public void testBreakpointStopAndEval() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test1.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 3); + } + + @Override + public void testing() throws Exception { + waitForPause(); + + eval("i").hasValue("0"); + + resume(); + + waitForPause(); + + eval("i").hasValue("1"); + + resume(); + + waitForPause(); + + eval("i").hasValue("2"); + } + }); + } + + public void testDebugger() { + runPythonTest(new PyUnitTestTask("", "test_debug.py") { + @Override + protected String getTestDataPath() { + return PythonHelpersLocator.getPythonCommunityPath() + "/helpers/pydev"; + } + + @Override + public void after() { + allTestsPassed(); + } + }); + } + + public void testConditionalBreakpoint() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test1.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 3); + XDebuggerTestUtil.setBreakpointCondition(getProject(), 3, "i == 1 or i == 11 or i == 111"); + } + + @Override + public void testing() throws Exception { + waitForPause(); + + eval("i").hasValue("1"); + + resume(); + + waitForPause(); + + eval("i").hasValue("11"); + + resume(); + + waitForPause(); + + eval("i").hasValue("111"); + } + }); + } + + public void testDebugConsole() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test1.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 3); + } + + @Override + public void testing() throws Exception { + waitForPause(); + + eval("i").hasValue("0"); + + resume(); + + waitForPause(); + + consoleExec("'i=%d'%i"); + + waitForOutput("'i=1'"); + + consoleExec("x"); + + waitForOutput("name 'x' is not defined"); + + consoleExec("1-;"); + + waitForOutput("SyntaxError"); + + resume(); + } + + private void consoleExec(String command) { + myDebugProcess.consoleExec(command, new ProcessDebugger.DebugCallback<String>() { + @Override + public void ok(String value) { + + } + + @Override + public void error(PyDebuggerException exception) { + } + }); + } + }); + } + + + public void testDebugCompletion() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test4.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 3); + } + + @Override + public void testing() throws Exception { + waitForPause(); + + List<PydevCompletionVariant> list = myDebugProcess.getCompletions("xvalu"); + assertEquals(2, list.size()); + } + }); + } + + public void testBreakpointLogExpression() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test1.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 3); + XDebuggerTestUtil.setBreakpointLogExpression(getProject(), 3, "'i = %d'%i"); + } + + @Override + public void testing() throws Exception { + waitForPause(); + resume(); + waitForOutput("i = 1"); + } + }); + } + + public void testStepOver() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test2.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 5); + } + + @Override + public void testing() throws Exception { + waitForPause(); + stepOver(); + waitForPause(); + stepOver(); + waitForPause(); + eval("z").hasValue("2"); + } + }); + } + + public void testStepInto() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test2.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 5); + } + + @Override + public void testing() throws Exception { + waitForPause(); + stepInto(); + waitForPause(); + eval("x").hasValue("1"); + stepOver(); + waitForPause(); + eval("y").hasValue("3"); + stepOver(); + waitForPause(); + eval("z").hasValue("1"); + } + }); + } + + public void testSmartStepInto() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test3.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 14); + } + + @Override + public void testing() throws Exception { + waitForPause(); + smartStepInto("foo"); + waitForPause(); + stepOver(); + waitForPause(); + eval("y").hasValue("4"); + } + }); + } + + public void testSmartStepInto2() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test3.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 18); + toggleBreakpoint(getScriptPath(), 25); + } + + @Override + public void testing() throws Exception { + waitForPause(); + toggleBreakpoint(getScriptPath(), 18); + smartStepInto("foo"); + waitForPause(); + eval("a.z").hasValue("1"); + } + }); + } + + public void testInput() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test_input.py") { + @Override + public void before() throws Exception { + } + + @Override + public void testing() throws Exception { + waitForOutput("print command >"); + input("GO!"); + waitForOutput("command was GO!"); + } + + @Override + public Set<String> getTags() { + return ImmutableSet.of("-jython"); //can't run on jython + } + }); + } + + public void testRunToLine() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test_runtoline.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 1); + toggleBreakpoint(getScriptPath(), 7); + } + + @Override + public void testing() throws Exception { + waitForPause(); + eval("x").hasValue("0"); + runToLine(4); + eval("x").hasValue("1"); + resume(); + waitForPause(); + eval("x").hasValue("12"); + resume(); + + waitForOutput("x = 12"); + } + }); + } + + private static void addExceptionBreakpoint(IdeaProjectTestFixture fixture, PyExceptionBreakpointProperties properties) { + XDebuggerTestUtil.addBreakpoint(fixture.getProject(), PyExceptionBreakpointType.class, properties); + } + + public void testExceptionBreakpointOnTerminate() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test_exceptbreak.py") { + @Override + public void before() throws Exception { + createExceptionBreak(myFixture, true, false, false); + } + + @Override + public void testing() throws Exception { + waitForPause(); + eval("__exception__[0].__name__").hasValue("'ZeroDivisionError'"); + resume(); + waitForTerminate(); + } + + @Override + public Set<String> getTags() { + return ImmutableSet.of("-iron"); + } + }); + } + + private static void createExceptionBreak(IdeaProjectTestFixture fixture, + boolean notifyOnTerminate, + boolean notifyAlways, + boolean notifyOnFirst) { + XDebuggerTestUtil.removeAllBreakpoints(fixture.getProject()); + XDebuggerTestUtil.setDefaultBreakpointEnabled(fixture.getProject(), PyExceptionBreakpointType.class, false); + + PyExceptionBreakpointProperties properties = new PyExceptionBreakpointProperties("exceptions.ZeroDivisionError"); + properties.setNotifyOnTerminate(notifyOnTerminate); + properties.setNotifyAlways(notifyAlways); + properties.setNotifyOnlyOnFirst(notifyOnFirst); + addExceptionBreakpoint(fixture, properties); + properties = new PyExceptionBreakpointProperties("builtins.ZeroDivisionError"); //for python 3 + properties.setNotifyOnTerminate(notifyOnTerminate); + properties.setNotifyAlways(notifyAlways); + properties.setNotifyOnlyOnFirst(notifyOnFirst); + addExceptionBreakpoint(fixture, properties); + } + + public void testExceptionBreakpointAlways() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test_exceptbreak.py") { + @Override + public void before() throws Exception { + createExceptionBreak(myFixture, false, true, false); + } + + @Override + public void testing() throws Exception { + waitForPause(); + eval("__exception__[0].__name__").hasValue("'ZeroDivisionError'"); + resume(); + waitForPause(); + resume(); + waitForPause(); + resume(); + waitForTerminate(); + } + + @Override + public Set<String> getTags() { + return ImmutableSet.of("-pypy"); //TODO: fix it for Pypy + } + }); + } + + public void testExceptionBreakpointOnFirstRaise() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test_exceptbreak.py") { + @Override + public void before() throws Exception { + createExceptionBreak(myFixture, false, false, true); + } + + @Override + public void testing() throws Exception { + waitForPause(); + eval("__exception__[0].__name__").hasValue("'ZeroDivisionError'"); + resume(); + waitForTerminate(); + } + + @Override + public Set<String> getTags() { + return ImmutableSet.of("-iron"); + } + }); + } + + public void testMultithreading() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test_multithread.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 9); + toggleBreakpoint(getScriptPath(), 13); + } + + @Override + public void testing() throws Exception { + waitForPause(); + eval("y").hasValue("2"); + resume(); + waitForPause(); + eval("z").hasValue("102"); + resume(); + } + + @Override + public Set<String> getTags() { + return ImmutableSet.of("-pypy"); //TODO: fix that for PyPy + } + }); + } + + public void testEggDebug() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test_egg.py") { + @Override + public void before() throws Exception { + String egg = getFilePath("Adder-0.1.egg"); + toggleBreakpointInEgg(egg, "adder/adder.py", 2); + PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(getRunConfiguration().getSdkHome()); + if (flavor != null) { + flavor.initPythonPath(Lists.newArrayList(egg), getRunConfiguration().getEnvs()); + } + else { + getRunConfiguration().getEnvs().put("PYTHONPATH", egg); + } + } + + @Override + public void testing() throws Exception { + waitForPause(); + eval("ret").hasValue("16"); + resume(); + } + + @Override + public Set<String> getTags() { + return ImmutableSet.of("-jython"); //TODO: fix that for Jython if anybody needs it + } + }); + } + + public void testStepOverConditionalBreakpoint() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test_stepOverCondition.py") { + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 1); + toggleBreakpoint(getScriptPath(), 2); + XDebuggerTestUtil.setBreakpointCondition(getProject(), 2, "y == 3"); + } + + @Override + public void testing() throws Exception { + waitForPause(); + stepOver(); + waitForPause(); + eval("y").hasValue("2"); + } + }); + } + + public void testMultiprocess() throws Exception { + runPythonTest(new PyDebuggerTask("/debug", "test_multiprocess.py") { + @Override + protected void init() { + setMultiprocessDebug(true); + } + + @Override + public void before() throws Exception { + toggleBreakpoint(getScriptPath(), 9); + } + + @Override + public void testing() throws Exception { + waitForPause(); + + eval("i").hasValue("'Result:OK'"); + + resume(); + + waitForOutput("Result:OK"); + } + + @Override + public Set<String> getTags() { + return Sets.newHashSet("python3"); + } + }); + } + + + //TODO: fix me as I don't work properly sometimes (something connected with process termination on agent) + //public void testResume() throws Exception { + // runPythonTest(new PyDebuggerTask("/debug", "Test_Resume.py") { + // @Override + // public void before() throws Exception { + // toggleBreakpoint(getScriptPath(), 2); + // } + // + // @Override + // public void testing() throws Exception { + // waitForPause(); + // eval("x").hasValue("1"); + // resume(); + // waitForPause(); + // eval("x").hasValue("2"); + // resume(); + // } + // }); + //} + + + //TODO: first fix strange hanging of that test + //public void testRemoteDebug() throws Exception { + // runPythonTest(new PyRemoteDebuggerTask("/debug", "test_remote.py") { + // @Override + // public void before() throws Exception { + // } + // + // @Override + // public void testing() throws Exception { + // waitForPause(); + // eval("x").hasValue("0"); + // stepOver(); + // waitForPause(); + // eval("x").hasValue("1"); + // stepOver(); + // waitForPause(); + // eval("x").hasValue("2"); + // resume(); + // } + // + // @Override + // protected void checkOutput(ProcessOutput output) { + // assertEmpty(output.getStderr()); + // assertEquals("OK", output.getStdout().trim()); + // } + // + // @Override + // public void after() throws Exception { + // stopDebugServer(); + // } + // }); + //} + + //TODO: That doesn't work now: case from test_continuation.py and test_continuation2.py are treated differently by interpreter + // (first line is executed in first case and last line in second) + + //public void testBreakOnContinuationLine() throws Exception { + // runPythonTest(new PyDebuggerTask("/debug", "test_continuation.py") { + // @Override + // public void before() throws Exception { + // toggleBreakpoint(getScriptPath(), 13); + // } + // + // @Override + // public void testing() throws Exception { + // waitForPause(); + // eval("x").hasValue("0"); + // stepOver(); + // waitForPause(); + // eval("x").hasValue("1"); + // stepOver(); + // waitForPause(); + // eval("x").hasValue("2"); + // } + // }); + //} +} + diff --git a/python/testSrc/com/jetbrains/env/python/PythonGeneratorTest.java b/python/testSrc/com/jetbrains/env/python/PythonGeneratorTest.java new file mode 100644 index 000000000000..273cb3f94011 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/PythonGeneratorTest.java @@ -0,0 +1,24 @@ +package com.jetbrains.env.python; + +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.ut.PyUnitTestTask; +import com.jetbrains.python.PythonHelpersLocator; + +/** + * @author traff + */ +public class PythonGeneratorTest extends PyEnvTestCase{ + public void testGenerator() { + runPythonTest(new PyUnitTestTask("", "test_generator.py") { + @Override + protected String getTestDataPath() { + return PythonHelpersLocator.getPythonCommunityPath() + "/helpers"; + } + + @Override + public void after() { + allTestsPassed(); + } + }); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/PythonSkeletonsTest.java b/python/testSrc/com/jetbrains/env/python/PythonSkeletonsTest.java new file mode 100644 index 000000000000..c6b150f31359 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/PythonSkeletonsTest.java @@ -0,0 +1,202 @@ +package com.jetbrains.env.python; + +import com.google.common.collect.ImmutableSet; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.PyExecutionFixtureTestTask; +import com.jetbrains.env.PyTestTask; +import com.jetbrains.python.PythonFileType; +import com.jetbrains.python.PythonTestUtil; +import com.jetbrains.python.documentation.PythonDocumentationProvider; +import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyBuiltinCache; +import com.jetbrains.python.psi.resolve.PythonSdkPathCache; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.sdk.PythonSdkType; +import com.jetbrains.python.sdk.skeletons.PySkeletonRefresher; +import com.jetbrains.python.sdk.skeletons.SkeletonVersionChecker; +import com.jetbrains.python.sdkTools.SdkCreationType; +import com.jetbrains.python.toolbox.Maybe; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.Set; + +/** + * Heavyweight integration tests of skeletons of Python binary modules. + * <p/> + * An environment test environment must have a 'skeletons' tag in order to be compatible with this test case. No specific packages are + * required currently. Both Python 2 and Python 3 are OK. All platforms are OK. At least one Python 2.6+ environment is required. + * + * @author vlan + */ +public class PythonSkeletonsTest extends PyEnvTestCase { + public static final ImmutableSet<String> TAGS = ImmutableSet.of("skeletons"); + + public void testBuiltins() { + runTest(new SkeletonsTask() { + @Override + public void runTestOn(@NotNull Sdk sdk) { + // Check the builtin skeleton header + final Project project = myFixture.getProject(); + final PyFile builtins = PyBuiltinCache.getBuiltinsForSdk(project, sdk); + assertNotNull(builtins); + final VirtualFile virtualFile = builtins.getVirtualFile(); + assertNotNull(virtualFile); + assertTrue(virtualFile.isInLocalFileSystem()); + final String path = virtualFile.getPath(); + final PySkeletonRefresher.SkeletonHeader header = PySkeletonRefresher.readSkeletonHeader(new File(path)); + assertNotNull(header); + final int version = header.getVersion(); + assertTrue("Header version must be > 0, currently it is " + version, version > 0); + assertEquals(SkeletonVersionChecker.BUILTIN_NAME, header.getBinaryFile()); + + // Run inspections on a file that uses builtins + myFixture.configureByFile(getTestName(false) + ".py"); + + + PsiFile expr = myFixture.getFile(); + + final Module module = ModuleUtil.findModuleForPsiElement(expr); + + final Sdk sdkFromModule = PythonSdkType.findPythonSdk(module); + assertNotNull(sdkFromModule); + + final Sdk sdkFromPsi = PyBuiltinCache.findSdkForFile(expr.getContainingFile()); + final PyFile builtinsFromSdkCache = PythonSdkPathCache.getInstance(project, sdkFromPsi).getBuiltins().getBuiltinsFile(); + assertNotNull(builtinsFromSdkCache); + assertEquals(builtins, builtinsFromSdkCache); + + final PyFile builtinsFromPsi = PyBuiltinCache.getInstance(expr).getBuiltinsFile(); + assertNotNull(builtinsFromPsi); + assertEquals(builtins, builtinsFromPsi); + + myFixture.enableInspections(PyUnresolvedReferencesInspection.class); + edt(new Runnable() { + @Override + public void run() { + myFixture.checkHighlighting(true, false, false); + } + }); + } + }); + } + + // PY-4349 + public void testFakeNamedTuple() { + runTest(new SkeletonsTask() { + @Override + protected void runTestOn(@NotNull Sdk sdk) { + final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk); + // Named tuples have been introduced in Python 2.6 + if (languageLevel.isOlderThan(LanguageLevel.PYTHON26)) { + return; + } + + // XXX: A workaround for invalidating VFS cache with the test file copied to our temp directory + LocalFileSystem.getInstance().refresh(false); + + // Run inspections on code that uses named tuples + myFixture.configureByFile(getTestName(false) + ".py"); + myFixture.enableInspections(PyUnresolvedReferencesInspection.class); + + edt(new Runnable() { + @Override + public void run() { + myFixture.checkHighlighting(true, false, false); + } + }); + } + }); + } + + public void testKnownPropertiesTypes() { + runTest(new SkeletonsTask() { + @Override + protected void runTestOn(@NotNull Sdk sdk) { + myFixture.configureByText(PythonFileType.INSTANCE, + "expr = slice(1, 2).start\n"); + final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class); + final TypeEvalContext context = TypeEvalContext.codeAnalysis(myFixture.getFile()); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + final PyType type = context.getType(expr); + final String actualType = PythonDocumentationProvider.getTypeName(type, context); + assertEquals("int", actualType); + } + }); + } + }); + } + + // PY-9797 + public void testReadWriteDeletePropertyDefault() { + runTest(new SkeletonsTask() { + @Override + protected void runTestOn(@NotNull Sdk sdk) { + final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk); + // We rely on int.real property that is not explicitly annotated in the skeletons generator + if (languageLevel.isOlderThan(LanguageLevel.PYTHON26)) { + return; + } + final Project project = myFixture.getProject(); + final PyFile builtins = PyBuiltinCache.getBuiltinsForSdk(project, sdk); + assertNotNull(builtins); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + final PyClass cls = builtins.findTopLevelClass("int"); + assertNotNull(cls); + final Property prop = cls.findProperty("real", true); + assertNotNull(prop); + assertIsNotNull(prop.getGetter()); + assertIsNotNull(prop.getSetter()); + assertIsNotNull(prop.getDeleter()); + } + }); + } + + private void assertIsNotNull(Maybe<Callable> accessor) { + if (accessor.isDefined()) { + assertNotNull(accessor.valueOrNull()); + } + } + }); + } + + + private void runTest(@NotNull PyTestTask task) { + runPythonTest(task); + } + + + private abstract class SkeletonsTask extends PyExecutionFixtureTestTask { + @Override + protected String getTestDataPath() { + return PythonTestUtil.getTestDataPath() + "/skeletons/"; + } + + @Override + public void runTestOn(String sdkHome) throws Exception { + final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.SDK_PACKAGES_AND_SKELETONS); + runTestOn(sdk); + } + + @Override + public Set<String> getTags() { + return TAGS; + } + + protected abstract void runTestOn(@NotNull Sdk sdk); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/console/PyConsoleTask.java b/python/testSrc/com/jetbrains/env/python/console/PyConsoleTask.java new file mode 100644 index 000000000000..9a242934042d --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/console/PyConsoleTask.java @@ -0,0 +1,380 @@ +package com.jetbrains.env.python.console; + +import com.google.common.collect.Lists; +import com.intellij.execution.console.LanguageConsoleView; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.util.ui.UIUtil; +import com.intellij.xdebugger.frame.XValueChildrenList; +import com.jetbrains.env.PyExecutionFixtureTestTask; +import com.jetbrains.python.console.*; +import com.jetbrains.python.console.pydev.ConsoleCommunicationListener; +import com.jetbrains.python.debugger.PyDebugValue; +import com.jetbrains.python.debugger.PyDebuggerException; +import com.jetbrains.python.sdkTools.SdkCreationType; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; + +import java.util.List; +import java.util.concurrent.Semaphore; + +/** + * @author traff + */ +public class PyConsoleTask extends PyExecutionFixtureTestTask { + private boolean myProcessCanTerminate; + + protected PyConsoleProcessHandler myProcessHandler; + protected PydevConsoleCommunication myCommunication; + + private boolean shouldPrintOutput = false; + private PythonConsoleView myConsoleView; + private Semaphore mySemaphore; + private Semaphore mySemaphore0; + private PydevConsoleExecuteActionHandler myExecuteHandler; + + public PyConsoleTask() { + setWorkingFolder(getTestDataPath()); + } + + public PythonConsoleView getConsoleView() { + return myConsoleView; + } + + @Override + public void setUp(final String testName) throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + if (myFixture == null) { + PyConsoleTask.super.setUp(testName); + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @NotNull + protected String output() { + return myConsoleView.getConsole().getHistoryViewer().getDocument().getText(); + } + + public void setProcessCanTerminate(boolean processCanTerminate) { + myProcessCanTerminate = processCanTerminate; + } + + @Override + public void tearDown() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + if (myConsoleView != null) { + disposeConsole(); + } + PyConsoleTask.super.tearDown(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + private void disposeConsole() throws InterruptedException { + if (myCommunication != null) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + myCommunication.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + myCommunication = null; + } + }); + } + + disposeConsoleProcess(); + + if (myConsoleView != null) { + new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + Disposer.dispose(myConsoleView); + myConsoleView = null; + } + }.execute(); + } + } + + @Override + public void runTestOn(final String sdkHome) throws Exception { + final Project project = getProject(); + + final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK); + + setProcessCanTerminate(false); + + PydevConsoleRunner consoleRunner = PydevConsoleRunner.create(project, sdk, PyConsoleType.PYTHON, getWorkingFolder()); + before(); + + mySemaphore0 = new Semaphore(0); + + consoleRunner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() { + @Override + public void handleConsoleInitialized(LanguageConsoleView consoleView) { + mySemaphore0.release(); + } + }); + + consoleRunner.run(); + + waitFor(mySemaphore0); + + mySemaphore = new Semaphore(0); + + myConsoleView = consoleRunner.getConsoleView(); + myProcessHandler = (PyConsoleProcessHandler)consoleRunner.getProcessHandler(); + + myExecuteHandler = (PydevConsoleExecuteActionHandler)consoleRunner.getConsoleExecuteActionHandler(); + + myCommunication = consoleRunner.getPydevConsoleCommunication(); + + myCommunication.addCommunicationListener(new ConsoleCommunicationListener() { + @Override + public void commandExecuted(boolean more) { + mySemaphore.release(); + } + + @Override + public void inputRequested() { + } + }); + + myProcessHandler.addProcessListener(new ProcessAdapter() { + @Override + public void processTerminated(ProcessEvent event) { + if (event.getExitCode() != 0 && !myProcessCanTerminate) { + Assert.fail("Process terminated unexpectedly\n" + output()); + } + } + }); + + OutputPrinter myOutputPrinter = null; + if (shouldPrintOutput) { + myOutputPrinter = new OutputPrinter(); + myOutputPrinter.start(); + } + + waitForOutput("PyDev console"); + + try { + testing(); + after(); + } + finally { + setProcessCanTerminate(true); + + if (myOutputPrinter != null) { + myOutputPrinter.stop(); + } + + disposeConsole(); + } + } + + private void disposeConsoleProcess() throws InterruptedException { + myProcessHandler.destroyProcess(); + + waitFor(myProcessHandler); + + if (!myProcessHandler.isProcessTerminated()) { + if (!waitFor(myProcessHandler)) { + if (!myProcessHandler.isProcessTerminated()) { + throw new RuntimeException("Cannot stop console process"); + } + } + } + myProcessHandler = null; + } + + /** + * Waits until all passed strings appear in output. + * If they don't appear in time limit, then exception is raised. + * + * @param string + * @throws InterruptedException + */ + public void waitForOutput(String... string) throws InterruptedException { + int count = 0; + while (true) { + List<String> missing = Lists.newArrayList(); + String out = output(); + boolean flag = true; + for (String s : string) { + if (!out.contains(s)) { + flag = false; + missing.add(s); + } + } + if (flag) { + break; + } + if (count > 10) { + Assert.fail("Strings: <--\n" + StringUtil.join(missing, "\n---\n") + "-->" + "are not present in output.\n" + output()); + } + Thread.sleep(2000); + count++; + } + } + + protected void waitForReady() throws InterruptedException { + int count = 0; + while (!myExecuteHandler.isEnabled() || !canExecuteNow()) { + if (count > 10) { + Assert.fail("Console is not ready"); + } + Thread.sleep(300); + count++; + } + } + + protected boolean canExecuteNow() { + return myExecuteHandler.canExecuteNow(); + } + + public void setShouldPrintOutput(boolean shouldPrintOutput) { + this.shouldPrintOutput = shouldPrintOutput; + } + + private class OutputPrinter { + private Thread myThread; + private int myLen = 0; + + public void start() { + myThread = new Thread(new Runnable() { + @Override + public void run() { + doJob(); + } + }); + myThread.setDaemon(true); + myThread.start(); + } + + private void doJob() { + try { + while (true) { + printToConsole(); + + Thread.sleep(500); + } + } + catch (Exception ignored) { + } + } + + private synchronized void printToConsole() { + String s = output(); + if (s.length() > myLen) { + System.out.print(s.substring(myLen)); + } + myLen = s.length(); + } + + public void stop() { + printToConsole(); + myThread.interrupt(); + } + } + + protected void exec(String command) throws InterruptedException { + waitForReady(); + int p = mySemaphore.availablePermits(); + myConsoleView.executeInConsole(command); + mySemaphore.acquire(p + 1); + //waitForOutput(">>> " + command); + } + + protected boolean hasValue(String varName, String value) throws PyDebuggerException { + PyDebugValue val = getValue(varName); + return val != null && value.equals(val.getValue()); + } + + protected void setValue(String varName, String value) throws PyDebuggerException { + PyDebugValue val = getValue(varName); + myCommunication.changeVariable(val, value); + } + + protected PyDebugValue getValue(String varName) throws PyDebuggerException { + XValueChildrenList l = myCommunication.loadFrame(); + + if (l == null) { + return null; + } + for (int i = 0; i < l.size(); i++) { + String name = l.getName(i); + if (varName.equals(name)) { + return (PyDebugValue)l.getValue(i); + } + } + + return null; + } + + protected List<String> getCompoundValueChildren(PyDebugValue value) throws PyDebuggerException { + XValueChildrenList list = myCommunication.loadVariable(value); + List<String> result = Lists.newArrayList(); + for (int i = 0; i<list.size(); i++) { + result.add(((PyDebugValue)list.getValue(i)).getValue()); + } + return result; + } + + protected void input(String text) { + myConsoleView.executeInConsole(text); + } + + protected void waitForFinish() throws InterruptedException { + waitFor(mySemaphore); + } + + protected void execNoWait(final String command) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + myConsoleView.executeCode(command, null); + } + }); + } + + protected void interrupt() { + myCommunication.interrupt(); + } + + + public void addTextToEditor(final String text) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + getConsoleView().getLanguageConsole().setInputText(text); + PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); + } + } + ); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java b/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java new file mode 100644 index 000000000000..38eed736c7ef --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/debug/PyBaseDebuggerTask.java @@ -0,0 +1,361 @@ +package com.jetbrains.env.python.debug; + +import com.google.common.collect.Sets; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.UIUtil; +import com.intellij.xdebugger.*; +import com.intellij.xdebugger.frame.XValue; +import com.jetbrains.env.PyExecutionFixtureTestTask; +import com.jetbrains.python.console.PythonDebugLanguageConsoleView; +import com.jetbrains.python.debugger.PyDebugProcess; +import com.jetbrains.python.debugger.PyDebugValue; +import com.jetbrains.python.debugger.PyDebuggerException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Assert; + +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.util.Set; +import java.util.concurrent.Semaphore; + +/** + * @author traff + */ +public abstract class PyBaseDebuggerTask extends PyExecutionFixtureTestTask { + private Set<Pair<String, Integer>> myBreakpoints = Sets.newHashSet(); + protected PyDebugProcess myDebugProcess; + protected XDebugSession mySession; + protected Semaphore myPausedSemaphore; + protected Semaphore myTerminateSemaphore; + protected boolean shouldPrintOutput = false; + protected boolean myProcessCanTerminate; + + protected void waitForPause() throws InterruptedException, InvocationTargetException { + Assert.assertTrue("Debugger didn't stopped within timeout\nOutput:" + output(), waitFor(myPausedSemaphore)); + + XDebuggerTestUtil.waitForSwing(); + } + + protected void waitForTerminate() throws InterruptedException, InvocationTargetException { + setProcessCanTerminate(true); + + Assert.assertTrue("Debugger didn't terminated within timeout\nOutput:" + output(), waitFor(myTerminateSemaphore)); + XDebuggerTestUtil.waitForSwing(); + } + + protected void runToLine(int line) throws InvocationTargetException, InterruptedException { + XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession(); + XSourcePosition position = currentSession.getCurrentPosition(); + + + currentSession.runToPosition(XDebuggerUtil.getInstance().createPosition(position.getFile(), line), false); + + waitForPause(); + } + + protected void resume() { + XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession(); + + Assert.assertTrue(currentSession.isSuspended()); + Assert.assertEquals(0, myPausedSemaphore.availablePermits()); + + currentSession.resume(); + } + + protected void stepOver() { + XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession(); + + Assert.assertTrue(currentSession.isSuspended()); + Assert.assertEquals(0, myPausedSemaphore.availablePermits()); + + currentSession.stepOver(false); + } + + protected void stepInto() { + XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession(); + + Assert.assertTrue(currentSession.isSuspended()); + Assert.assertEquals(0, myPausedSemaphore.availablePermits()); + + currentSession.stepInto(); + } + + protected void smartStepInto(String funcName) { + XDebugSession currentSession = XDebuggerManager.getInstance(getProject()).getCurrentSession(); + + Assert.assertTrue(currentSession.isSuspended()); + Assert.assertEquals(0, myPausedSemaphore.availablePermits()); + + myDebugProcess.startSmartStepInto(funcName); + } + + @NotNull + protected String output() { + if (mySession != null && mySession.getConsoleView() != null) { + PythonDebugLanguageConsoleView pydevConsoleView = (PythonDebugLanguageConsoleView)mySession.getConsoleView(); + if (pydevConsoleView != null) { + return XDebuggerTestUtil.getConsoleText(pydevConsoleView.getTextConsole()); + } + } + return "Console output not available."; + } + + protected void input(String text) { + PrintWriter pw = new PrintWriter(myDebugProcess.getProcessHandler().getProcessInput()); + pw.println(text); + pw.flush(); + } + + private void outputContains(String substring) { + Assert.assertTrue(output().contains(substring)); + } + + public void setProcessCanTerminate(boolean processCanTerminate) { + myProcessCanTerminate = processCanTerminate; + } + + protected void clearAllBreakpoints() { + + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + XDebuggerTestUtil.removeAllBreakpoints(getProject()); + } + }); + } + + /** + * Toggles breakpoint + * + * @param file getScriptPath() or path to script + * @param line starting with 0 + */ + protected void toggleBreakpoint(final String file, final int line) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + doToggleBreakpoint(file, line); + } + }); + + addOrRemoveBreakpoint(file, line); + } + + private void addOrRemoveBreakpoint(String file, int line) { + if (myBreakpoints.contains(Pair.create(file, line))) { + myBreakpoints.remove(Pair.create(file, line)); + } + else { + myBreakpoints.add(Pair.create(file, line)); + } + } + + protected void toggleBreakpointInEgg(final String file, final String innerPath, final int line) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + VirtualFile f = LocalFileSystem.getInstance().findFileByPath(file); + Assert.assertNotNull(f); + final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(f); + Assert.assertNotNull(jarRoot); + VirtualFile innerFile = jarRoot.findFileByRelativePath(innerPath); + Assert.assertNotNull(innerFile); + XDebuggerTestUtil.toggleBreakpoint(getProject(), innerFile, line); + } + }); + + addOrRemoveBreakpoint(file, line); + } + + public boolean canPutBreakpointAt(Project project, String file, int line) { + VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(file); + Assert.assertNotNull(vFile); + return XDebuggerUtil.getInstance().canPutBreakpointAt(project, vFile, line); + } + + private void doToggleBreakpoint(String file, int line) { + Assert.assertTrue(canPutBreakpointAt(getProject(), file, line)); + XDebuggerTestUtil.toggleBreakpoint(getProject(), LocalFileSystem.getInstance().findFileByPath(file), line); + } + + protected Variable eval(String name) throws InterruptedException { + Assert.assertTrue("Eval works only while suspended", mySession.isSuspended()); + XValue var = XDebuggerTestUtil.evaluate(mySession, name).first; + Assert.assertNotNull("There is no variable named " + name, var); + return new Variable(var); + } + + protected void setVal(String name, String value) throws InterruptedException, PyDebuggerException { + XValue var = XDebuggerTestUtil.evaluate(mySession, name).first; + myDebugProcess.changeVariable((PyDebugValue)var, value); + } + + public void waitForOutput(String ... string) throws InterruptedException { + long started = System.currentTimeMillis(); + + while (!containsOneOf(output(), string)) { + if (System.currentTimeMillis() - started > myTimeout) { + Assert.fail("None of '" + StringUtil.join(string, ", ") + "'" + " is not present in output.\n" + output()); + } + Thread.sleep(2000); + } + } + + protected boolean containsOneOf(String output, String[] strings) { + for (String s: strings) { + if (output.contains(s)) { + return true; + } + } + + return false; + } + + + public void setShouldPrintOutput(boolean shouldPrintOutput) { + this.shouldPrintOutput = shouldPrintOutput; + } + + @Override + public void setUp(final String testName) throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + if (myFixture == null) { + PyBaseDebuggerTask.super.setUp(testName); + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @Override + public void tearDown() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + public void run() { + try { + if (mySession != null) { + finishSession(); + } + PyBaseDebuggerTask.super.tearDown(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + protected void finishSession() throws InterruptedException { + disposeDebugProcess(); + + if (mySession != null) { + new WriteAction() { + protected void run(Result result) throws Throwable { + mySession.stop(); + } + }.execute(); + + waitFor(mySession.getDebugProcess().getProcessHandler()); //wait for process termination after session.stop() which is async + + XDebuggerTestUtil.disposeDebugSession(mySession); + mySession = null; + myDebugProcess = null; + myPausedSemaphore = null; + } + } + + protected abstract void disposeDebugProcess() throws InterruptedException; + + protected void doTest(@Nullable OutputPrinter myOutputPrinter) throws InterruptedException { + try { + testing(); + after(); + } + catch (Throwable e) { + throw new RuntimeException(output(), e); + } + finally { + clearAllBreakpoints(); + + setProcessCanTerminate(true); + + if (myOutputPrinter != null) { + myOutputPrinter.stop(); + } + + finishSession(); + } + } + + protected static class Variable { + private final XTestValueNode myValueNode; + + public Variable(XValue value) throws InterruptedException { + myValueNode = XDebuggerTestUtil.computePresentation(value); + } + + public Variable hasValue(String value) { + Assert.assertEquals(value, myValueNode.myValue); + return this; + } + + public Variable hasType(String type) { + Assert.assertEquals(type, myValueNode.myType); + return this; + } + + public Variable hasName(String name) { + Assert.assertEquals(name, myValueNode.myName); + return this; + } + } + + public class OutputPrinter { + private Thread myThread; + + public void start() { + myThread = new Thread(new Runnable() { + @Override + public void run() { + doJob(); + } + }); + myThread.setDaemon(true); + myThread.start(); + } + + private void doJob() { + int len = 0; + try { + while (true) { + String s = output(); + if (s.length() > len) { + System.out.print(s.substring(len)); + } + len = s.length(); + + Thread.sleep(500); + } + } + catch (Exception e) { + } + } + + public void stop() { + myThread.interrupt(); + } + } +} diff --git a/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java b/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java new file mode 100644 index 000000000000..7de13d1ebed6 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/debug/PyDebuggerTask.java @@ -0,0 +1,211 @@ +package com.jetbrains.env.python.debug; + +import com.intellij.execution.*; +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.executors.DefaultDebugExecutor; +import com.intellij.execution.process.KillableColoredProcessHandler; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; +import com.intellij.xdebugger.*; +import com.jetbrains.python.debugger.PyDebugProcess; +import com.jetbrains.python.debugger.PyDebugRunner; +import com.jetbrains.python.run.PythonCommandLineState; +import com.jetbrains.python.run.PythonConfigurationType; +import com.jetbrains.python.run.PythonRunConfiguration; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.concurrent.Semaphore; + +/** + * @author traff + */ +public class PyDebuggerTask extends PyBaseDebuggerTask { + + private boolean myMultiprocessDebug = false; + private PythonRunConfiguration myRunConfiguration; + + public PyDebuggerTask() { + init(); + } + + public PyDebuggerTask(String workingFolder, String scriptName, String scriptParameters) { + setWorkingFolder(getTestDataPath() + workingFolder); + setScriptName(scriptName); + setScriptParameters(scriptParameters); + init(); + } + + public PyDebuggerTask(String workingFolder, String scriptName) { + this(workingFolder, scriptName, null); + } + + protected void init() { + + } + + public void runTestOn(String sdkHome) throws Exception { + final Project project = getProject(); + + final ConfigurationFactory factory = PythonConfigurationType.getInstance().getConfigurationFactories()[0]; + + + final RunnerAndConfigurationSettings settings = + RunManager.getInstance(project).createRunConfiguration("test", factory); + + myRunConfiguration = (PythonRunConfiguration)settings.getConfiguration(); + + myRunConfiguration.setSdkHome(sdkHome); + myRunConfiguration.setScriptName(getScriptPath()); + myRunConfiguration.setWorkingDirectory(getWorkingFolder()); + myRunConfiguration.setScriptParameters(getScriptParameters()); + + new WriteAction() { + @Override + protected void run(Result result) throws Throwable { + RunManagerEx.getInstanceEx(project).addConfiguration(settings, false); + RunManagerEx.getInstanceEx(project).setSelectedConfiguration(settings); + Assert.assertSame(settings, RunManagerEx.getInstanceEx(project).getSelectedConfiguration()); + } + }.execute(); + + final PyDebugRunner runner = (PyDebugRunner)ProgramRunnerUtil.getRunner(DefaultDebugExecutor.EXECUTOR_ID, settings); + Assert.assertTrue(runner.canRun(DefaultDebugExecutor.EXECUTOR_ID, myRunConfiguration)); + + final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance(); + final ExecutionEnvironment env = new ExecutionEnvironment(executor, runner, settings, project); + + final PythonCommandLineState pyState = (PythonCommandLineState)myRunConfiguration.getState(executor, env); + + assert pyState != null; + pyState.setMultiprocessDebug(isMultiprocessDebug()); + + final ServerSocket serverSocket; + try { + //noinspection SocketOpenedButNotSafelyClosed + serverSocket = new ServerSocket(0); + } + catch (IOException e) { + throw new ExecutionException("Failed to find free socket port", e); + } + + + final int serverLocalPort = serverSocket.getLocalPort(); + final RunProfile profile = env.getRunProfile(); + + before(); + + setProcessCanTerminate(false); + + myTerminateSemaphore = new Semaphore(0); + + new WriteAction<ExecutionResult>() { + @Override + protected void run(@NotNull Result<ExecutionResult> result) throws Throwable { + final ExecutionResult res = + pyState.execute(executor, PyDebugRunner.createCommandLinePatchers(myFixture.getProject(), pyState, profile, serverLocalPort)); + + mySession = XDebuggerManager.getInstance(getProject()). + startSession(runner, env, env.getContentToReuse(), new XDebugProcessStarter() { + @NotNull + public XDebugProcess start(@NotNull final XDebugSession session) { + myDebugProcess = + new PyDebugProcess(session, serverSocket, res.getExecutionConsole(), res.getProcessHandler(), isMultiprocessDebug()); + + myDebugProcess.getProcessHandler().addProcessListener(new ProcessAdapter() { + + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + } + + @Override + public void processTerminated(ProcessEvent event) { + myTerminateSemaphore.release(); + if (event.getExitCode() != 0 && !myProcessCanTerminate) { + Assert.fail("Process terminated unexpectedly\n" + output()); + } + } + }); + + + myDebugProcess.getProcessHandler().startNotify(); + + return myDebugProcess; + } + }); + result.setResult(res); + } + }.execute().getResultObject(); + + OutputPrinter myOutputPrinter = null; + if (shouldPrintOutput) { + myOutputPrinter = new OutputPrinter(); + myOutputPrinter.start(); + } + + + myPausedSemaphore = new Semaphore(0); + + + mySession.addSessionListener(new XDebugSessionAdapter() { + @Override + public void sessionPaused() { + if (myPausedSemaphore != null) { + myPausedSemaphore.release(); + } + } + }); + + doTest(myOutputPrinter); + } + + public PythonRunConfiguration getRunConfiguration() { + return myRunConfiguration; + } + + private boolean isMultiprocessDebug() { + return myMultiprocessDebug; + } + + public void setMultiprocessDebug(boolean multiprocessDebug) { + myMultiprocessDebug = multiprocessDebug; + } + + @Override + protected void disposeDebugProcess() throws InterruptedException { + if (myDebugProcess != null) { + ProcessHandler processHandler = myDebugProcess.getProcessHandler(); + + myDebugProcess.stop(); + + waitFor(processHandler); + + if (!processHandler.isProcessTerminated()) { + killDebugProcess(); + if (!waitFor(processHandler)) { + new Throwable("Cannot stop debugger process").printStackTrace(); + } + } + } + } + + private void killDebugProcess() { + if (myDebugProcess.getProcessHandler() instanceof KillableColoredProcessHandler) { + KillableColoredProcessHandler h = (KillableColoredProcessHandler)myDebugProcess.getProcessHandler(); + + h.killProcess(); + } + else { + myDebugProcess.getProcessHandler().destroyProcess(); + } + } +} diff --git a/python/testSrc/com/jetbrains/env/python/dotNet/PyIronPythonTest.java b/python/testSrc/com/jetbrains/env/python/dotNet/PyIronPythonTest.java new file mode 100644 index 000000000000..a6f9dd68971d --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/dotNet/PyIronPythonTest.java @@ -0,0 +1,145 @@ +package com.jetbrains.env.python.dotNet; + +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.application.Result; +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.python.psi.PyFile; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; + +/** + * IronPython.NET specific tests + * + * @author Ilya.Kazakevich + */ +public class PyIronPythonTest extends PyEnvTestCase { + + /** + * IronPython tag + */ + static final String IRON_TAG = "iron"; + + public PyIronPythonTest() { + super(IRON_TAG); + } + + /** + * Tests skeleton generation + */ + public void testSkeletons() throws Exception { + runPythonTest(new SkeletonTestTask( + "dotNet/expected.skeleton.java.py", + "com.just.like.java", + "testSkeleton.py", + null + )); + } + + /** + * Tests skeleton generation with "from" statements + */ + public void testClassFromModule() throws Exception { + runPythonTest(new SkeletonTestTask( + "dotNet/expected.skeleton.java.py", + "com.just.like.java", + "import_class_from_module.py", + null + )); + } + + /** + * Tests skeleton generation when imported as alias + */ + public void testClassFromModuleAlias() throws Exception { + runPythonTest(new SkeletonTestTask( + "dotNet/expected.skeleton.java.py", + "com.just.like.java", + "import_class_from_module_alias.py", + null + )); + } + + /** + * Tests skeleton generation when module is imported + */ + public void testModuleFromPackage() throws Exception { + runPythonTest(new SkeletonTestTask( + "dotNet/expected.skeleton.java.py", + "com.just.like.java", + "import_module_from_package.py", + null + )); + } + + /** + * Tests skeleton generation when several classes are imported + */ + public void testSeveralClasses() throws Exception { + runPythonTest(new SkeletonTestTask( + "dotNet/expected.skeleton.java.py", + "com.just.like.java", + "import_several_classes_from_module.py", + "com.just.like.java.LikeJavaClass" + )); + } + + /** + * Tests skeletons for built-in classes. We can't compare files (CLR class may be changed from version to version), + * but we are sure there should be class System.Web.AspNetHostingPermissionLevel which is part of public API + */ + public void testImportBuiltInSystem() throws Exception { + final SkeletonTestTask task = new SkeletonTestTask( + null, + "System.Web", + "import_system.py", + null + ); + runPythonTest(task); + final PyFile skeleton = task.getGeneratedSkeleton(); + new ReadAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + Assert.assertNotNull("System.Web does not contain class AspNetHostingPermissionLevel. Error generating stub? It has classes " + + skeleton.getTopLevelClasses(), + skeleton.findTopLevelClass("AspNetHostingPermissionLevel")); + } + }.execute(); + + } + + /** + * Test importing of inner classes + */ + public void testImportInnerClass() throws Exception { + runPythonTest(new SkeletonTestTask( + "dotNet/expected.skeleton.Deep.py", + "SingleNameSpace.Some.Deep", + "inner_class.py", + null + )); + } + + /** + * Test importing of the whole namespace + */ + public void testWholeNameSpace() throws Exception { + runPythonTest(new SkeletonTestTask( + "dotNet/expected.skeleton.SingleNameSpace.py", + "SingleNameSpace", + "whole_namespace.py", + null + )); + } + + /** + * Test importing of single class + */ + public void testSingleClass() throws Exception { + runPythonTest(new SkeletonTestTask( + "dotNet/expected.skeleton.SingleNameSpace.py", + "SingleNameSpace", + "single_class.py", + null + )); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/dotNet/SkeletonTestTask.java b/python/testSrc/com/jetbrains/env/python/dotNet/SkeletonTestTask.java new file mode 100644 index 000000000000..e10916cbe7de --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/dotNet/SkeletonTestTask.java @@ -0,0 +1,142 @@ +package com.jetbrains.env.python.dotNet; + +import com.google.common.collect.Sets; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ex.QuickFixWrapper; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.progress.util.AbstractProgressIndicatorBase; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.util.ui.UIUtil; +import com.jetbrains.env.PyExecutionFixtureTestTask; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.PyNames; +import com.jetbrains.python.inspections.quickfix.GenerateBinaryStubsFix; +import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection; +import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.sdk.InvalidSdkException; +import com.jetbrains.python.sdk.PythonSdkType; +import com.jetbrains.python.sdkTools.SdkCreationType; +import org.hamcrest.Matchers; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Assert; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.Set; + +/** + * Task for test that checks skeleton generation + * + * @author Ilya.Kazakevich + */ +class SkeletonTestTask extends PyExecutionFixtureTestTask { + + /** + * Tags for this task to run + */ + private static final Set<String> IRON_TAGS = Sets.newHashSet(PyIronPythonTest.IRON_TAG); + /** + * Number of seconds we wait for skeleton generation external process (should be enough) + */ + private static final int SECONDS_TO_WAIT_FOR_SKELETON_GENERATION = 20; + + @Nullable + private final String myExpectedSkeletonFile; + @NotNull + private final String myModuleNameToBeGenerated; + @NotNull + private final String mySourceFileToRunGenerationOn; + @NotNull + private final String myUseQuickFixWithThisModuleOnly; + private PyFile myGeneratedSkeleton; + + /** + * @param expectedSkeletonFile if you want test to compare generated result with some file, provide its name. + * Pass null if you do not want to compare result with anything (you may do it yourself with {@link #getGeneratedSkeleton()}) + * @param moduleNameToBeGenerated name of module you think we should generate in dotted notation (like "System.Web" or "com.myModule"). + * System will wait for skeleton file for this module to be generated + * @param sourceFileToRunGenerationOn Source file where we should run "generate stubs" on. Be sure to place "caret" on appropriate place! + * @param useQuickFixWithThisModuleOnly If there are several quick fixes in code, you may run fix only on this module. + * Pass null if you are sure there would be only one quickfix + */ + SkeletonTestTask(@Nullable final String expectedSkeletonFile, + @NotNull final String moduleNameToBeGenerated, + @NotNull final String sourceFileToRunGenerationOn, + @Nullable final String useQuickFixWithThisModuleOnly) { + myExpectedSkeletonFile = expectedSkeletonFile; + myModuleNameToBeGenerated = moduleNameToBeGenerated.replace('.', '/'); + mySourceFileToRunGenerationOn = sourceFileToRunGenerationOn; + myUseQuickFixWithThisModuleOnly = useQuickFixWithThisModuleOnly != null ? useQuickFixWithThisModuleOnly : ""; + } + + + @Override + public void runTestOn(@NotNull final String sdkHome) throws IOException, InvalidSdkException { + final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.SDK_PACKAGES_ONLY); + final File skeletonsPath = new File(PythonSdkType.getSkeletonsPath(PathManager.getSystemPath(), sdk.getHomePath())); + File skeletonFileOrDirectory = new File(skeletonsPath, myModuleNameToBeGenerated); // File with module skeleton + + // Module may be stored in "moduleName.py" or "moduleName/__init__.py" + if (skeletonFileOrDirectory.isDirectory()) { + skeletonFileOrDirectory = new File(skeletonFileOrDirectory, PyNames.INIT_DOT_PY); + } + else { + skeletonFileOrDirectory = new File(skeletonFileOrDirectory.getAbsolutePath() + PyNames.DOT_PY); + } + + final File skeletonFile = skeletonFileOrDirectory; + + if (skeletonFile.exists()) { // To make sure we do not reuse it + assert skeletonFile.delete() : "Failed to delete file " + skeletonFile; + } + + myFixture.copyFileToProject("dotNet/" + mySourceFileToRunGenerationOn, mySourceFileToRunGenerationOn); // File that uses CLR library + myFixture.copyFileToProject("dotNet/PythonLibs.dll", "PythonLibs.dll"); // Library itself + myFixture.copyFileToProject("dotNet/SingleNameSpace.dll", "SingleNameSpace.dll"); // Another library + myFixture.configureByFile(mySourceFileToRunGenerationOn); + myFixture.enableInspections(PyUnresolvedReferencesInspection.class); // This inspection should suggest us to generate stubs + + + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + PsiDocumentManager.getInstance(myFixture.getProject()).commitAllDocuments(); + final String intentionName = PyBundle.message("sdk.gen.stubs.for.binary.modules", myUseQuickFixWithThisModuleOnly); + final IntentionAction intention = myFixture.findSingleIntention(intentionName); + Assert.assertNotNull("No intention found to generate skeletons!", intention); + Assert.assertThat("Intention should be quick fix to run", intention, Matchers.instanceOf(QuickFixWrapper.class)); + final LocalQuickFix quickFix = ((QuickFixWrapper)intention).getFix(); + Assert.assertThat("Quick fix should be 'generate binary skeletons' fix to run", quickFix, + Matchers.instanceOf(GenerateBinaryStubsFix.class)); + final Task fixTask = ((GenerateBinaryStubsFix)quickFix).getFixTask(myFixture.getFile()); + fixTask.run(new AbstractProgressIndicatorBase()); + } + }); + + FileUtil.copy(skeletonFile, new File(myFixture.getTempDirPath(), skeletonFile.getName())); + if (myExpectedSkeletonFile != null) { + myFixture.checkResultByFile(skeletonFile.getName(), myExpectedSkeletonFile, false); + } + myGeneratedSkeleton = (PyFile)myFixture.configureByFile(skeletonFile.getName()); + } + + + @Override + public Set<String> getTags() { + return Collections.unmodifiableSet(IRON_TAGS); + } + + /** + * @return File for generated skeleton. Call it after {@link #runTestOn(String)} only! + */ + @NotNull + PyFile getGeneratedSkeleton() { + return myGeneratedSkeleton; + } +} diff --git a/python/testSrc/com/jetbrains/env/python/dotNet/package-info.java b/python/testSrc/com/jetbrains/env/python/dotNet/package-info.java new file mode 100644 index 000000000000..e56e6203b98d --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/dotNet/package-info.java @@ -0,0 +1,5 @@ +/** + * IronPython tests. Install IronPython, .NET and add "iron" to "tags.txt" file. Check for more info: http://confluence.jetbrains.com/display/PYINT/Env+Tests + * @author Ilya.Kazakevich + */ +package com.jetbrains.env.python.dotNet;
\ No newline at end of file diff --git a/python/testSrc/com/jetbrains/env/python/testing/PythonDocTestingTest.java b/python/testSrc/com/jetbrains/env/python/testing/PythonDocTestingTest.java new file mode 100644 index 000000000000..411b6049350a --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/testing/PythonDocTestingTest.java @@ -0,0 +1,66 @@ +package com.jetbrains.env.python.testing; + +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.ut.PyDocTestTask; + +/** + * User : catherine + */ +public class PythonDocTestingTest extends PyEnvTestCase{ + public void testUTRunner() { + runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test1.py") { + + @Override + public void after() { + assertEquals(3, allTestsCount()); + assertEquals(3, passedTestsCount()); + allTestsPassed(); + } + }); + } + + public void testClass() { + runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test1.py::FirstGoodTest") { + + @Override + public void after() { + assertEquals(1, allTestsCount()); + assertEquals(1, passedTestsCount()); + } + }); + } + + public void testMethod() { + runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test1.py::SecondGoodTest::test_passes") { + + @Override + public void after() { + assertEquals(1, allTestsCount()); + assertEquals(1, passedTestsCount()); + } + }); + } + + public void testFunction() { + runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test1.py::factorial") { + + @Override + public void after() { + assertEquals(1, allTestsCount()); + assertEquals(1, passedTestsCount()); + } + }); + } + + public void testUTRunner2() { + runPythonTest(new PyDocTestTask("/testRunner/env/doc", "test2.py") { + + @Override + public void after() { + assertEquals(3, allTestsCount()); + assertEquals(1, passedTestsCount()); + assertEquals(2, failedTestsCount()); + } + }); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/testing/PythonNoseTestingTest.java b/python/testSrc/com/jetbrains/env/python/testing/PythonNoseTestingTest.java new file mode 100644 index 000000000000..33250ae17fa4 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/testing/PythonNoseTestingTest.java @@ -0,0 +1,33 @@ +package com.jetbrains.env.python.testing; + +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.ut.PyNoseTestTask; + +/** + * User : catherine + */ +public class PythonNoseTestingTest extends PyEnvTestCase{ + public void testNoseRunner() { + runPythonTest(new PyNoseTestTask("/testRunner/env/nose", "test1.py") { + + @Override + public void after() { + assertEquals(3, allTestsCount()); + assertEquals(3, passedTestsCount()); + allTestsPassed(); + } + }); + } + + public void testNoseRunner2() { + runPythonTest(new PyNoseTestTask("/testRunner/env/nose", "test2.py") { + + @Override + public void after() { + assertEquals(8, allTestsCount()); + assertEquals(5, passedTestsCount()); + assertEquals(3, failedTestsCount()); + } + }); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/testing/PythonPyTestingTest.java b/python/testSrc/com/jetbrains/env/python/testing/PythonPyTestingTest.java new file mode 100644 index 000000000000..ab3dee60c5b0 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/testing/PythonPyTestingTest.java @@ -0,0 +1,33 @@ +package com.jetbrains.env.python.testing; + +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.ut.PyTestTestTask; + +/** + * User : catherine + */ +public class PythonPyTestingTest extends PyEnvTestCase{ + public void testPytestRunner() { + runPythonTest(new PyTestTestTask("/testRunner/env/pytest", "test1.py") { + + @Override + public void after() { + assertEquals(3, allTestsCount()); + assertEquals(3, passedTestsCount()); + allTestsPassed(); + } + }); + } + + public void testPytestRunner2() { + runPythonTest(new PyTestTestTask("/testRunner/env/pytest", "test2.py") { + + @Override + public void after() { + assertEquals(8, allTestsCount()); + assertEquals(5, passedTestsCount()); + assertEquals(3, failedTestsCount()); + } + }); + } +} diff --git a/python/testSrc/com/jetbrains/env/python/testing/PythonUnitTestingTest.java b/python/testSrc/com/jetbrains/env/python/testing/PythonUnitTestingTest.java new file mode 100644 index 000000000000..682eb8160285 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/python/testing/PythonUnitTestingTest.java @@ -0,0 +1,78 @@ +package com.jetbrains.env.python.testing; + +import com.jetbrains.env.PyEnvTestCase; +import com.jetbrains.env.ut.PyUnitTestTask; +import junit.framework.Assert; + +/** + * @author traff + */ +public class PythonUnitTestingTest extends PyEnvTestCase{ + public void testUTRunner() { + runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test1.py") { + + @Override + public void after() { + Assert.assertEquals(2, allTestsCount()); + Assert.assertEquals(2, passedTestsCount()); + allTestsPassed(); + } + }); + } + + public void testUTRunner2() { + runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test2.py") { + + @Override + public void after() { + assertEquals(3, allTestsCount()); + assertEquals(1, passedTestsCount()); + assertEquals(2, failedTestsCount()); + } + }); + } + + public void testClass() { + runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test_file.py::GoodTest") { + + @Override + public void after() { + assertEquals(1, allTestsCount()); + assertEquals(1, passedTestsCount()); + } + }); + } + + public void testMethod() { + runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test_file.py::GoodTest::test_passes") { + + @Override + public void after() { + assertEquals(1, allTestsCount()); + assertEquals(1, passedTestsCount()); + } + }); + } + + public void testFolder() { + runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "test_folder/") { + + @Override + public void after() { + assertEquals(5, allTestsCount()); + assertEquals(3, passedTestsCount()); + } + }); + } + + public void testDependent() { + runPythonTest(new PyUnitTestTask("/testRunner/env/unit", "dependentTests/test_my_class.py") { + + @Override + public void after() { + assertEquals(1, allTestsCount()); + assertEquals(1, passedTestsCount()); + } + }); + } +} diff --git a/python/testSrc/com/jetbrains/env/ut/PyDocTestTask.java b/python/testSrc/com/jetbrains/env/ut/PyDocTestTask.java new file mode 100644 index 000000000000..e8228d767287 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/ut/PyDocTestTask.java @@ -0,0 +1,24 @@ +package com.jetbrains.env.ut; + +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.openapi.project.Project; +import com.jetbrains.python.testing.PythonTestConfigurationType; + +/** + * User : catherine + */ +public abstract class PyDocTestTask extends PyUnitTestTask { + public PyDocTestTask(String workingFolder, String scriptName, String scriptParameters) { + super(workingFolder, scriptName, scriptParameters); + } + + public PyDocTestTask(String workingFolder, String scriptName) { + this(workingFolder, scriptName, null); + } + + public void runTestOn(String sdkHome) throws Exception { + final Project project = getProject(); + final ConfigurationFactory factory = PythonTestConfigurationType.getInstance().PY_DOCTEST_FACTORY; + runConfiguration(factory, sdkHome, project); + } +} diff --git a/python/testSrc/com/jetbrains/env/ut/PyNoseTestTask.java b/python/testSrc/com/jetbrains/env/ut/PyNoseTestTask.java new file mode 100644 index 000000000000..e130dd5717a4 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/ut/PyNoseTestTask.java @@ -0,0 +1,32 @@ +package com.jetbrains.env.ut; + +import com.google.common.collect.ImmutableSet; +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.openapi.project.Project; +import com.jetbrains.python.testing.PythonTestConfigurationType; + +import java.util.Set; + +/** + * User : catherine + */ +public abstract class PyNoseTestTask extends PyUnitTestTask { + public PyNoseTestTask(String workingFolder, String scriptName, String scriptParameters) { + super(workingFolder, scriptName, scriptParameters); + } + + public PyNoseTestTask(String workingFolder, String scriptName) { + this(workingFolder, scriptName, null); + } + + public void runTestOn(String sdkHome) throws Exception { + final Project project = getProject(); + final ConfigurationFactory factory = PythonTestConfigurationType.getInstance().PY_NOSETEST_FACTORY; + runConfiguration(factory, sdkHome, project); + } + + @Override + public Set<String> getTags() { + return ImmutableSet.of("nose"); + } +} diff --git a/python/testSrc/com/jetbrains/env/ut/PyTestTestTask.java b/python/testSrc/com/jetbrains/env/ut/PyTestTestTask.java new file mode 100644 index 000000000000..1e4261188206 --- /dev/null +++ b/python/testSrc/com/jetbrains/env/ut/PyTestTestTask.java @@ -0,0 +1,40 @@ +package com.jetbrains.env.ut; + +import com.google.common.collect.ImmutableSet; +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.openapi.project.Project; +import com.jetbrains.python.testing.AbstractPythonTestRunConfiguration; +import com.jetbrains.python.testing.PythonTestConfigurationType; +import com.jetbrains.python.testing.pytest.PyTestRunConfiguration; + +import java.util.Set; + +/** + * User : catherine + */ +public abstract class PyTestTestTask extends PyUnitTestTask { + public PyTestTestTask(String workingFolder, String scriptName, String scriptParameters) { + super(workingFolder, scriptName, scriptParameters); + } + + public PyTestTestTask(String workingFolder, String scriptName) { + this(workingFolder, scriptName, null); + } + + public void runTestOn(String sdkHome) throws Exception { + final Project project = getProject(); + final ConfigurationFactory factory = PythonTestConfigurationType.getInstance().PY_PYTEST_FACTORY; + runConfiguration(factory, sdkHome, project); + } + + @Override + public Set<String> getTags() { + return ImmutableSet.of("pytest"); + } + + protected void configure(AbstractPythonTestRunConfiguration config) { + if (config instanceof PyTestRunConfiguration) + ((PyTestRunConfiguration)config).setTestToRun(getScriptPath()); + } + +} diff --git a/python/testSrc/com/jetbrains/env/ut/PyUnitTestTask.java b/python/testSrc/com/jetbrains/env/ut/PyUnitTestTask.java new file mode 100644 index 000000000000..01401ca1ca9a --- /dev/null +++ b/python/testSrc/com/jetbrains/env/ut/PyUnitTestTask.java @@ -0,0 +1,241 @@ +package com.jetbrains.env.ut; + +import com.google.common.collect.Lists; +import com.intellij.execution.*; +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.executors.DefaultRunExecutor; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.runners.ExecutionEnvironmentBuilder; +import com.intellij.execution.runners.ProgramRunner; +import com.intellij.execution.testframework.Filter; +import com.intellij.execution.testframework.sm.runner.SMTestProxy; +import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView; +import com.intellij.execution.testframework.sm.runner.ui.TestResultsViewer; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Key; +import com.intellij.util.ui.UIUtil; +import com.intellij.xdebugger.XDebuggerTestUtil; +import com.jetbrains.env.PyExecutionFixtureTestTask; +import com.jetbrains.python.sdk.PythonEnvUtil; +import com.jetbrains.python.sdk.flavors.JythonSdkFlavor; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import com.jetbrains.python.testing.AbstractPythonTestRunConfiguration; +import com.jetbrains.python.testing.PythonTestConfigurationType; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; + +/** + * @author traff + */ +public abstract class PyUnitTestTask extends PyExecutionFixtureTestTask { + + protected ProcessHandler myProcessHandler; + private boolean shouldPrintOutput = false; + private SMTestProxy.SMRootTestProxy myTestProxy; + private boolean mySetUp = false; + private SMTRunnerConsoleView myConsoleView; + private RunContentDescriptor myDescriptor; + private StringBuilder myOutput; + + public PyUnitTestTask() { + } + + public PyUnitTestTask(String workingFolder, String scriptName, String scriptParameters) { + setWorkingFolder(getTestDataPath() + workingFolder); + setScriptName(scriptName); + setScriptParameters(scriptParameters); + } + + public PyUnitTestTask(String workingFolder, String scriptName) { + this(workingFolder, scriptName, null); + } + + @Override + public void setUp(final String testName) throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + if (myFixture == null) { + PyUnitTestTask.super.setUp(testName); + mySetUp = true; + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @NotNull + protected String output() { + return myOutput.toString(); + } + + @Override + public void tearDown() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + if (mySetUp) { + if (myConsoleView != null) { + Disposer.dispose(myConsoleView); + myConsoleView = null; + } + if (myDescriptor != null) { + Disposer.dispose(myDescriptor); + myDescriptor = null; + } + + + PyUnitTestTask.super.tearDown(); + + mySetUp = false; + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + ); + } + + @Override + public void runTestOn(String sdkHome) throws Exception { + final Project project = getProject(); + final ConfigurationFactory factory = PythonTestConfigurationType.getInstance().PY_UNITTEST_FACTORY; + runConfiguration(factory, sdkHome, project); + } + + protected void runConfiguration(ConfigurationFactory factory, String sdkHome, final Project project) throws Exception { + final RunnerAndConfigurationSettings settings = + RunManager.getInstance(project).createRunConfiguration("test", factory); + + AbstractPythonTestRunConfiguration config = (AbstractPythonTestRunConfiguration)settings.getConfiguration(); + + + config.setSdkHome(sdkHome); + config.setScriptName(getScriptPath()); + config.setWorkingDirectory(getWorkingFolder()); + + PythonSdkFlavor sdk = PythonSdkFlavor.getFlavor(sdkHome); + + + if (sdk instanceof JythonSdkFlavor) { + config.setInterpreterOptions(JythonSdkFlavor.getPythonPathCmdLineArgument(Lists.<String>newArrayList(getWorkingFolder()))); + } + else { + PythonEnvUtil.addToPythonPath(config.getEnvs(), getWorkingFolder()); + } + + + configure(config); + + new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + RunManagerEx.getInstanceEx(project).addConfiguration(settings, false); + RunManagerEx.getInstanceEx(project).setSelectedConfiguration(settings); + Assert.assertSame(settings, RunManagerEx.getInstanceEx(project).getSelectedConfiguration()); + } + }.execute(); + + final ExecutionEnvironment environment = ExecutionEnvironmentBuilder.create(DefaultRunExecutor.getRunExecutorInstance(), settings).build(); + //noinspection ConstantConditions + Assert.assertTrue(environment.getRunner().canRun(DefaultRunExecutor.EXECUTOR_ID, config)); + + before(); + + final com.intellij.util.concurrency.Semaphore s = new com.intellij.util.concurrency.Semaphore(); + s.down(); + + myOutput = new StringBuilder(); + + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + environment.getRunner().execute(environment, new ProgramRunner.Callback() { + @Override + public void processStarted(RunContentDescriptor descriptor) { + myDescriptor = descriptor; + myProcessHandler = myDescriptor.getProcessHandler(); + myProcessHandler.addProcessListener(new ProcessAdapter() { + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + myOutput.append(event.getText()); + } + }); + myConsoleView = (com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView)descriptor.getExecutionConsole(); + myTestProxy = myConsoleView.getResultsViewer().getTestsRootNode(); + myConsoleView.getResultsViewer().addEventsListener(new TestResultsViewer.SMEventsAdapter() { + @Override + public void onTestingFinished(TestResultsViewer sender) { + s.up(); + } + }); + } + }); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + + Assert.assertTrue(s.waitFor(60000)); + + XDebuggerTestUtil.waitForSwing(); + + assertFinished(); + + Assert.assertTrue(output(), allTestsCount() > 0); + + after(); + + disposeProcess(myProcessHandler); + } + + protected void configure(AbstractPythonTestRunConfiguration config) { + } + + public void allTestsPassed() { + Assert.assertEquals(output(), 0, myTestProxy.getChildren(Filter.NOT_PASSED).size()); + Assert.assertEquals(output(), 0, failedTestsCount()); + } + + public int failedTestsCount() { + return myTestProxy.collectChildren(NOT_SUIT.and(Filter.FAILED_OR_INTERRUPTED)).size(); + } + + public int passedTestsCount() { + return myTestProxy.collectChildren(NOT_SUIT.and(Filter.PASSED)).size(); + } + + public void assertFinished() { + Assert.assertTrue("State is " + myTestProxy.getMagnitudeInfo().getTitle() + "\n" + output(), + myTestProxy.wasLaunched() && !myTestProxy.wasTerminated()); + } + + public int allTestsCount() { + return myTestProxy.collectChildren(NOT_SUIT).size(); + } + + public static final Filter<SMTestProxy> NOT_SUIT = new Filter<SMTestProxy>() { + @Override + public boolean shouldAccept(SMTestProxy test) { + return !test.isSuite(); + } + }; +} diff --git a/python/testSrc/com/jetbrains/python/PyAddImportTest.java b/python/testSrc/com/jetbrains/python/PyAddImportTest.java index 01f83847aa56..f95187aff819 100644 --- a/python/testSrc/com/jetbrains/python/PyAddImportTest.java +++ b/python/testSrc/com/jetbrains/python/PyAddImportTest.java @@ -17,8 +17,13 @@ package com.jetbrains.python; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.psi.PsiPolyVariantReference; import com.jetbrains.python.codeInsight.imports.AddImportHelper; +import com.jetbrains.python.fixtures.PyResolveTestCase; import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.psi.PyElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author yole @@ -53,4 +58,46 @@ public class PyAddImportTest extends PyTestCase { }.execute(); myFixture.checkResultByFile("addImport/" + getTestName(true) + ".after.py"); } + + // PY-6020 + public void testLocalFromImport() { + doAddLocalImport("foo", "package.module"); + } + + // PY-6020 + public void testLocalImport() { + doAddLocalImport("module", null); + } + + // PY-13668 + public void testLocalImportInlineFunctionBody() { + testLocalImport(); + } + + // PY-13668 + public void testLocalImportInlineBranch() { + testLocalImport(); + } + + /** + * Add local import statement + * @param name reference name in corresponding import element + * @param qualifier if not {@code null} form {@code from qualifier import name} will be used, otherwise {@code import name} + */ + private void doAddLocalImport(@NotNull final String name, @Nullable final String qualifier) { + myFixture.configureByFile("addImport/" + getTestName(true) + ".py"); + new WriteCommandAction(myFixture.getProject(), myFixture.getFile()) { + @Override + protected void run(Result result) throws Throwable { + final PsiPolyVariantReference reference = PyResolveTestCase.findReferenceByMarker(myFixture.getFile()); + if (qualifier != null) { + AddImportHelper.addLocalFromImportStatement((PyElement)reference.getElement(), qualifier, name); + } + else { + AddImportHelper.addLocalImportStatement((PyElement)reference.getElement(), name); + } + } + }.execute(); + myFixture.checkResultByFile("addImport/" + getTestName(true) + ".after.py"); + } } diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 88243f065a3b..b0e41bcffddc 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -829,6 +829,12 @@ public class PyTypeTest extends PyTestCase { " pass\n"); } + // PY-12801 + public void testTupleConcatenation() { + doTest("(int, bool, str)", + "expr = (1,) + (True, 'spam') + ()"); + } + private static TypeEvalContext getTypeEvalContext(@NotNull PyExpression element) { return TypeEvalContext.userInitiated(element.getContainingFile()).withTracing(); } diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index f365eea4da34..a1366ac2e17a 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -626,23 +626,85 @@ public class PythonCompletionTest extends PyTestCase { } // PY-4073 - public void testSpecialFunctionAttributes() throws Exception { - setLanguageLevel(LanguageLevel.PYTHON27); - try { - List<String> suggested = doTestByText("def func(): pass; func.func_<caret>"); - assertNotNull(suggested); - assertContainsElements(suggested, - "func_defaults", "func_globals", "func_closure", - "func_code", "func_name", "func_doc", "func_dict"); - - suggested = doTestByText("def func(): pass; func.__<caret>"); - assertNotNull(suggested); - assertContainsElements(suggested, "__defaults__", "__globals__", "__closure__", - "__code__", "__name__", "__doc__", "__dict__", "__module__"); - assertDoesntContain(suggested, "__annotations__", "__kwdefaults__"); - } - finally { - setLanguageLevel(null); - } + public void testFunctionSpecialAttributes() { + runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() { + @Override + public void run() { + List<String> suggested = doTestByText("def func(): pass; func.func_<caret>"); + assertNotNull(suggested); + assertContainsElements(suggested, PyNames.LEGACY_FUNCTION_SPECIAL_ATTRIBUTES); + + suggested = doTestByText("def func(): pass; func.__<caret>"); + assertNotNull(suggested); + assertContainsElements(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES); + assertDoesntContain(suggested, PyNames.PY3_ONLY_FUNCTION_SPECIAL_ATTRIBUTES); + } + }); + } + + // PY-9342 + public void testBoundMethodSpecialAttributes() { + List<String> suggested = doTestByText("{}.update.im_<caret>"); + assertNotNull(suggested); + assertContainsElements(suggested, PyNames.LEGACY_METHOD_SPECIAL_ATTRIBUTES); + + suggested = doTestByText("{}.update.__<caret>"); + assertNotNull(suggested); + assertContainsElements(suggested, PyNames.METHOD_SPECIAL_ATTRIBUTES); + assertDoesntContain(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES); + } + + // PY-9342 + public void testWeakQualifierBoundMethodAttributes() { + assertUnderscoredMethodSpecialAttributesSuggested(); + } + + private void assertUnderscoredMethodSpecialAttributesSuggested() { + myFixture.configureByFile("completion/" + getTestName(true) + ".py"); + myFixture.completeBasic(); + final List<String> suggested = myFixture.getLookupElementStrings(); + assertNotNull(suggested); + assertContainsElements(suggested, PyNames.METHOD_SPECIAL_ATTRIBUTES); + assertDoesntContain(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES); + } + + // PY-9342 + public void testUnboundMethodSpecialAttributes() { + runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() { + @Override + public void run() { + assertUnderscoredMethodSpecialAttributesSuggested(); + } + }); + runWithLanguageLevel(LanguageLevel.PYTHON32, new Runnable() { + @Override + public void run() { + assertUnderscoredFunctionAttributesSuggested(); + } + }); + } + + // PY-9342 + public void testStaticMethodSpecialAttributes() { + assertUnderscoredFunctionAttributesSuggested(); + } + + // PY-9342 + public void testLambdaSpecialAttributes() { + assertUnderscoredFunctionAttributesSuggested(); + } + + // PY-9342 + public void testReassignedMethodSpecialAttributes() { + assertUnderscoredMethodSpecialAttributesSuggested(); + } + + private void assertUnderscoredFunctionAttributesSuggested() { + myFixture.configureByFile("completion/" + getTestName(true) + ".py"); + myFixture.completeBasic(); + final List<String> suggested = myFixture.getLookupElementStrings(); + assertNotNull(suggested); + assertContainsElements(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES); + assertDoesntContain(suggested, PyNames.METHOD_SPECIAL_ATTRIBUTES); } } diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyCommandLineTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyCommandLineTestCase.java index bbaf31252c3e..65815c112b2e 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyCommandLineTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyCommandLineTestCase.java @@ -20,16 +20,15 @@ import com.intellij.execution.ExecutionException; import com.intellij.execution.Executor; import com.intellij.execution.configurations.ConfigurationFactory; import com.intellij.execution.configurations.ConfigurationType; -import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.executors.DefaultRunExecutor; -import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironmentBuilder; import com.intellij.openapi.project.Project; import com.jetbrains.python.PythonHelpersLocator; import com.jetbrains.python.debugger.PyDebugRunner; import com.jetbrains.python.run.AbstractPythonRunConfiguration; import com.jetbrains.python.run.PythonCommandLineState; +import org.jetbrains.annotations.NotNull; import java.util.List; @@ -59,11 +58,9 @@ public abstract class PyCommandLineTestCase extends PyTestCase { protected List<String> buildRunCommandLine(AbstractPythonRunConfiguration configuration) { try { - final Executor executor = DefaultRunExecutor.getRunExecutorInstance(); - ExecutionEnvironment env = new ExecutionEnvironmentBuilder(myFixture.getProject(), executor).setRunProfile(configuration).build(); - final PythonCommandLineState state = (PythonCommandLineState)configuration.getState(executor, env); - final GeneralCommandLine generalCommandLine = state.generateCommandLine(); - return generalCommandLine.getParametersList().getList(); + PythonCommandLineState state = getState(configuration, DefaultRunExecutor.getRunExecutorInstance()); + assert state != null; + return state.generateCommandLine().getParametersList().getList(); } catch (ExecutionException e) { throw new RuntimeException(e); @@ -72,15 +69,21 @@ public abstract class PyCommandLineTestCase extends PyTestCase { protected List<String> buildDebugCommandLine(AbstractPythonRunConfiguration configuration) { try { - final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance(); - ExecutionEnvironment env = new ExecutionEnvironmentBuilder(myFixture.getProject(), executor).setRunProfile(configuration).build(); - final PythonCommandLineState state = (PythonCommandLineState)configuration.getState(executor, env); - final GeneralCommandLine generalCommandLine = - state.generateCommandLine(PyDebugRunner.createCommandLinePatchers(configuration.getProject(), state, configuration, PORT)); - return generalCommandLine.getParametersList().getList(); + PythonCommandLineState state = getState(configuration, DefaultDebugExecutor.getDebugExecutorInstance()); + assert state != null; + return state.generateCommandLine(PyDebugRunner.createCommandLinePatchers(configuration.getProject(), state, configuration, PORT)) + .getParametersList() + .getList(); } catch (ExecutionException e) { throw new RuntimeException(e); } } + + + private static PythonCommandLineState getState(@NotNull AbstractPythonRunConfiguration configuration, @NotNull Executor executor) throws ExecutionException { + return (PythonCommandLineState)ExecutionEnvironmentBuilder.create(executor, configuration) + .build() + .getState(); + } } diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java index 45d582a783a8..99da2a43aea1 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java @@ -18,6 +18,8 @@ package com.jetbrains.python.fixtures; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ex.QuickFixWrapper; +import com.intellij.find.findUsages.CustomUsageSearcher; +import com.intellij.find.findUsages.FindUsagesOptions; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; @@ -33,6 +35,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; +import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.PlatformTestCase; import com.intellij.testFramework.TestDataPath; @@ -42,6 +45,10 @@ import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import com.intellij.testFramework.fixtures.TestFixtureBuilder; import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl; +import com.intellij.usageView.UsageInfo; +import com.intellij.usages.Usage; +import com.intellij.usages.rules.PsiElementUsage; +import com.intellij.util.CommonProcessors.CollectProcessor; import com.jetbrains.python.PythonHelpersLocator; import com.jetbrains.python.PythonMockSdk; import com.jetbrains.python.PythonModuleTypeBase; @@ -55,6 +62,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; /** * @author yole @@ -184,6 +194,36 @@ public abstract class PyTestCase extends UsefulTestCase { myFixture.getEditor().getCaretModel().moveToOffset(element.getTextOffset()); } + /** + * Finds all usages of element. Works much like method in {@link com.intellij.testFramework.fixtures.CodeInsightTestFixture#findUsages(com.intellij.psi.PsiElement)}, + * but supports {@link com.intellij.find.findUsages.CustomUsageSearcher} and {@link com.intellij.psi.search.searches.ReferencesSearch} as well + * + * @param element what to find + * @return usages + */ + @NotNull + protected Collection<PsiElement> findUsage(@NotNull final PsiElement element) { + final Collection<PsiElement> result = new ArrayList<PsiElement>(); + final CollectProcessor<Usage> usageCollector = new CollectProcessor<Usage>(); + for (final CustomUsageSearcher searcher : CustomUsageSearcher.EP_NAME.getExtensions()) { + searcher.processElementUsages(element, usageCollector, new FindUsagesOptions(myFixture.getProject())); + } + for (final Usage usage : usageCollector.getResults()) { + if (usage instanceof PsiElementUsage) { + result.add(((PsiElementUsage)usage).getElement()); + } + } + for (final PsiReference reference : ReferencesSearch.search(element).findAll()) { + result.add(reference.getElement()); + } + + for (final UsageInfo info : myFixture.findUsages(element)) { + result.add(info.getElement()); + } + + return result; + } + protected static class PyLightProjectDescriptor implements LightProjectDescriptor { private final String myPythonVersion; diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index a0a2a1232b2e..3451712f6bcd 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -371,6 +371,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { doMultiFileTest(); } + // PY-9342 + public void testMethodSpecialAttributes() { + doTest(); + } + // PY-11472 public void testUnusedImportBeforeStarImport() { doMultiFileTest(); diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java index 769e72077461..a54c68a0db09 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceConstantTest.java @@ -51,7 +51,7 @@ public class PyIntroduceConstantTest extends PyIntroduceTestCase { public void testSuggestUniqueNames() { // PY-4409 doTestSuggestions(PyExpression.class, "S1"); } - + public void testSuggestUniqueNamesGlobalScope() { // PY-4409 doTestSuggestions(PyExpression.class, "S1"); } @@ -60,6 +60,11 @@ public class PyIntroduceConstantTest extends PyIntroduceTestCase { doTestInplace(null); } + // PY-13484 + public void testFromParameterDefaultValue() { + doTest(); + } + @Override protected String getTestDataPath() { return super.getTestDataPath() + "/refactoring/introduceConstant"; diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java index f2117977cbf3..33a191f2b9a0 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java @@ -245,6 +245,8 @@ public class PyIntroduceVariableTest extends PyIntroduceTestCase { } } + public void testSelectionBreaksBinaryOperator() {doTest();} + private void doTestCannotPerform() { boolean thrownExpectedException = false; try { diff --git a/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java b/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java new file mode 100644 index 000000000000..2d97e747f00b --- /dev/null +++ b/python/testSrc/com/jetbrains/python/sdkTools/PyTestSdkTools.java @@ -0,0 +1,142 @@ +package com.jetbrains.python.sdkTools; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkModificator; +import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; +import com.intellij.openapi.roots.ModuleRootModificationUtil; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.UsefulTestCase; +import com.jetbrains.python.sdk.InvalidSdkException; +import com.jetbrains.python.sdk.PythonSdkType; +import com.jetbrains.python.sdk.skeletons.PySkeletonRefresher; +import com.jetbrains.python.sdk.skeletons.SkeletonVersionChecker; +import org.hamcrest.Matchers; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Engine to create SDK for tests. + * See {@link #createTempSdk(com.intellij.openapi.vfs.VirtualFile, SdkCreationType, com.intellij.openapi.module.Module)} + * + * @author Ilya.Kazakevich + */ +public final class PyTestSdkTools { + + private static final Sdk[] NO_SDK = new Sdk[0]; + + private PyTestSdkTools() { + + } + + /** + * Creates SDK by its path and associates it with module. + * + * @param sdkHome path to sdk + * @param sdkCreationType SDK creation strategy (see {@link SdkCreationType} doc) + * @return sdk + */ + @NotNull + public static Sdk createTempSdk(@NotNull final VirtualFile sdkHome, + @NotNull final SdkCreationType sdkCreationType, + @NotNull final Module module + ) + throws InvalidSdkException, IOException { + final Ref<Sdk> ref = Ref.create(); + UsefulTestCase.edt(new Runnable() { + + @Override + public void run() { + final Sdk sdk = SdkConfigurationUtil.setupSdk(NO_SDK, sdkHome, PythonSdkType.getInstance(), true, null, null); + Assert.assertNotNull("Failed to create SDK on " + sdkHome, sdk); + ref.set(sdk); + } + }); + final Sdk sdk = ref.get(); + if (sdkCreationType != SdkCreationType.EMPTY_SDK) { + generateTempSkeletonsOrPackages(sdk, sdkCreationType == SdkCreationType.SDK_PACKAGES_AND_SKELETONS, module); + } + UsefulTestCase.edt(new Runnable() { + @Override + public void run() { + SdkConfigurationUtil.addSdk(sdk); + } + }); + return sdk; + } + + + /** + * Adds installed eggs to SDK, generates skeletons (optionally) and associates it with modle. + * + * @param sdk sdk to process + * @param addSkeletons add skeletons or only packages + * @param module module to associate with + * @throws InvalidSdkException bas sdk + * @throws IOException failed to read eggs + */ + private static void generateTempSkeletonsOrPackages(@NotNull final Sdk sdk, + final boolean addSkeletons, + @NotNull final Module module) + throws InvalidSdkException, IOException { + final Project project = module.getProject(); + ModuleRootModificationUtil.setModuleSdk(module, sdk); + + UsefulTestCase.edt(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + ProjectRootManager.getInstance(project).setProjectSdk(sdk); + } + }); + } + }); + + + final SdkModificator modificator = sdk.getSdkModificator(); + modificator.removeRoots(OrderRootType.CLASSES); + + for (final String path : PythonSdkType.getSysPathsFromScript(sdk.getHomePath())) { + PythonSdkType.addSdkRoot(modificator, path); + } + if (!addSkeletons) { + UsefulTestCase.edt(new Runnable() { + @Override + public void run() { + modificator.commitChanges(); + } + }); + return; + } + + final File tempDir = FileUtil.createTempDirectory(PyTestSdkTools.class.getName(), null); + final File skeletonsDir = new File(tempDir, PythonSdkType.SKELETON_DIR_NAME); + FileUtil.createDirectory(skeletonsDir); + final String skeletonsPath = skeletonsDir.toString(); + PythonSdkType.addSdkRoot(modificator, skeletonsPath); + + UsefulTestCase.edt(new Runnable() { + @Override + public void run() { + modificator.commitChanges(); + } + }); + + final SkeletonVersionChecker checker = new SkeletonVersionChecker(0); + final PySkeletonRefresher refresher = new PySkeletonRefresher(project, null, sdk, skeletonsPath, null, null); + final List<String> errors = refresher.regenerateSkeletons(checker, null); + Assert.assertThat("Errors found", errors, Matchers.empty()); + } +} diff --git a/python/testSrc/com/jetbrains/python/sdkTools/SdkCreationType.java b/python/testSrc/com/jetbrains/python/sdkTools/SdkCreationType.java new file mode 100644 index 000000000000..231f243f564e --- /dev/null +++ b/python/testSrc/com/jetbrains/python/sdkTools/SdkCreationType.java @@ -0,0 +1,20 @@ +package com.jetbrains.python.sdkTools; + +/** + * SDK creation type + * @author Ilya.Kazakevich + */ +public enum SdkCreationType { + /** + * SDK only (no packages nor skeletons) + */ + EMPTY_SDK, + /** + * SDK + installed packages from syspath + */ + SDK_PACKAGES_ONLY, + /** + * SDK + installed packages from syspath + skeletons + */ + SDK_PACKAGES_AND_SKELETONS +} diff --git a/python/testSrc/com/jetbrains/python/sdkTools/package-info.java b/python/testSrc/com/jetbrains/python/sdkTools/package-info.java new file mode 100644 index 000000000000..8b8fb74b9167 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/sdkTools/package-info.java @@ -0,0 +1,6 @@ +/** + * Engine to create SDK in tests. + * See {@link com.jetbrains.python.sdkTools.PyTestSdkTools} + * @author Ilya.Kazakevich + */ +package com.jetbrains.python.sdkTools;
\ No newline at end of file |