diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/AiGenerated.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/AiGenerated.kt index a4e2baf..d8af6f1 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/AiGenerated.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/AiGenerated.kt @@ -133,6 +133,8 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_nexthop_section", "NEXTHOP_GROUP") to ConfigParseNexthopSectionOptionValue() as OptionValueInformation, Validator("config_parse_nexthop_section", "NEXTHOP_ID") to ConfigParseNexthopSectionOptionValue() as OptionValueInformation, Validator("config_parse_nexthop_section", "NEXTHOP_ONLINK") to ConfigParseNexthopSectionOptionValue() as OptionValueInformation, + Validator("config_parse_nsec", "0") to ConfigParseNsecOptionValue() as OptionValueInformation, + Validator("config_parse_permille", "0") to ConfigParsePermilleOptionValue() as OptionValueInformation, Validator("config_parse_pfifo_size", "QDISC_KIND_PFIFO") to ConfigParsePfifoSizeOptionValue() as OptionValueInformation, Validator("config_parse_pfifo_size", "QDISC_KIND_PFIFO_HEAD_DROP") to ConfigParsePfifoSizeOptionValue() as OptionValueInformation, Validator("config_parse_pid2", "0") to ConfigParsePid2OptionValue() as OptionValueInformation, @@ -167,6 +169,8 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_sr_iov_num_vfs", "0") to ConfigParseSrIovNumVfsOptionValue() as OptionValueInformation, Validator("config_parse_sr_iov_vlan_proto", "0") to ConfigParseSrIovVlanProtoOptionValue() as OptionValueInformation, Validator("config_parse_swap_priority", "0") to ConfigParseSwapPriorityOptionValue() as OptionValueInformation, + Validator("config_parse_tasks_max", "0") to ConfigParseTasksMaxOptionValue() as OptionValueInformation, + Validator("config_parse_tbf_size", "QDISC_KIND_TBF") to ConfigParseTbfSizeOptionValue() as OptionValueInformation, Validator("config_parse_tcp_window", "0") to ConfigParseTcpWindowOptionValue() as OptionValueInformation, Validator("config_parse_timezone_mode", "0") to ConfigParseTimezoneModeOptionValue() as OptionValueInformation, Validator("config_parse_trigger_unit", "0") to ConfigParseTriggerUnitOptionValue() as OptionValueInformation, @@ -176,6 +180,7 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_unit_condition_string", "CONDITION_CONTROL_GROUP_CONTROLLER") to ConfigParseUnitConditionStringOptionValue() as OptionValueInformation, Validator("config_parse_unit_condition_string", "CONDITION_CPU_FEATURE") to ConfigParseUnitConditionStringOptionValue() as OptionValueInformation, Validator("config_parse_unit_condition_string", "CONDITION_FIRST_BOOT") to ConfigParseUnitConditionStringOptionValue() as OptionValueInformation, + Validator("config_parse_unit_slice", "0") to ConfigParseUnitSliceOptionValue() as OptionValueInformation, Validator("config_parse_use_domains", "0") to ConfigParseUseDomainsOptionValue() as OptionValueInformation, Validator("config_parse_userns_chown", "0") to ConfigParseUsernsChownOptionValue() as OptionValueInformation, Validator("config_parse_userns_ownership", "0") to ConfigParseUsernsOwnershipOptionValue() as OptionValueInformation, diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseNsecOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseNsecOptionValue.kt new file mode 100644 index 0000000..a9f796c --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseNsecOptionValue.kt @@ -0,0 +1,21 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* + +/** + * Validator for nanosecond-time options such as TimerSlackNSec=. + * + * C function: defined via DEFINE_PARSER(nsec, ..., parse_nsec) in src/shared/conf-parser.c, + * which delegates to parse_nsec in src/basic/time-util.c. parse_nsec accepts "infinity" or + * one or more numeric terms with optional unit suffix (s/ms/us/ns/min/h/d/w/M/y...). The same + * syntax that TIME_VALUE encodes for the parse_sec family applies here; only the default + * (suffix-less) multiplier differs. + */ +class ConfigParseNsecOptionValue : SimpleGrammarOptionValues( + "config_parse_nsec", + SequenceCombinator( + OptionalWhitespacePrefix(TIME_VALUE), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParsePermilleOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParsePermilleOptionValue.kt new file mode 100644 index 0000000..3eda554 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParsePermilleOptionValue.kt @@ -0,0 +1,27 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* + +/** + * Validator for permille (parts-per-thousand) options such as CAN.SamplePoint=. + * + * C function: config_parse_permille in src/shared/conf-parser.c → parse_permille in + * src/basic/percent-util.c. Accepts either: + * - "N‰" (integer 0..1000) + * - "N%" or "N.x%" (0..100% via tenths place, internally translated to 0..1000) + * + * Only the ASCII percent form is matched here; the ‰ Unicode suffix is syntactically rare. + */ +class ConfigParsePermilleOptionValue : SimpleGrammarOptionValues( + "config_parse_permille", + SequenceCombinator( + AlternativeCombinator( + // 0..99(.x)?% + SequenceCombinator(IntegerTerminal(0, 100), ZeroOrOne(RegexTerminal("\\.[0-9]", "\\.[0-9]")), LiteralChoiceTerminal("%")), + // 100% or 100.0% + SequenceCombinator(LiteralChoiceTerminal("100"), ZeroOrOne(LiteralChoiceTerminal(".0")), LiteralChoiceTerminal("%")) + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTasksMaxOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTasksMaxOptionValue.kt new file mode 100644 index 0000000..7905131 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTasksMaxOptionValue.kt @@ -0,0 +1,37 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* + +/** + * Validator for TasksMax=. + * + * C function: config_parse_tasks_max in src/core/load-fragment.c. The accepted values are: + * - "infinity" + * - a permyriad form: parse_permyriad accepts N%, N.N%, N.NN%, N‰, N.N‰, or N‱ + * (bounded 0..10000). Only the ASCII percent form is matched here; the ‰/‱ Unicode + * suffixes are syntactically rare in unit files. + * - a strictly positive uint64 via safe_atou64 (>0 and abc + TimerSlackNSec=-1 + TimerSlackNSec=10zz + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(3, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParsePermilleOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParsePermilleOptionValueTest.kt new file mode 100644 index 0000000..c3dcfc0 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParsePermilleOptionValueTest.kt @@ -0,0 +1,52 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest +import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection +import org.junit.Test + +class ConfigParsePermilleOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [CAN] + SamplePoint=0% + SamplePoint=50% + SamplePoint=87.5% + SamplePoint=99% + SamplePoint=99.9% + SamplePoint=100% + SamplePoint=100.0% + DataSamplePoint=75% + DataSamplePoint=12.3% + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // language="unit file (systemd)" + val file = """ + [CAN] + SamplePoint=50 + SamplePoint=100.5% + SamplePoint=101% + SamplePoint=200% + SamplePoint=abc + SamplePoint=50.12% + SamplePoint=-5% + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(7, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTasksMaxOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTasksMaxOptionValueTest.kt new file mode 100644 index 0000000..f6ed454 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTasksMaxOptionValueTest.kt @@ -0,0 +1,54 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest +import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection +import org.junit.Test + +class ConfigParseTasksMaxOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + TasksMax=infinity + TasksMax=1 + TasksMax=512 + TasksMax=999999999 + TasksMax=0% + TasksMax=50% + TasksMax=99.9% + TasksMax=12.34% + TasksMax=100% + TasksMax=100.0% + TasksMax=100.00% + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + TasksMax=0 + TasksMax=-1 + TasksMax=abc + TasksMax=100.5% + TasksMax=250% + TasksMax=50.123% + TasksMax=infinity 1 + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(7, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTbfSizeOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTbfSizeOptionValueTest.kt new file mode 100644 index 0000000..f900d04 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTbfSizeOptionValueTest.kt @@ -0,0 +1,46 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest +import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection +import org.junit.Test + +class ConfigParseTbfSizeOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [TokenBucketFilter] + BurstBytes=1024 + LimitBytes=64K + MTUBytes=1500 + MPUBytes=64 + BurstBytes=1M + LimitBytes=1G + MTUBytes=2T + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // language="unit file (systemd)" + val file = """ + [TokenBucketFilter] + BurstBytes=abc + LimitBytes=-1 + MTUBytes=10X + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(3, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUnitSliceOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUnitSliceOptionValueTest.kt new file mode 100644 index 0000000..9c120e5 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUnitSliceOptionValueTest.kt @@ -0,0 +1,49 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest +import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection +import org.junit.Test + +class ConfigParseUnitSliceOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + Slice=system.slice + Slice=user-1000.slice + Slice=user.slice + Slice=machine.slice + Slice=app-myapp.slice + Slice=-.slice + Slice=%p.slice + Slice=user-%i.slice + Slice=%n + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + Slice=system.target + Slice=system.service + Slice=noextension + Slice=system.slice extra + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(4, highlights) + } +}