Skip to content

Commit 63006d9

Browse files
SJrXSteve Ramage
andauthored
feat: Add validation support for 28 new options controlling unit output and directories (Resolves #281)
Co-authored-by: Steve Ramage <[email protected]>
1 parent b0bd442 commit 63006d9

File tree

6 files changed

+333
-2
lines changed

6 files changed

+333
-2
lines changed

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class SemanticDataRepository private constructor() {
7474
validatorMap.putAll(DocumentationOptionValue.validators)
7575
validatorMap.putAll(ModeStringOptionValue.validators)
7676
validatorMap.putAll(ExecOptionValue.validators)
77+
validatorMap.putAll(ExecOutputOptionValue.validators)
7778
validatorMap.putAll(UnitDependencyOptionValue.validators)
7879
validatorMap.putAll(NullOptionValue.validators)
7980
validatorMap.putAll(MemoryLimitOptionValue.validators)
@@ -85,6 +86,7 @@ class SemanticDataRepository private constructor() {
8586
validatorMap.putAll(ConfigParseSecValidators.validators)
8687
validatorMap.putAll(AllowedCpuSetOptionValue.validators)
8788
validatorMap.putAll(TtySizeOptionValue.validators)
89+
validatorMap.putAll(ExecDirectoriesOptionValue.validators)
8890

8991
// Scopes are not supported since they aren't standard unit files.
9092

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues
2+
3+
import com.intellij.openapi.project.Project
4+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator
5+
6+
class ExecDirectoriesOptionValue : OptionValueInformation {
7+
8+
override fun getAutoCompleteOptions(project: Project): Set<String> {
9+
return emptySet()
10+
}
11+
12+
override fun getErrorMessage(value: String): String? {
13+
val values = value.split("\\s+")
14+
15+
// loop over element in values
16+
for (element in values) {
17+
if (element.startsWith("/")) {
18+
return "Invalid path: '${element}'. Takes a list of absolute paths."
19+
} else if (element.contains("..")) {
20+
return "Invalid path: '${element}'. Path cannot contain `..`."
21+
}
22+
continue
23+
}
24+
25+
return null
26+
}
27+
28+
29+
override val validatorName: String
30+
get() = VALIDATOR_NAME
31+
32+
companion object {
33+
private val VALIDATOR_NAME = "config_parse_exec_directories"
34+
35+
val validators = mapOf(Validator(VALIDATOR_NAME, "0") to ExecDirectoriesOptionValue())
36+
}
37+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues
2+
3+
import com.intellij.openapi.project.Project
4+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator
5+
6+
class ExecOutputOptionValue() : OptionValueInformation {
7+
override fun getAutoCompleteOptions(project: Project): Set<String> {
8+
return emptySet()
9+
}
10+
11+
override fun getErrorMessage(value: String): String? {
12+
13+
if (value.startsWith("file:") || value.startsWith("append:") || value.startsWith("truncate:") || value.startsWith("fd:")) {
14+
val firstArg = value.split(":", limit = 2)[0]
15+
val secondArg = value.split(":", limit = 2)[1]
16+
17+
if (secondArg.trim().isEmpty()) {
18+
when(firstArg) {
19+
"file", "append", "truncate" -> return "path must be specified after the colon."
20+
"fd" -> return "name must be specified after the colon."
21+
}
22+
}
23+
24+
// We could do a better job of validating paths later
25+
return null
26+
}
27+
28+
when (value) {
29+
"inherit", "null", "tty", "journal", "kmsg", "journal+console", "kmsg+console", "socket" -> return null
30+
else -> return "Takes one of inherit, null, tty, journal, kmsg, journal+console, kmsg+console, file:path, append:path, truncate:path, socket or fd:name."
31+
32+
}
33+
}
34+
35+
override val validatorName: String
36+
get() = VALIDATOR_NAME
37+
38+
39+
companion object {
40+
private val VALIDATOR_NAME = "config_parse_exec_output"
41+
42+
val validators = mapOf(Validator(VALIDATOR_NAME, "0") to ExecOutputOptionValue())
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.inspections
2+
3+
import junit.framework.TestCase
4+
import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest
5+
6+
class InvalidValueInspectionForExecDirectoriesOptionsTest : AbstractUnitFileTest() {
7+
8+
fun testNoWarningWhenUsingASingleRelativeDirectory() {
9+
// Fixture Setup
10+
// language="unit file (systemd)"
11+
val file = """
12+
[Service]
13+
RuntimeDirectory=foo
14+
15+
""".trimIndent()
16+
17+
18+
// Execute SUT
19+
setupFileInEditor("file.service", file)
20+
enableInspection(InvalidValueInspection::class.java)
21+
val highlights = myFixture.doHighlighting()
22+
23+
// Verification
24+
assertSize(0, highlights)
25+
}
26+
27+
fun testNoWarningWhenUsingSeveralRelativeDirectories() {
28+
// Fixture Setup
29+
// language="unit file (systemd)"
30+
val file = """
31+
[Service]
32+
RuntimeDirectory=foo bar \
33+
hello
34+
35+
""".trimIndent()
36+
37+
38+
// Execute SUT
39+
setupFileInEditor("file.service", file)
40+
enableInspection(InvalidValueInspection::class.java)
41+
val highlights = myFixture.doHighlighting()
42+
43+
// Verification
44+
assertSize(0, highlights)
45+
}
46+
47+
fun testWarningWhenUsingASingleAbsoluteDirectory() {
48+
// Fixture Setup
49+
// language="unit file (systemd)"
50+
val file = """
51+
[Service]
52+
RuntimeDirectory=/tmp/myunit
53+
54+
""".trimIndent()
55+
56+
57+
// Execute SUT
58+
setupFileInEditor("file.service", file)
59+
enableInspection(InvalidValueInspection::class.java)
60+
val highlights = myFixture.doHighlighting()
61+
62+
// Verification
63+
assertSize(1, highlights)
64+
val info = highlights[0]
65+
AbstractUnitFileTest.Companion.assertStringContains("Takes a list of absolute paths", info!!.description)
66+
TestCase.assertEquals("/tmp/myunit", info.text)
67+
}
68+
69+
fun testWarningWhenUsingSeveralAbsoluteDirectory() {
70+
// Fixture Setup
71+
// language="unit file (systemd)"
72+
val file = """
73+
[Service]
74+
RuntimeDirectory=/tmp/myunit /tmp/myunit2
75+
76+
""".trimIndent()
77+
78+
79+
// Execute SUT
80+
setupFileInEditor("file.service", file)
81+
enableInspection(InvalidValueInspection::class.java)
82+
val highlights = myFixture.doHighlighting()
83+
84+
// Verification
85+
assertSize(1, highlights)
86+
val info = highlights[0]
87+
AbstractUnitFileTest.Companion.assertStringContains("Takes a list of absolute paths", info!!.description)
88+
TestCase.assertEquals("/tmp/myunit /tmp/myunit2", info.text)
89+
}
90+
91+
fun testWarningWhenUsingParentDirectoryOperator() {
92+
// Fixture Setup
93+
// language="unit file (systemd)"
94+
val file = """
95+
[Service]
96+
RuntimeDirectory=../myunit
97+
98+
""".trimIndent()
99+
100+
101+
// Execute SUT
102+
setupFileInEditor("file.service", file)
103+
enableInspection(InvalidValueInspection::class.java)
104+
val highlights = myFixture.doHighlighting()
105+
106+
// Verification
107+
assertSize(1, highlights)
108+
val info = highlights[0]
109+
AbstractUnitFileTest.Companion.assertStringContains("Path cannot contain `..`", info!!.description)
110+
TestCase.assertEquals("../myunit", info.text)
111+
}
112+
113+
fun testWarningWhenUsingParentDirectoryOperatorInAThirdDirectory() {
114+
// Fixture Setup
115+
// language="unit file (systemd)"
116+
val file = """
117+
[Service]
118+
RuntimeDirectory=foo/bar test/tmp/ ../myunit
119+
120+
""".trimIndent()
121+
122+
123+
// Execute SUT
124+
setupFileInEditor("file.service", file)
125+
enableInspection(InvalidValueInspection::class.java)
126+
val highlights = myFixture.doHighlighting()
127+
128+
// Verification
129+
assertSize(1, highlights)
130+
val info = highlights[0]
131+
AbstractUnitFileTest.Companion.assertStringContains("Path cannot contain `..`", info!!.description)
132+
TestCase.assertEquals("foo/bar test/tmp/ ../myunit", info.text)
133+
}
134+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.inspections
2+
3+
import junit.framework.TestCase
4+
import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest
5+
6+
class InvalidValueInspectionForExecOutputOptionsTest : AbstractUnitFileTest() {
7+
8+
9+
fun testNoWarningWhenUsingValidValuesForOutput() {
10+
runValidValueTest("inherit")
11+
runValidValueTest("null")
12+
runValidValueTest("tty")
13+
runValidValueTest("journal")
14+
runValidValueTest("kmsg")
15+
runValidValueTest("journal+console")
16+
runValidValueTest("kmsg+console")
17+
runValidValueTest("file:/dev/null")
18+
runValidValueTest("append:/tmp/log.txt")
19+
runValidValueTest("truncate:/tmp/path")
20+
runValidValueTest("socket")
21+
runValidValueTest("fd:stdout")
22+
}
23+
24+
fun runValidValueTest(validValue : String) {
25+
// Fixture Setup
26+
// language="unit file (systemd)"
27+
val file = """
28+
[Service]
29+
StandardOutput=$validValue
30+
31+
""".trimIndent()
32+
33+
34+
// Execute SUT
35+
setupFileInEditor("file.service", file)
36+
enableInspection(InvalidValueInspection::class.java)
37+
val highlights = myFixture.doHighlighting()
38+
39+
// Verification
40+
assertSize(0, highlights)
41+
}
42+
43+
44+
fun testWeakWarningWhenUsingInvalidValueForExecOption() {
45+
// Fixture Setup
46+
// language="unit file (systemd)"
47+
val file = """
48+
[Service]
49+
StandardOutput=foo
50+
51+
""".trimIndent()
52+
53+
54+
// Execute SUT
55+
setupFileInEditor("file.service", file)
56+
enableInspection(InvalidValueInspection::class.java)
57+
val highlights = myFixture.doHighlighting()
58+
59+
// Verification
60+
assertSize(1, highlights)
61+
val info = highlights[0]
62+
AbstractUnitFileTest.Companion.assertStringContains("Takes one of", info!!.description)
63+
TestCase.assertEquals("foo", info.text)
64+
}
65+
66+
fun testWeakWarningWhenMissingSecondArgumentForFd() {
67+
// Fixture Setup
68+
// language="unit file (systemd)"
69+
val file = """
70+
[Service]
71+
StandardOutput=fd:
72+
73+
""".trimIndent()
74+
75+
76+
// Execute SUT
77+
setupFileInEditor("file.service", file)
78+
enableInspection(InvalidValueInspection::class.java)
79+
val highlights = myFixture.doHighlighting()
80+
81+
// Verification
82+
assertSize(1, highlights)
83+
val info = highlights[0]
84+
AbstractUnitFileTest.Companion.assertStringContains("name must be specified after the colon", info!!.description)
85+
TestCase.assertEquals("fd:", info.text)
86+
}
87+
88+
fun testWeakWarningWhenMissingSecondArgumentForFile() {
89+
// Fixture Setup
90+
// language="unit file (systemd)"
91+
val file = """
92+
[Service]
93+
StandardOutput=file:
94+
95+
""".trimIndent()
96+
97+
98+
// Execute SUT
99+
setupFileInEditor("file.service", file)
100+
enableInspection(InvalidValueInspection::class.java)
101+
val highlights = myFixture.doHighlighting()
102+
103+
// Verification
104+
assertSize(1, highlights)
105+
val info = highlights[0]
106+
AbstractUnitFileTest.Companion.assertStringContains("path must be specified after the colon", info!!.description)
107+
TestCase.assertEquals("file:", info.text)
108+
}
109+
110+
}

src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/OptionValueTest.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class OptionValueTest : AbstractUnitFileTest() {
1212

1313
val missingValidators = hashMapOf<Validator, Int>()
1414
var totalMissingValidators = 0
15+
var totalFoundValidators = 0
1516
for (sectionName in SemanticDataRepository.instance.sectionNamesFromValidators) {
1617
for (key in SemanticDataRepository.instance.getAllowedKeywordsInSectionFromValidators(sectionName)) {
1718

@@ -21,6 +22,8 @@ class OptionValueTest : AbstractUnitFileTest() {
2122
if (!validatorMap.containsKey(validator)) {
2223
missingValidators[validator] = (missingValidators[validator] ?: 0) + 1
2324
totalMissingValidators++
25+
} else {
26+
totalFoundValidators++
2427
}
2528
}
2629
}
@@ -29,8 +32,9 @@ class OptionValueTest : AbstractUnitFileTest() {
2932

3033
val sortedList = missingValidatorList.sortedDescending().joinToString("\n")
3134

32-
println(totalMissingValidators)
33-
if (totalMissingValidators > 630) {
35+
println("Missing:$totalMissingValidators")
36+
println("Found:$totalFoundValidators")
37+
if (totalMissingValidators > 600) {
3438
assertEquals(sortedList, "")
3539
}
3640

0 commit comments

Comments
 (0)