PY-23746, PY-23536, PY-23534, PY-13191: Use `ParametersListUtil` to parse arguments

* ``ParametersListUtil`` supports escape, quotes and other useful stuff.
This commit is contained in:
Ilya.Kazakevich
2017-04-17 21:07:58 +03:00
parent d4d6d6a69d
commit eb714de2fa
4 changed files with 47 additions and 144 deletions
@@ -1,75 +0,0 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.testing.universalTests
import com.intellij.execution.ExecutionException
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiErrorElement
import com.intellij.psi.PsiFileFactory
import com.jetbrains.commandInterface.commandLine.CommandLineLanguage
import com.jetbrains.commandInterface.commandLine.CommandLinePart
import com.jetbrains.commandInterface.commandLine.psi.CommandLineArgument
import com.jetbrains.commandInterface.commandLine.psi.CommandLineFile
import com.jetbrains.commandInterface.commandLine.psi.CommandLineOption
import java.util.*
/**
* @author Ilya.Kazakevich
*/
//TODO: Migrate to [ParametersListUtil#parse] but support single quotes
/**
* Emulates command line processor (cmd, bash) by parsing command line to arguments that can be provided as argv.
* Escape chars are not supported but quotes work.
* @throws ExecutionException if can't be parsed
*/
fun getParsedAdditionalArguments(project: Project, additionalArguments: String): List<String> {
val factory = PsiFileFactory.getInstance(project)
val file = factory.createFileFromText(CommandLineLanguage.INSTANCE,
String.format("fake_command %s", additionalArguments)) as CommandLineFile
if (file.children.any { it is PsiErrorElement }) {
throw ExecutionException("Additional arguments can't be parsed. Please check they are valid: $additionalArguments")
}
val additionalArgsList = ArrayList<String>()
var skipArgument = false
file.children.filterIsInstance(CommandLinePart::class.java).forEach {
when (it) {
is CommandLineOption -> {
val optionText = it.text
val possibleArgument = it.findArgument()
if (possibleArgument != null) {
additionalArgsList.add(optionText + possibleArgument.valueNoQuotes)
skipArgument = true
}
else {
additionalArgsList.add(optionText)
}
}
is CommandLineArgument -> {
if (!skipArgument) {
additionalArgsList.add(it.valueNoQuotes)
}
skipArgument = false
}
}
}
return additionalArgsList
}
@@ -50,15 +50,13 @@ import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.QualifiedName
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.listeners.UndoRefactoringElementAdapter
import com.intellij.util.execution.ParametersListUtil
import com.jetbrains.extensions.getQName
import com.jetbrains.extenstions.QNameResolveContext
import com.jetbrains.extenstions.splitNameParts
import com.jetbrains.extenstions.toElement
import com.jetbrains.python.PyBundle
import com.jetbrains.python.psi.PyClass
import com.jetbrains.python.psi.PyFile
import com.jetbrains.python.psi.PyFunction
import com.jetbrains.python.psi.PyQualifiedNameOwner
import com.jetbrains.python.psi.*
import com.jetbrains.python.psi.types.TypeEvalContext
import com.jetbrains.python.run.AbstractPythonRunConfiguration
import com.jetbrains.python.run.CommandLinePatcher
@@ -491,7 +489,7 @@ abstract class PyUniversalTestConfiguration(project: Project,
private fun generateRawArguments(forRerun: Boolean = false): List<String> {
val rawArguments = additionalArguments + " " + getCustomRawArgumentsString(forRerun)
if (rawArguments.isNotBlank()) {
return listOf("--") + getParsedAdditionalArguments(project, rawArguments)
return listOf("--") + ParametersListUtil.parse(rawArguments, false, true)
}
return emptyList()
}
@@ -65,36 +65,16 @@ public final class PythonNoseTestingTest extends PyEnvTestCase {
// Ensure slow test is not run when --attr="!slow" is provided
@Test
public void testMarkerWithSlow() throws Exception {
runPythonTest(
new PyProcessWithConsoleTestTask<PyNoseTestProcessRunner>("/testRunner/env/nose/test_with_slow", SdkCreationType.EMPTY_SDK) {
@NotNull
@Override
protected PyNoseTestProcessRunner createProcessRunner() throws Exception {
return new PyNoseTestProcessRunner("test_with_slow.py", 0) {
@Override
protected void configurationCreatedAndWillLaunch(@NotNull PyUniversalNoseTestConfiguration configuration) throws IOException {
super.configurationCreatedAndWillLaunch(configuration);
configuration.setAdditionalArguments("--attr=\"!slow\"");
}
};
}
@Override
protected void checkTestResults(@NotNull PyNoseTestProcessRunner runner,
@NotNull String stdout,
@NotNull String stderr,
@NotNull String all) {
Assert.assertEquals("--slow runner borken", "Test tree:\n" +
"[root]\n" +
".test_with_slow\n" +
"..test_fast(+)\n",
runner.getFormattedTestTree());
}
});
runPythonTest(new SlowRunnerTask("--attr=\"!slow\" -vvv"));
}
@Test
public void testMarkerWithSlowSingleQuotes() throws Exception {
runPythonTest(new SlowRunnerTask("--attr='!slow' -vvv"));
}
@Test
public void testMarkerWithSlowRegexp() throws Exception {
runPythonTest(new SlowRunnerTask("--attr='!slow' -vvv -m \"(?:^|[\\b_\\./-])[Tt]est\""));
}
@Test
public void testMultipleCases() throws Exception {
@@ -225,4 +205,39 @@ public final class PythonNoseTestingTest extends PyEnvTestCase {
}
});
}
private static class SlowRunnerTask extends PyProcessWithConsoleTestTask<PyNoseTestProcessRunner> {
@NotNull
private final String myArguments;
SlowRunnerTask(@NotNull final String arguments) {
super("/testRunner/env/nose/test_with_slow", SdkCreationType.EMPTY_SDK);
myArguments = arguments;
}
@NotNull
@Override
protected PyNoseTestProcessRunner createProcessRunner() throws Exception {
return new PyNoseTestProcessRunner("test_with_slow.py", 0) {
@Override
protected void configurationCreatedAndWillLaunch(@NotNull PyUniversalNoseTestConfiguration configuration) throws IOException {
super.configurationCreatedAndWillLaunch(configuration);
configuration.setAdditionalArguments(myArguments);
}
};
}
@Override
protected void checkTestResults(@NotNull PyNoseTestProcessRunner runner,
@NotNull String stdout,
@NotNull String stderr,
@NotNull String all) {
Assert.assertEquals("--slow runner broken on arguments" + myArguments, "Test tree:\n" +
"[root]\n" +
".test_with_slow\n" +
"..test_fast(+)\n",
runner.getFormattedTestTree());
}
}
}
@@ -1,35 +0,0 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.testing.universalTests
import com.jetbrains.python.fixtures.PyTestCase
import org.junit.Assert
import org.junit.Test
/**
* @author Ilya.Kazakevich
*/
class PyTestRunnerUtilsKtTest : PyTestCase() {
@Test
fun testGetParsedAdditionalArguments() {
var list = getParsedAdditionalArguments(myFixture.project, "-v --color=red -m 'spam and eggs'")
Assert.assertEquals("List parsed incorrectly", listOf("-v", "--color=red", "-m", "spam and eggs"), list)
list = getParsedAdditionalArguments(myFixture.project, "--eggs=spam --foo=\"eggs and spam\"")
Assert.assertEquals("List parsed incorrectly", listOf("--eggs=spam", "--foo=eggs and spam"), list)
}
}