From fc34c5ba4eb5f4e6e6d520c225d5b570506d7a84 Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Thu, 30 Apr 2026 19:15:14 -0700 Subject: [PATCH 1/5] feat: import 5 HIGH-confidence AI validators (verified against systemd source) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First batch of HIGH-confidence validators from the LLM batch processor, manually verified and corrected against the systemd C source and gperf data: - config_parse_string (CONFIG_PARSE_STRING_SAFE) — string_is_safe semantics - config_parse_bind_user_shell — boolean OR absolute path (parse_user_shell) - config_parse_cake_priority_queueing_preset — adds missing diffserv3 - config_parse_hostname — proper hostname_is_valid grammar - config_parse_device_allow — /dev/path or block-/char- + optional [rwm]+ perms Each AI generation had bugs (wrong section names, hallucinated grammar shape, or grammars not matching the actual C validation function). Tests rewritten to use the real (section, key) pairs from the gperf source. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../semanticdata/optionvalues/AiGenerated.kt | 5 ++ .../ai/ConfigParseBindUserShellOptionValue.kt | 23 +++++++ ...seCakePriorityQueueingPresetOptionValue.kt | 19 ++++++ .../ai/ConfigParseDeviceAllowOptionValue.kt | 29 +++++++++ .../ai/ConfigParseHostnameOptionValue.kt | 25 ++++++++ .../ai/ConfigParseStringOptionValue.kt | 24 ++++++++ ...ConfigParseBindUserShellOptionValueTest.kt | 61 +++++++++++++++++++ ...kePriorityQueueingPresetOptionValueTest.kt | 42 +++++++++++++ .../ConfigParseDeviceAllowOptionValueTest.kt | 46 ++++++++++++++ .../ai/ConfigParseHostnameOptionValueTest.kt | 47 ++++++++++++++ .../ai/ConfigParseStringOptionValueTest.kt | 45 ++++++++++++++ 11 files changed, 366 insertions(+) create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseBindUserShellOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakePriorityQueueingPresetOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDeviceAllowOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseHostnameOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseStringOptionValue.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseBindUserShellOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakePriorityQueueingPresetOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDeviceAllowOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseHostnameOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseStringOptionValueTest.kt 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 0d17110..98eacc3 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 @@ -186,6 +186,11 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_wlan_iftype", "0") to ConfigParseWlanIftypeOptionValue() as OptionValueInformation, Validator("config_parse_ad_actor_system", "0") to ConfigParseAdActorSystemOptionValue() as OptionValueInformation, Validator("config_parse_address_section", "ADDRESS_SCOPE") to ConfigParseAddressSectionAddressScopeOptionValue() as OptionValueInformation, + Validator("config_parse_string", "CONFIG_PARSE_STRING_SAFE") to ConfigParseStringOptionValue() as OptionValueInformation, + Validator("config_parse_bind_user_shell", "0") to ConfigParseBindUserShellOptionValue() as OptionValueInformation, + Validator("config_parse_cake_priority_queueing_preset", "QDISC_KIND_CAKE") to ConfigParseCakePriorityQueueingPresetOptionValue() as OptionValueInformation, + Validator("config_parse_hostname", "0") to ConfigParseHostnameOptionValue() as OptionValueInformation, + Validator("config_parse_device_allow", "0") to ConfigParseDeviceAllowOptionValue() as OptionValueInformation, ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseBindUserShellOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseBindUserShellOptionValue.kt new file mode 100644 index 0000000..4bfafa1 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseBindUserShellOptionValue.kt @@ -0,0 +1,23 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for Files.BindUserShell (.nspawn). + * C Function: config_parse_bind_user_shell(0) + * + * The C path calls parse_user_shell which accepts either: + * - an absolute, normalized path (path_is_absolute && path_is_normalized), or + * - a boolean (parse_boolean) + */ +class ConfigParseBindUserShellOptionValue : SimpleGrammarOptionValues( + "config_parse_bind_user_shell", + SequenceCombinator( + AlternativeCombinator( + BOOLEAN, + RegexTerminal("/[^\\s]+", "/[^\\s]+") + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakePriorityQueueingPresetOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakePriorityQueueingPresetOptionValue.kt new file mode 100644 index 0000000..f21ecf1 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakePriorityQueueingPresetOptionValue.kt @@ -0,0 +1,19 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for CAKE.PriorityQueueingPreset (.network). + * C Function: config_parse_cake_priority_queueing_preset(QDISC_KIND_CAKE) + * + * Accepts the 5 entries in cake_priority_queueing_preset_table: + * besteffort, precedence, diffserv3, diffserv4, diffserv8. + */ +class ConfigParseCakePriorityQueueingPresetOptionValue : SimpleGrammarOptionValues( + "config_parse_cake_priority_queueing_preset", + SequenceCombinator( + FlexibleLiteralChoiceTerminal("besteffort", "precedence", "diffserv3", "diffserv4", "diffserv8"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDeviceAllowOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDeviceAllowOptionValue.kt new file mode 100644 index 0000000..e117c01 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDeviceAllowOptionValue.kt @@ -0,0 +1,29 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for {Service,Socket,Mount,Swap,Slice,Scope}.DeviceAllow. + * C Function: config_parse_device_allow(0) + * + * Per valid_device_allow_pattern + cgroup_device_permissions_from_string: + * - device specifier is either a /dev/... path or a block-/char- prefixed device class + * - optional whitespace + permissions, where permissions is a single token of [rwm]+ + */ +class ConfigParseDeviceAllowOptionValue : SimpleGrammarOptionValues( + "config_parse_device_allow", + SequenceCombinator( + AlternativeCombinator( + RegexTerminal("(block-|char-)\\S+", "(block-|char-)\\S+"), + RegexTerminal("/dev/\\S+", "/dev/\\S+") + ), + ZeroOrOne( + SequenceCombinator( + WhitespaceTerminal(), + RegexTerminal("[rwm]+", "[rwm]+") + ) + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseHostnameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseHostnameOptionValue.kt new file mode 100644 index 0000000..58a7e7a --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseHostnameOptionValue.kt @@ -0,0 +1,25 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for config_parse_hostname(0). + * Used by Options: Exec.Hostname (.nspawn), DHCPv4.Hostname, DHCPv6.Hostname, DHCP.Hostname (.network). + * + * Mirrors hostname_is_valid(s, 0): + * - LDH chars only (letters, digits, hyphen) + * - dots between labels, no leading or consecutive dots + * - no leading or trailing hyphen on a label + * - no trailing dot (VALID_HOSTNAME_TRAILING_DOT not set) + */ +class ConfigParseHostnameOptionValue : SimpleGrammarOptionValues( + "config_parse_hostname", + SequenceCombinator( + RegexTerminal( + "[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*", + "[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*" + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseStringOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseStringOptionValue.kt new file mode 100644 index 0000000..def5919 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseStringOptionValue.kt @@ -0,0 +1,24 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for config_parse_string with CONFIG_PARSE_STRING_SAFE. + * Used by Options: Tun.User, Tun.Group, Tap.User, Tap.Group, Exec.User (nspawn), + * DHCPv4.VendorClassIdentifier, *.NetLabel, Manager.RuntimeWatchdogPreGovernor, ... + * + * The C path is config_parse_string -> string_is_safe(rvalue, STRING_ALLOW_GLOBS): + * - rejects control characters (0..31, 127) + * - rejects backslash + * - rejects quotes (" and ') + * - allows globs (*, ?, [) + * - empty resets and is always valid (handled outside the grammar) + */ +class ConfigParseStringOptionValue : SimpleGrammarOptionValues( + "config_parse_string", + SequenceCombinator( + RegexTerminal("[^\\x00-\\x1F\\x7F\"'\\\\]+", "[^\\x00-\\x1F\\x7F\"'\\\\]+"), + EOF() + ) +) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseBindUserShellOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseBindUserShellOptionValueTest.kt new file mode 100644 index 0000000..e9b147f --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseBindUserShellOptionValueTest.kt @@ -0,0 +1,61 @@ +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 ConfigParseBindUserShellOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidBooleans() { + // language="unit file (systemd)" + val file = """ + [Files] + BindUserShell=yes + BindUserShell=no + BindUserShell=true + BindUserShell=false + BindUserShell=on + BindUserShell=off + BindUserShell=1 + BindUserShell=0 + """.trimIndent() + + setupFileInEditor("file.nspawn", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testValidAbsolutePaths() { + // language="unit file (systemd)" + val file = """ + [Files] + BindUserShell=/bin/sh + BindUserShell=/usr/bin/zsh + BindUserShell=/bin/bash + """.trimIndent() + + setupFileInEditor("file.nspawn", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // language="unit file (systemd)" + val file = """ + [Files] + BindUserShell=invalid + BindUserShell=relative/path + BindUserShell=bin/sh + """.trimIndent() + + setupFileInEditor("file.nspawn", 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/ConfigParseCakePriorityQueueingPresetOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakePriorityQueueingPresetOptionValueTest.kt new file mode 100644 index 0000000..a68dbe3 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakePriorityQueueingPresetOptionValueTest.kt @@ -0,0 +1,42 @@ +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 ConfigParseCakePriorityQueueingPresetOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [CAKE] + PriorityQueueingPreset=besteffort + PriorityQueueingPreset=precedence + PriorityQueueingPreset=diffserv3 + PriorityQueueingPreset=diffserv4 + PriorityQueueingPreset=diffserv8 + """.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 = """ + [CAKE] + PriorityQueueingPreset=diffserv5 + PriorityQueueingPreset=invalid + PriorityQueueingPreset=diffserv + """.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/ConfigParseDeviceAllowOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDeviceAllowOptionValueTest.kt new file mode 100644 index 0000000..d757f55 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDeviceAllowOptionValueTest.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 ConfigParseDeviceAllowOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + DeviceAllow=/dev/sda1 + DeviceAllow=/dev/sda1 r + DeviceAllow=/dev/sda1 rw + DeviceAllow=/dev/sda1 rwm + DeviceAllow=/dev/dri/card0 rw + DeviceAllow=block-loop + DeviceAllow=block-loop rw + DeviceAllow=char-rtc r + """.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] + DeviceAllow=relative-path + DeviceAllow=/dev/sda1 x + DeviceAllow=/dev/sda1 rwx + DeviceAllow=block-loop xyz + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(4, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseHostnameOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseHostnameOptionValueTest.kt new file mode 100644 index 0000000..6e8cf20 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseHostnameOptionValueTest.kt @@ -0,0 +1,47 @@ +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 ConfigParseHostnameOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidHostnames() { + // language="unit file (systemd)" + val file = """ + [DHCPv4] + Hostname=myhost + Hostname=my-host + Hostname=host.example.com + Hostname=host123 + Hostname=123host + Hostname=h + Hostname=a-b-c + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testInvalidHostnames() { + // language="unit file (systemd)" + val file = """ + [DHCPv4] + Hostname=-host + Hostname=host- + Hostname=host..com + Hostname=host_name + Hostname=host! + Hostname=host name + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(6, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseStringOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseStringOptionValueTest.kt new file mode 100644 index 0000000..82e8339 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseStringOptionValueTest.kt @@ -0,0 +1,45 @@ +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 ConfigParseStringOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Tun] + User=systemd-network + User=ftp + User=user-name + User=user.with.dots + User=user with spaces + User=glob*pattern + User=glob?pattern + User=glob[abc]pattern + """.trimIndent() + + setupFileInEditor("file.netdev", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // language="unit file (systemd)" + val file = """ + [Tun] + User=user"name + User=user'name + User=user\name + """.trimIndent() + + setupFileInEditor("file.netdev", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(3, highlights) + } +} From 5b8e6f7835a0a4f7e2449b909045e106a4f8227b Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Thu, 30 Apr 2026 19:32:31 -0700 Subject: [PATCH 2/5] feat: import 10 more HIGH-confidence AI validators (verified) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Batch 2: 10 validators verified against systemd source via parallel agents and corrected. Each has been checked against gperf (real section/key/ltype) and the C parser function (real validation logic): - config_parse_wol — WakeOnLan flag list (off | space-separated flags) - config_parse_dnssec_mode — boolean | "allow-downgrade" - config_parse_protect_hostname — boolean | : form - config_parse_ip_tos — symbolic name | integer 0-255 - config_parse_ip_protocol — protocol name | integer - config_parse_dns — list of IPv4/IPv6 with optional :port and %iface - config_parse_trigger_unit — unit name with valid type suffix - config_parse_sr_iov_link_state — boolean | "auto" - config_parse_required_family_for_online — ipv4/ipv6/both/any - config_parse_ifname — interface name (max 15 chars, no whitespace/slash) Burndown test now passes — moved from 432 missing to ~417, under the 427 cap for today (2026-04-30). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../semanticdata/optionvalues/AiGenerated.kt | 10 ++ .../ai/ConfigParseDnsOptionValue.kt | 59 +++++++ .../ai/ConfigParseDnssecModeOptionValue.kt | 27 +++ .../ai/ConfigParseIfnameOptionValue.kt | 26 +++ .../ai/ConfigParseIpProtocolOptionValue.kt | 25 +++ .../ai/ConfigParseIpTosOptionValue.kt | 23 +++ .../ConfigParseProtectHostnameOptionValue.kt | 56 ++++++ ...ParseRequiredFamilyForOnlineOptionValue.kt | 15 ++ .../ConfigParseSrIovLinkStateOptionValue.kt | 27 +++ .../ai/ConfigParseTriggerUnitOptionValue.kt | 26 +++ .../ai/ConfigParseWolOptionValue.kt | 32 ++++ .../ai/ConfigParseDnsOptionValueTest.kt | 159 ++++++++++++++++++ .../ConfigParseDnssecModeOptionValueTest.kt | 62 +++++++ .../ai/ConfigParseIfnameOptionValueTest.kt | 43 +++++ .../ConfigParseIpProtocolOptionValueTest.kt | 53 ++++++ .../ai/ConfigParseIpTosOptionValueTest.kt | 138 +++++++++++++++ ...nfigParseProtectHostnameOptionValueTest.kt | 65 +++++++ ...eRequiredFamilyForOnlineOptionValueTest.kt | 42 +++++ ...onfigParseSrIovLinkStateOptionValueTest.kt | 46 +++++ .../ConfigParseTriggerUnitOptionValueTest.kt | 96 +++++++++++ .../ai/ConfigParseWolOptionValueTest.kt | 115 +++++++++++++ 21 files changed, 1145 insertions(+) create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnssecModeOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIpProtocolOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIpTosOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseProtectHostnameOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRequiredFamilyForOnlineOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSrIovLinkStateOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTriggerUnitOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseWolOptionValue.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnssecModeOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIpProtocolOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIpTosOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseProtectHostnameOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseRequiredFamilyForOnlineOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSrIovLinkStateOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTriggerUnitOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseWolOptionValueTest.kt 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 98eacc3..a7b149e 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 @@ -99,6 +99,7 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_iaid", "AF_INET") to ConfigParseIaidOptionValue() as OptionValueInformation, Validator("config_parse_in_addr_non_null", "AF_INET") to ConfigParseInAddrNonNullOptionValue() as OptionValueInformation, Validator("config_parse_ip_masquerade", "0") to ConfigParseIpMasqueradeOptionValue() as OptionValueInformation, + Validator("config_parse_ip_protocol", "true") to ConfigParseIpProtocolOptionValue() as OptionValueInformation, Validator("config_parse_ip_reverse_path_filter", "0") to ConfigParseIpReversePathFilterOptionValue() as OptionValueInformation, Validator("config_parse_ipoib_mode", "0") to ConfigParseIpoibModeOptionValue() as OptionValueInformation, Validator("config_parse_ipv4_force_igmp_version", "0") to ConfigParseIpv4ForceIgmpVersionOptionValue() as OptionValueInformation, @@ -163,11 +164,13 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_rx_tx_queues", "0") to ConfigParseRxTxQueuesOptionValue() as OptionValueInformation, Validator("config_parse_service_restart_mode", "0") to ConfigParseServiceRestartModeOptionValue() as OptionValueInformation, Validator("config_parse_socket_defer_trigger", "0") to ConfigParseSocketDeferTriggerOptionValue() as OptionValueInformation, + Validator("config_parse_sr_iov_link_state", "0") to ConfigParseSrIovLinkStateOptionValue() as OptionValueInformation, 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_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, Validator("config_parse_tunnel_mode", "0") to ConfigParseTunnelModeOptionValue() as OptionValueInformation, Validator("config_parse_txqueuelen", "0") to ConfigParseTxqueuelenOptionValue() as OptionValueInformation, Validator("config_parse_unit_condition_string", "CONDITION_AC_POWER") to ConfigParseUnitConditionStringOptionValue() as OptionValueInformation, @@ -191,6 +194,13 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_cake_priority_queueing_preset", "QDISC_KIND_CAKE") to ConfigParseCakePriorityQueueingPresetOptionValue() as OptionValueInformation, Validator("config_parse_hostname", "0") to ConfigParseHostnameOptionValue() as OptionValueInformation, Validator("config_parse_device_allow", "0") to ConfigParseDeviceAllowOptionValue() as OptionValueInformation, + Validator("config_parse_ifname", "0") to ConfigParseIfnameOptionValue() as OptionValueInformation, + Validator("config_parse_dns", "0") to ConfigParseDnsOptionValue() as OptionValueInformation, + Validator("config_parse_wol", "0") to ConfigParseWolOptionValue() as OptionValueInformation, + Validator("config_parse_dnssec_mode", "0") to ConfigParseDnssecModeOptionValue() as OptionValueInformation, + Validator("config_parse_protect_hostname", "0") to ConfigParseProtectHostnameOptionValue() as OptionValueInformation, + Validator("config_parse_ip_tos", "0") to ConfigParseIpTosOptionValue() as OptionValueInformation, + Validator("config_parse_required_family_for_online", "0") to ConfigParseRequiredFamilyForOnlineOptionValue() as OptionValueInformation, ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsOptionValue.kt new file mode 100644 index 0000000..9b6d732 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsOptionValue.kt @@ -0,0 +1,59 @@ +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.AlternativeCombinator +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.EOF +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.IPV4_ADDR +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.IPV6_ADDR +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.IntegerTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.LiteralChoiceTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.RegexTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.WhitespaceTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.ZeroOrMore +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.ZeroOrOne + +private val DNS_PORT = IntegerTerminal(0, 65536) +private val DNS_IFACE = RegexTerminal("[A-Za-z0-9_][A-Za-z0-9_.-]*", "[A-Za-z0-9_][A-Za-z0-9_.-]*") +private val DNS_IFACE_SUFFIX = SequenceCombinator(LiteralChoiceTerminal("%"), DNS_IFACE) + +private val DNS_IPV4_ENTRY = SequenceCombinator( + IPV4_ADDR, + ZeroOrOne(SequenceCombinator(LiteralChoiceTerminal(":"), DNS_PORT)), + ZeroOrOne(DNS_IFACE_SUFFIX) +) +private val DNS_IPV6_BRACKETED_ENTRY = SequenceCombinator( + LiteralChoiceTerminal("["), + IPV6_ADDR, + LiteralChoiceTerminal("]"), + ZeroOrOne(SequenceCombinator(LiteralChoiceTerminal(":"), DNS_PORT)), + ZeroOrOne(DNS_IFACE_SUFFIX) +) +private val DNS_IPV6_BARE_ENTRY = SequenceCombinator( + IPV6_ADDR, + ZeroOrOne(DNS_IFACE_SUFFIX) +) +private val DNS_ENTRY = AlternativeCombinator( + DNS_IPV6_BRACKETED_ENTRY, + DNS_IPV4_ENTRY, + DNS_IPV6_BARE_ENTRY +) + +/** + * Validator for Network.DNS (.network). + * + * C Function: config_parse_dns(0) + * + * Accepts a whitespace-separated list of DNS server addresses. Each entry is an IPv4 + * or IPv6 address. IPv4 addresses may optionally include a port suffix (":port") and + * an interface suffix ("%iface"). IPv6 addresses can be bare, or bracketed with a port + * ("[ipv6]:port") and may also have an interface suffix. + */ +class ConfigParseDnsOptionValue : SimpleGrammarOptionValues( + "config_parse_dns", + SequenceCombinator( + DNS_ENTRY, + ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), DNS_ENTRY)), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnssecModeOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnssecModeOptionValue.kt new file mode 100644 index 0000000..bdf2011 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnssecModeOptionValue.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.AlternativeCombinator +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.BOOLEAN +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.EOF +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.FlexibleLiteralChoiceTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator + +/** + * Validator for Network.DNSSEC (.network) and Resolve.DNSSEC (.conf via resolved.conf). + * + * C Function: config_parse_dnssec_mode(0) + * + * Backed by DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dnssec_mode, ...), so accepts + * any boolean value as well as the explicit table entries: "no", "allow-downgrade", "yes". + */ +class ConfigParseDnssecModeOptionValue : SimpleGrammarOptionValues( + "config_parse_dnssec_mode", + SequenceCombinator( + AlternativeCombinator( + FlexibleLiteralChoiceTerminal("allow-downgrade"), + BOOLEAN + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt new file mode 100644 index 0000000..df4181e --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt @@ -0,0 +1,26 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for keys parsed by config_parse_ifname (e.g. Network.Bridge, Network.Bond, + * Network.VRF, Network.BatmanAdvanced in .network; NetDev.Name, Peer.Name, VXCAN.Peer in + * .netdev; Link.Name in .link; Network.Bridge in .nspawn). + * + * The C function delegates to ifname_valid(), which requires a Linux interface name: + * - 1..15 characters + * - no whitespace, no '/' + * - cannot be "." or ".." + * - must be valid UTF-8 (effectively printable ASCII in practice) + * + * The grammar below is a reasonable approximation: 1..15 characters that are not + * whitespace and not '/'. The "." / ".." edge cases are not modelled. + */ +class ConfigParseIfnameOptionValue : SimpleGrammarOptionValues( + "config_parse_ifname", + SequenceCombinator( + RegexTerminal("[^\\s/]{1,15}", "[^\\s/]{1,15}"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIpProtocolOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIpProtocolOptionValue.kt new file mode 100644 index 0000000..27521ff --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIpProtocolOptionValue.kt @@ -0,0 +1,25 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for FooOverUDP.Protocol + * C Function: config_parse_ip_protocol(true) + * Used by Options: FooOverUDP.Protocol + * + * Accepts an IP protocol name from /etc/protocols (e.g. tcp, udp, icmp) or an integer 0..255. + * The list of protocol names below is intentionally conservative; rare names will produce a + * (false-positive) warning, which is acceptable. + */ +class ConfigParseIpProtocolOptionValue : SimpleGrammarOptionValues( + "config_parse_ip_protocol", + SequenceCombinator( + AlternativeCombinator( + FlexibleLiteralChoiceTerminal("tcp", "udp", "icmp", "icmpv6", "sctp", "udplite", "dccp"), + IntegerTerminal(0, 256) + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIpTosOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIpTosOptionValue.kt new file mode 100644 index 0000000..dc657b0 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIpTosOptionValue.kt @@ -0,0 +1,23 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for Socket.IPTOS + * C Function: config_parse_ip_tos(0) + * + * Accepts a symbolic name (low-delay, throughput, reliability, low-cost) or a + * raw integer in the range 0..255 (ip_tos_from_string falls back to safe_atou + * with max 0xff). + */ +class ConfigParseIpTosOptionValue : SimpleGrammarOptionValues( + "config_parse_ip_tos", + SequenceCombinator( + AlternativeCombinator( + FlexibleLiteralChoiceTerminal("low-delay", "throughput", "reliability", "low-cost"), + IntegerTerminal(0, 256) + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseProtectHostnameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseProtectHostnameOptionValue.kt new file mode 100644 index 0000000..9534edf --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseProtectHostnameOptionValue.kt @@ -0,0 +1,56 @@ +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 Service.ProtectHostname (also Socket/Mount/Swap.ProtectHostname). + * C Function: config_parse_protect_hostname(0) + * + * Accepts either: + * - a boolean (yes/no/y/n/true/false/t/f/1/0/on/off) or "private" + * - ":" where is a "true" boolean (yes/y/true/t/1/on) + * or "private", and matches hostname_is_valid (LDH chars; labels + * joined by '.' with no leading/trailing hyphens). + * + * The C code rejects "no:" (and other false synonyms with a hostname). + */ +class ConfigParseProtectHostnameOptionValue : SimpleGrammarOptionValues( + "config_parse_protect_hostname", + SequenceCombinator( + AlternativeCombinator( + // Form: : + SequenceCombinator( + BOOLEAN_TRUE_OR_PRIVATE, + COLON_LITERAL, + HOSTNAME + ), + // Form: standalone keyword (any boolean or "private") + BOOLEAN_OR_PRIVATE + ), + EOF() + ) +) { + companion object { + private val COLON_LITERAL = LiteralChoiceTerminal(":") + + // Boolean true synonyms plus "private" (used when a hostname is supplied). + private val BOOLEAN_TRUE_OR_PRIVATE = FlexibleLiteralChoiceTerminal( + "yes", "y", "true", "t", "on", "1", "private" + ) + + // Any boolean (true or false synonyms) plus "private". + private val BOOLEAN_OR_PRIVATE = FlexibleLiteralChoiceTerminal( + "1", "yes", "y", "true", "t", "on", + "0", "no", "n", "false", "f", "off", + "private" + ) + + // Hostname: one or more LDH labels separated by '.'. Each label starts and + // ends with [A-Za-z0-9] and may contain hyphens internally. + private val HOSTNAME = RegexTerminal( + "[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*", + "[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*" + ) + } +} diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRequiredFamilyForOnlineOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRequiredFamilyForOnlineOptionValue.kt new file mode 100644 index 0000000..80494dc --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseRequiredFamilyForOnlineOptionValue.kt @@ -0,0 +1,15 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for Link.RequiredFamilyForOnline (.network) + */ +class ConfigParseRequiredFamilyForOnlineOptionValue : SimpleGrammarOptionValues( + "config_parse_required_family_for_online", + SequenceCombinator( + FlexibleLiteralChoiceTerminal("ipv4", "ipv6", "both", "any"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSrIovLinkStateOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSrIovLinkStateOptionValue.kt new file mode 100644 index 0000000..7251d65 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSrIovLinkStateOptionValue.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.AlternativeCombinator +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.BOOLEAN +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.EOF +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.FlexibleLiteralChoiceTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator + +/** + * Validator for SR-IOV.LinkState in .network and .link files. + * + * C Function: config_parse_sr_iov_link_state(0) + * + * Accepts the literal "auto", or any boolean value (which is parsed via parse_boolean + * and mapped to SR_IOV_LINK_STATE_ENABLE / SR_IOV_LINK_STATE_DISABLE). + */ +class ConfigParseSrIovLinkStateOptionValue : SimpleGrammarOptionValues( + "config_parse_sr_iov_link_state", + SequenceCombinator( + AlternativeCombinator( + BOOLEAN, + FlexibleLiteralChoiceTerminal("auto") + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTriggerUnitOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTriggerUnitOptionValue.kt new file mode 100644 index 0000000..8ba6e20 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTriggerUnitOptionValue.kt @@ -0,0 +1,26 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for [Path] Unit= and [Timer] Unit=. + * C function: config_parse_trigger_unit (src/core/load-fragment.c) + * + * The C path resolves specifiers in rvalue, then requires the result to be a + * valid systemd unit name (unit_name_to_type must succeed). A unit name is + * . where the body characters are letters, digits, '-', '_', '@', + * ':', and '.', and is one of the recognised unit type extensions. + * + * Used by .path (Path.Unit) and .timer (Timer.Unit) units. + */ +class ConfigParseTriggerUnitOptionValue : SimpleGrammarOptionValues( + "config_parse_trigger_unit", + SequenceCombinator( + RegexTerminal( + "[A-Za-z0-9_:.@-]+\\.(service|socket|target|device|mount|automount|timer|swap|path|slice|scope)", + "[A-Za-z0-9_:.@-]+\\.(service|socket|target|device|mount|automount|timer|swap|path|slice|scope)" + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseWolOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseWolOptionValue.kt new file mode 100644 index 0000000..b5b9e43 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseWolOptionValue.kt @@ -0,0 +1,32 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for Link.WakeOnLan (.link). + * C Function: config_parse_wol(0) + * + * Source: src/shared/ethtool-util.c — config_parse_wol() and the wol_option_map[] table. + * Accepts either the standalone literal "off", or one or more whitespace-separated + * Wake-on-LAN flags from the set: phy, unicast, multicast, broadcast, arp, magic, secureon. + * (Empty value resets the field — handled separately by the inspection framework.) + */ +class ConfigParseWolOptionValue : SimpleGrammarOptionValues( + "config_parse_wol", + SequenceCombinator( + AlternativeCombinator( + LiteralChoiceTerminal("off"), + SequenceCombinator( + LiteralChoiceTerminal("phy", "unicast", "multicast", "broadcast", "arp", "magic", "secureon"), + ZeroOrMore( + SequenceCombinator( + WhitespaceTerminal(), + LiteralChoiceTerminal("phy", "unicast", "multicast", "broadcast", "arp", "magic", "secureon") + ) + ) + ) + ), + EOF() + ) +) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsOptionValueTest.kt new file mode 100644 index 0000000..ef80672 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsOptionValueTest.kt @@ -0,0 +1,159 @@ +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 ConfigParseDnsOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidSingleIpv4() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=192.168.1.1 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testValidMultipleIpv4() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=192.168.1.1 8.8.8.8 1.1.1.1 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testValidIpv6() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=2001:4860:4860::8888 + DNS=::1 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testValidMixedIpv4AndIpv6() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=192.168.1.1 2001:4860:4860::8888 8.8.4.4 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testValidIpv4WithPort() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=192.168.1.1:53 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testValidIpv4WithInterface() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=192.168.1.1%eth0 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testValidIpv6BracketedWithPort() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=[2001:4860:4860::8888]:53 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidIpv4OctetTooLarge() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=192.168.1.256 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(1, highlights) + } + + @Test + fun testInvalidHostname() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=dns.example.com + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(1, highlights) + } + + @Test + fun testInvalidGarbage() { + // language="unit file (systemd)" + val file = """ + [Network] + DNS=not-an-ip + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(1, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnssecModeOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnssecModeOptionValueTest.kt new file mode 100644 index 0000000..c9943a6 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnssecModeOptionValueTest.kt @@ -0,0 +1,62 @@ +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 ConfigParseDnssecModeOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidTableValues() { + // language="unit file (systemd)" + val file = """ + [Network] + DNSSEC=yes + DNSSEC=no + DNSSEC=allow-downgrade + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testValidBooleanValues() { + // language="unit file (systemd)" + val file = """ + [Network] + DNSSEC=true + DNSSEC=false + DNSSEC=on + DNSSEC=off + DNSSEC=1 + DNSSEC=0 + """.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 = """ + [Network] + DNSSEC=invalid + DNSSEC=allow + DNSSEC=downgrade + """.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/ConfigParseIfnameOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt new file mode 100644 index 0000000..d943ac5 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt @@ -0,0 +1,43 @@ +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 ConfigParseIfnameOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidInterfaceNames() { + // language="unit file (systemd)" + val file = """ + [Network] + Bridge=br0 + Bridge=eth0 + Bridge=my-iface + Bridge=iface_1 + Bridge=abcdefghijklmno + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testInvalidInterfaceNames() { + // Names containing '/' are rejected, and names longer than 15 characters + // are rejected by ifname_valid(). + // language="unit file (systemd)" + val file = """ + [Network] + Bridge=bad/name + Bridge=thisnameiswaytoolong + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIpProtocolOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIpProtocolOptionValueTest.kt new file mode 100644 index 0000000..f441017 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIpProtocolOptionValueTest.kt @@ -0,0 +1,53 @@ +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 ConfigParseIpProtocolOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [FooOverUDP] + Protocol=tcp + Protocol=udp + Protocol=icmp + Protocol=icmpv6 + Protocol=sctp + Protocol=0 + Protocol=47 + Protocol=255 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.netdev", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [FooOverUDP] + Protocol=256 + Protocol=-1 + Protocol=not-a-protocol + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.netdev", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(3, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIpTosOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIpTosOptionValueTest.kt new file mode 100644 index 0000000..00bc330 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIpTosOptionValueTest.kt @@ -0,0 +1,138 @@ +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 ConfigParseIpTosOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidNamedValues() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=low-delay + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidNamedValueThroughput() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=throughput + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidNamedValueReliability() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=reliability + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidNamedValueLowCost() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=low-cost + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidIntegerLowerBound() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=0 + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidIntegerUpperBound() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=255 + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidIntegerMidRange() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=42 + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testInvalidWord() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=invalid_value + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(1, myFixture.doHighlighting()) + } + + @Test + fun testInvalidIntegerOutOfRange() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=300 + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(1, myFixture.doHighlighting()) + } + + @Test + fun testInvalidIntegerNegative() { + // language="unit file (systemd)" + val file = """ + [Socket] + IPTOS=-1 + """.trimIndent() + + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(1, myFixture.doHighlighting()) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseProtectHostnameOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseProtectHostnameOptionValueTest.kt new file mode 100644 index 0000000..943833e --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseProtectHostnameOptionValueTest.kt @@ -0,0 +1,65 @@ +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 ConfigParseProtectHostnameOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidBooleanValues() { + // language="unit file (systemd)" + val file = """ + [Service] + ProtectHostname=yes + ProtectHostname=no + ProtectHostname=true + ProtectHostname=false + ProtectHostname=on + ProtectHostname=off + ProtectHostname=1 + ProtectHostname=0 + ProtectHostname=private + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testValidKeywordWithHostname() { + // language="unit file (systemd)" + val file = """ + [Service] + ProtectHostname=yes:my-host + ProtectHostname=private:example.com + ProtectHostname=true:foo.bar.example + ProtectHostname=on:host1 + ProtectHostname=1:a-b-c.d + """.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] + ProtectHostname=invalid + ProtectHostname=yes:-bad-leading-hyphen + ProtectHostname=private:bad..double.dot + ProtectHostname=yes:bad_underscore + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(4, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseRequiredFamilyForOnlineOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseRequiredFamilyForOnlineOptionValueTest.kt new file mode 100644 index 0000000..5d5f5ed --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseRequiredFamilyForOnlineOptionValueTest.kt @@ -0,0 +1,42 @@ +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 ConfigParseRequiredFamilyForOnlineOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Link] + RequiredFamilyForOnline=ipv4 + RequiredFamilyForOnline=ipv6 + RequiredFamilyForOnline=both + RequiredFamilyForOnline=any + """.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 = """ + [Link] + RequiredFamilyForOnline=invalid + RequiredFamilyForOnline=value + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSrIovLinkStateOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSrIovLinkStateOptionValueTest.kt new file mode 100644 index 0000000..3b58bfa --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSrIovLinkStateOptionValueTest.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 ConfigParseSrIovLinkStateOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [SR-IOV] + LinkState=auto + LinkState=yes + LinkState=no + LinkState=true + LinkState=false + LinkState=on + LinkState=off + LinkState=1 + LinkState=0 + """.trimIndent() + + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // language="unit file (systemd)" + val file = """ + [SR-IOV] + LinkState=invalid_value_1 + LinkState=enable + LinkState=disable + """.trimIndent() + + setupFileInEditor("file.link", 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/ConfigParseTriggerUnitOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTriggerUnitOptionValueTest.kt new file mode 100644 index 0000000..ee7a485 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTriggerUnitOptionValueTest.kt @@ -0,0 +1,96 @@ +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 ConfigParseTriggerUnitOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidUnitNames() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Path] + Unit=foo.service + Unit=bar.socket + Unit=baz.target + Unit=my-app.mount + Unit=net_dev.automount + Unit=tmp.timer + Unit=swapfile.swap + Unit=watcher.path + Unit=workload.slice + Unit=session.scope + Unit=fs-home.device + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.path", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testValidTemplateAndComplexNames() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Path] + Unit=getty@tty1.service + Unit=user@1000.service + Unit=foo.bar.service + Unit=a-b_c.service + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.path", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidUnitTypes() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Path] + Unit=foo.bogus + Unit=bar.unknown + Unit=foo + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.path", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(3, highlights) + } + + @Test + fun testInvalidCharacters() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Path] + Unit=foo bar.service + Unit=foo/bar.service + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.path", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseWolOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseWolOptionValueTest.kt new file mode 100644 index 0000000..1792b21 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseWolOptionValueTest.kt @@ -0,0 +1,115 @@ +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 ConfigParseWolOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidOff() { + // language="unit file (systemd)" + val file = """ + [Link] + WakeOnLan=off + """.trimIndent() + + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testValidSingleFlags() { + // language="unit file (systemd)" + val file = """ + [Link] + WakeOnLan=phy + WakeOnLan=unicast + WakeOnLan=multicast + WakeOnLan=broadcast + WakeOnLan=arp + WakeOnLan=magic + WakeOnLan=secureon + """.trimIndent() + + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testValidMultipleFlags() { + // language="unit file (systemd)" + val file = """ + [Link] + WakeOnLan=phy magic + WakeOnLan=unicast multicast broadcast + WakeOnLan=phy unicast multicast broadcast arp magic secureon + """.trimIndent() + + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(0, highlights) + } + + @Test + fun testInvalidUnknownFlag() { + // language="unit file (systemd)" + val file = """ + [Link] + WakeOnLan=bogus + """.trimIndent() + + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(1, highlights) + } + + @Test + fun testInvalidMixedWithUnknown() { + // language="unit file (systemd)" + val file = """ + [Link] + WakeOnLan=phy bogus + """.trimIndent() + + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(1, highlights) + } + + @Test + fun testInvalidCommaSeparator() { + // language="unit file (systemd)" + val file = """ + [Link] + WakeOnLan=phy,magic + """.trimIndent() + + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(1, highlights) + } + + @Test + fun testInvalidOffMixed() { + // off is only valid as a standalone value + // language="unit file (systemd)" + val file = """ + [Link] + WakeOnLan=off phy + """.trimIndent() + + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + assertSize(1, highlights) + } +} From 023ebc21ff4f270fc1f4320deb71da0054152ad5 Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Thu, 30 Apr 2026 20:20:18 -0700 Subject: [PATCH 3/5] feat: import 20 more HIGH-confidence AI validators (verified) Adds validators for fq_bool, fq_size, fq_u32, si_uint64, fdb_vlan_id, dhcp_socket_priority, cake_rtt, coalesce_sec, codel_usec, tbf_latency, service_timeout, service_timeout_abort, job_running_timeout_sec, exec_secure_bits, namespace_flags, can_bitrate, can_time_quanta, fdname, udev_property_name, dns_name. Time-suffix regexes order longer alternatives (year/week/hour/day/min/sec) before shorter (m/h/d/w/y) so that "1min" doesn't get mismatched as "1m" + leftover "in". Co-Authored-By: Claude Opus 4.7 (1M context) --- .../semanticdata/optionvalues/AiGenerated.kt | 20 +++ .../ai/ConfigParseCakeRttOptionValue.kt | 22 ++++ .../ai/ConfigParseCanBitrateOptionValue.kt | 20 +++ .../ai/ConfigParseCanTimeQuantaOptionValue.kt | 26 ++++ .../ai/ConfigParseCoalesceSecOptionValue.kt | 27 ++++ ...nfigParseControlledDelayUsecOptionValue.kt | 22 ++++ ...onfigParseDhcpSocketPriorityOptionValue.kt | 21 +++ .../ai/ConfigParseDnsNameOptionValue.kt | 32 +++++ .../ConfigParseExecSecureBitsOptionValue.kt | 35 +++++ .../ConfigParseFairQueueingBoolOptionValue.kt | 21 +++ .../ConfigParseFairQueueingSizeOptionValue.kt | 20 +++ .../ConfigParseFairQueueingU32OptionValue.kt | 17 +++ .../ai/ConfigParseFdbVlanIdOptionValue.kt | 22 ++++ .../ai/ConfigParseFdnameOptionValue.kt | 22 ++++ ...figParseJobRunningTimeoutSecOptionValue.kt | 26 ++++ .../ConfigParseNamespaceFlagsOptionValue.kt | 51 ++++++++ ...nfigParseServiceTimeoutAbortOptionValue.kt | 27 ++++ .../ConfigParseServiceTimeoutOptionValue.kt | 26 ++++ .../ai/ConfigParseSiUint64OptionValue.kt | 23 ++++ ...arseTokenBucketFilterLatencyOptionValue.kt | 22 ++++ .../ConfigParseUdevPropertyNameOptionValue.kt | 28 ++++ .../ai/ConfigParseCakeRttOptionValueTest.kt | 40 ++++++ .../ConfigParseCanBitrateOptionValueTest.kt | 51 ++++++++ ...ConfigParseCanTimeQuantaOptionValueTest.kt | 51 ++++++++ .../ConfigParseCoalesceSecOptionValueTest.kt | 56 ++++++++ ...ParseControlledDelayUsecOptionValueTest.kt | 51 ++++++++ ...gParseDhcpSocketPriorityOptionValueTest.kt | 52 ++++++++ .../ai/ConfigParseDnsNameOptionValueTest.kt | 50 +++++++ ...onfigParseExecSecureBitsOptionValueTest.kt | 52 ++++++++ ...figParseFairQueueingBoolOptionValueTest.kt | 58 +++++++++ ...figParseFairQueueingSizeOptionValueTest.kt | 55 ++++++++ ...nfigParseFairQueueingU32OptionValueTest.kt | 50 +++++++ .../ai/ConfigParseFdbVlanIdOptionValueTest.kt | 89 +++++++++++++ .../ai/ConfigParseFdnameOptionValueTest.kt | 48 +++++++ ...arseJobRunningTimeoutSecOptionValueTest.kt | 53 ++++++++ ...onfigParseNamespaceFlagsOptionValueTest.kt | 122 ++++++++++++++++++ ...ParseServiceTimeoutAbortOptionValueTest.kt | 45 +++++++ ...onfigParseServiceTimeoutOptionValueTest.kt | 54 ++++++++ .../ai/ConfigParseSiUint64OptionValueTest.kt | 51 ++++++++ ...TokenBucketFilterLatencyOptionValueTest.kt | 52 ++++++++ ...figParseUdevPropertyNameOptionValueTest.kt | 116 +++++++++++++++++ 41 files changed, 1726 insertions(+) create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakeRttOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanBitrateOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanTimeQuantaOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCoalesceSecOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseControlledDelayUsecOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDhcpSocketPriorityOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSecureBitsOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingBoolOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingSizeOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingU32OptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFdbVlanIdOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFdnameOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseJobRunningTimeoutSecOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseNamespaceFlagsOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutAbortOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSiUint64OptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTokenBucketFilterLatencyOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseUdevPropertyNameOptionValue.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakeRttOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanBitrateOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanTimeQuantaOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCoalesceSecOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseControlledDelayUsecOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDhcpSocketPriorityOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsNameOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSecureBitsOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingBoolOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingSizeOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingU32OptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFdbVlanIdOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFdnameOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseJobRunningTimeoutSecOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseNamespaceFlagsOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseServiceTimeoutAbortOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseServiceTimeoutOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSiUint64OptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTokenBucketFilterLatencyOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUdevPropertyNameOptionValueTest.kt 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 a7b149e..077d32d 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 @@ -196,11 +196,31 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_device_allow", "0") to ConfigParseDeviceAllowOptionValue() as OptionValueInformation, Validator("config_parse_ifname", "0") to ConfigParseIfnameOptionValue() as OptionValueInformation, Validator("config_parse_dns", "0") to ConfigParseDnsOptionValue() as OptionValueInformation, + Validator("config_parse_dns_name", "0") to ConfigParseDnsNameOptionValue() as OptionValueInformation, Validator("config_parse_wol", "0") to ConfigParseWolOptionValue() as OptionValueInformation, Validator("config_parse_dnssec_mode", "0") to ConfigParseDnssecModeOptionValue() as OptionValueInformation, Validator("config_parse_protect_hostname", "0") to ConfigParseProtectHostnameOptionValue() as OptionValueInformation, Validator("config_parse_ip_tos", "0") to ConfigParseIpTosOptionValue() as OptionValueInformation, Validator("config_parse_required_family_for_online", "0") to ConfigParseRequiredFamilyForOnlineOptionValue() as OptionValueInformation, + Validator("config_parse_fq_bool", "QDISC_KIND_FQ") to ConfigParseFairQueueingBoolOptionValue() as OptionValueInformation, + Validator("config_parse_fq_size", "QDISC_KIND_FQ") to ConfigParseFairQueueingSizeOptionValue() as OptionValueInformation, + Validator("config_parse_fq_u32", "QDISC_KIND_FQ") to ConfigParseFairQueueingU32OptionValue() as OptionValueInformation, + Validator("config_parse_si_uint64", "0") to ConfigParseSiUint64OptionValue() as OptionValueInformation, + Validator("config_parse_fdb_vlan_id", "0") to ConfigParseFdbVlanIdOptionValue() as OptionValueInformation, + Validator("config_parse_dhcp_socket_priority", "0") to ConfigParseDhcpSocketPriorityOptionValue() as OptionValueInformation, + Validator("config_parse_cake_rtt", "QDISC_KIND_CAKE") to ConfigParseCakeRttOptionValue() as OptionValueInformation, + Validator("config_parse_coalesce_sec", "0") to ConfigParseCoalesceSecOptionValue() as OptionValueInformation, + Validator("config_parse_codel_usec", "QDISC_KIND_CODEL") to ConfigParseControlledDelayUsecOptionValue() as OptionValueInformation, + Validator("config_parse_tbf_latency", "QDISC_KIND_TBF") to ConfigParseTokenBucketFilterLatencyOptionValue() as OptionValueInformation, + Validator("config_parse_service_timeout", "0") to ConfigParseServiceTimeoutOptionValue() as OptionValueInformation, + Validator("config_parse_service_timeout_abort", "0") to ConfigParseServiceTimeoutAbortOptionValue() as OptionValueInformation, + Validator("config_parse_job_running_timeout_sec", "0") to ConfigParseJobRunningTimeoutSecOptionValue() as OptionValueInformation, + Validator("config_parse_exec_secure_bits", "0") to ConfigParseExecSecureBitsOptionValue() as OptionValueInformation, + Validator("config_parse_namespace_flags", "0") to ConfigParseNamespaceFlagsOptionValue() as OptionValueInformation, + Validator("config_parse_can_bitrate", "0") to ConfigParseCanBitrateOptionValue() as OptionValueInformation, + Validator("config_parse_can_time_quanta", "0") to ConfigParseCanTimeQuantaOptionValue() as OptionValueInformation, + Validator("config_parse_fdname", "0") to ConfigParseFdnameOptionValue() as OptionValueInformation, + Validator("config_parse_udev_property_name", "0") to ConfigParseUdevPropertyNameOptionValue() as OptionValueInformation, ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakeRttOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakeRttOptionValue.kt new file mode 100644 index 0000000..b74d695 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakeRttOptionValue.kt @@ -0,0 +1,22 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for CAKE.RTTSec (.network). + * C Function: config_parse_cake_rtt(QDISC_KIND_CAKE) + * + * Calls parse_sec; accepts a positive systemd time value (ms/us/s/m/h/d/...). + * Empty resets and is always valid (handled outside the grammar). + */ +class ConfigParseCakeRttOptionValue : SimpleGrammarOptionValues( + "config_parse_cake_rtt", + SequenceCombinator( + RegexTerminal( + "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?", + "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?" + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanBitrateOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanBitrateOptionValue.kt new file mode 100644 index 0000000..45038a9 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanBitrateOptionValue.kt @@ -0,0 +1,20 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for CAN.BitRate and CAN.DataBitRate. + * C Function: config_parse_can_bitrate(0) in src/network/networkd-can.c. + * + * Internally calls parse_size(rvalue, 1000, &sz), so the value is a decimal bit count + * optionally suffixed with an SI unit (K, M, G, ...). The result must fit in a uint32_t, + * i.e. be in the range 1..4294967295. + */ +class ConfigParseCanBitrateOptionValue : SimpleGrammarOptionValues( + "config_parse_can_bitrate", + SequenceCombinator( + RegexTerminal("[0-9]+(?:K|M|G)?", "[0-9]+(?:K|M|G)?"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanTimeQuantaOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanTimeQuantaOptionValue.kt new file mode 100644 index 0000000..d083fc0 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanTimeQuantaOptionValue.kt @@ -0,0 +1,26 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for CAN.TimeQuantaNSec, CAN.DataTimeQuantaNSec. + * C Function: config_parse_can_time_quanta(0) + * + * Per parse_nsec: accepts a time value (with optional unit suffix, possibly + * compound like "1ms 500us") OR the literal "infinity". A bare integer with no + * suffix is interpreted as nanoseconds. + */ +class ConfigParseCanTimeQuantaOptionValue : SimpleGrammarOptionValues( + "config_parse_can_time_quanta", + SequenceCombinator( + AlternativeCombinator( + FlexibleLiteralChoiceTerminal("infinity"), + RegexTerminal( + "[0-9]+(?:ns|nsec|us|µs|μs|ms|msec|s|sec|seconds?|m|min|minutes?|h|hr|hours?|d|days?|w|weeks?|M|months?|y|years?)?(?:\\s+[0-9]+(?:ns|nsec|us|µs|μs|ms|msec|s|sec|seconds?|m|min|minutes?|h|hr|hours?|d|days?|w|weeks?|M|months?|y|years?)?)*", + "[0-9]+(?:ns|nsec|us|µs|μs|ms|msec|s|sec|seconds?|m|min|minutes?|h|hr|hours?|d|days?|w|weeks?|M|months?|y|years?)?(?:\\s+[0-9]+(?:ns|nsec|us|µs|μs|ms|msec|s|sec|seconds?|m|min|minutes?|h|hr|hours?|d|days?|w|weeks?|M|months?|y|years?)?)*" + ) + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCoalesceSecOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCoalesceSecOptionValue.kt new file mode 100644 index 0000000..d0b2c19 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCoalesceSecOptionValue.kt @@ -0,0 +1,27 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for Link.{RxCoalesceSec, RxCoalesceIrqSec, TxCoalesceSec, TxCoalesceIrqSec, + * StatisticsBlockCoalesceSec, RxCoalesceLowSec, TxCoalesceLowSec, + * RxCoalesceHighSec, TxCoalesceHighSec, CoalescePacketRateSampleIntervalSec}. + * C Function: config_parse_coalesce_sec(0) + * + * Per src/shared/ethtool-util.c, the rvalue is parsed via parse_sec() which accepts a + * non-negative integer with an optional time unit suffix (defaulting to seconds when + * no suffix is present). The C code further rejects values exceeding UINT32_MAX usec + * and zero values for two specific keys, but those numeric checks cannot be enforced + * at the grammar level here (the same validator is shared across all ten keys). + */ +class ConfigParseCoalesceSecOptionValue : SimpleGrammarOptionValues( + "config_parse_coalesce_sec", + SequenceCombinator( + RegexTerminal( + "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?", + "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?" + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseControlledDelayUsecOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseControlledDelayUsecOptionValue.kt new file mode 100644 index 0000000..8c1c408 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseControlledDelayUsecOptionValue.kt @@ -0,0 +1,22 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for ControlledDelay.TargetSec, ControlledDelay.IntervalSec, ControlledDelay.CEThresholdSec + * C Function: config_parse_codel_usec(QDISC_KIND_CODEL) + * + * Parses a time value via parse_sec(), accepting an integer optionally followed + * by a time unit suffix (ms, us, µs, s, m, h, d, w, y). + */ +class ConfigParseControlledDelayUsecOptionValue : SimpleGrammarOptionValues( + "config_parse_codel_usec", + SequenceCombinator( + RegexTerminal( + "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?", + "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?" + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDhcpSocketPriorityOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDhcpSocketPriorityOptionValue.kt new file mode 100644 index 0000000..b89a472 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDhcpSocketPriorityOptionValue.kt @@ -0,0 +1,21 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for DHCPv4.SocketPriority + * C Function: config_parse_dhcp_socket_priority(0) + * Used by Options: DHCPv4.SocketPriority + * + * The C implementation uses safe_atoi() with no range restriction beyond what fits in a + * signed 32-bit int. SO_PRIORITY values 0..6 are typical, but the parser itself accepts + * any signed int. Empty values are allowed (clears the setting) and so are skipped here. + */ +class ConfigParseDhcpSocketPriorityOptionValue : SimpleGrammarOptionValues( + "config_parse_dhcp_socket_priority", + SequenceCombinator( + IntegerTerminal(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong() + 1L), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt new file mode 100644 index 0000000..a492f10 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt @@ -0,0 +1,32 @@ +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.EOF +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.RegexTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator + +/** + * Validator for config_parse_dns_name(0). + * + * Used by .network DHCPServer.Domain, DHCPServer.BootServerName, DHCPServer.LocalLeaseDomain. + * + * Mirrors dns_name_is_valid(s) (no DNS_LABEL_LDH flag), which validates via + * dns_name_concat -> dns_label_unescape. Without the LDH flag, label characters + * are more permissive than hostname_is_valid (e.g. underscores are allowed and + * leading/trailing hyphens are not specifically rejected). A single optional + * trailing dot is accepted to denote a fully-qualified name. + * + * Note: dns_label_unescape also supports backslash-escaped characters and + * decimal-escaped octets ("\NNN"); these are extremely rare in DHCP server + * configuration values and are intentionally not modelled here. + */ +class ConfigParseDnsNameOptionValue : SimpleGrammarOptionValues( + "config_parse_dns_name", + SequenceCombinator( + RegexTerminal( + "[a-zA-Z0-9_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])?(?:\\.[a-zA-Z0-9_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])?)*\\.?", + "[a-zA-Z0-9_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])?(?:\\.[a-zA-Z0-9_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])?)*\\.?" + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSecureBitsOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSecureBitsOptionValue.kt new file mode 100644 index 0000000..67353ac --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseExecSecureBitsOptionValue.kt @@ -0,0 +1,35 @@ +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.EOF +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.FlexibleLiteralChoiceTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.WhitespaceTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.ZeroOrMore + +private val SECURE_BIT = FlexibleLiteralChoiceTerminal( + "keep-caps", + "keep-caps-locked", + "no-setuid-fixup", + "no-setuid-fixup-locked", + "noroot", + "noroot-locked" +) + +/** + * Validator for Service.SecureBits, Socket.SecureBits, Mount.SecureBits, Swap.SecureBits. + * + * C Function: config_parse_exec_secure_bits(0) + * + * Accepts a whitespace-separated list of secure bits flags. Valid values are: + * keep-caps, keep-caps-locked, no-setuid-fixup, no-setuid-fixup-locked, + * noroot, noroot-locked. + */ +class ConfigParseExecSecureBitsOptionValue : SimpleGrammarOptionValues( + "config_parse_exec_secure_bits", + SequenceCombinator( + SECURE_BIT, + ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), SECURE_BIT)), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingBoolOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingBoolOptionValue.kt new file mode 100644 index 0000000..70b20ba --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingBoolOptionValue.kt @@ -0,0 +1,21 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for FairQueueing.Pacing (.network) + * C function: config_parse_fq_bool + * + * The C implementation calls parse_tristate(rvalue, &fq->pacing). Since the + * "third" argument is NULL, an empty string sets the value to -1 (auto) and + * any other value is parsed via parse_boolean (yes/y/true/t/on/1 or no/n/false/f/off/0). + * Empty values are handled by the framework, so we only validate boolean. + */ +class ConfigParseFairQueueingBoolOptionValue : SimpleGrammarOptionValues( + "config_parse_fq_bool", + SequenceCombinator( + BOOLEAN, + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingSizeOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingSizeOptionValue.kt new file mode 100644 index 0000000..f176bcd --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingSizeOptionValue.kt @@ -0,0 +1,20 @@ +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 FairQueueing.QuantumBytes, FairQueueing.InitialQuantumBytes (and the deprecated + * Quantum / InitialQuantum aliases). + * + * C function: config_parse_fq_size (QDISC_KIND_FQ) in src/network/tc/fq.c. Internally calls + * parse_size(rvalue, 1024, &sz), so the value is a decimal byte count optionally suffixed with + * an IEC unit (B, K, M, G, T, P, E). Hexadecimal and octal forms are not accepted. + */ +class ConfigParseFairQueueingSizeOptionValue : SimpleGrammarOptionValues( + "config_parse_fq_size", + SequenceCombinator( + OptionalWhitespacePrefix(BYTES), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingU32OptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingU32OptionValue.kt new file mode 100644 index 0000000..1d91db5 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFairQueueingU32OptionValue.kt @@ -0,0 +1,17 @@ +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.EOF +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.IntegerTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator + +/** + * Validator for FairQueueing keys parsed by config_parse_fq_u32 (QDISC_KIND_FQ). + * C Function: config_parse_fq_u32 - parses a uint32_t via safe_atou32. + * Used by Options: FairQueueing.PacketLimit, FairQueueing.FlowLimit, + * FairQueueing.Buckets, FairQueueing.OrphanMask. + */ +class ConfigParseFairQueueingU32OptionValue : SimpleGrammarOptionValues( + "config_parse_fq_u32", + SequenceCombinator(IntegerTerminal(0L, 4_294_967_296L), EOF()) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFdbVlanIdOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFdbVlanIdOptionValue.kt new file mode 100644 index 0000000..6b525e9 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFdbVlanIdOptionValue.kt @@ -0,0 +1,22 @@ +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.EOF +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.IntegerTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator + +/** + * Validator for BridgeFDB.VLANId + * C Function: config_parse_fdb_vlan_id(0) + * Used by Options: BridgeFDB.VLANId + * + * Delegates to config_parse_vlanid which accepts a uint16 in the inclusive range + * 0..VLANID_MAX (4094). + */ +class ConfigParseFdbVlanIdOptionValue : SimpleGrammarOptionValues( + "config_parse_fdb_vlan_id", + SequenceCombinator( + IntegerTerminal(0, 4095), // Range 0-4094 inclusive (max is exclusive) + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFdnameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFdnameOptionValue.kt new file mode 100644 index 0000000..5174560 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseFdnameOptionValue.kt @@ -0,0 +1,22 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for config_parse_fdname(0). + * Used by Options: Socket.FileDescriptorName. + * + * Mirrors fdname_is_valid: + * - Printable ASCII only: bytes >= 0x20 (space) and < 0x7F (DEL). + * - The ':' character (0x3A) is reserved as a separator in $LISTEN_FDNAMES and is forbidden. + * - Maximum length is 255 characters (FDNAME_MAX). The empty string is handled by the + * caller (it clears the value) and is not validated here. + */ +class ConfigParseFdnameOptionValue : SimpleGrammarOptionValues( + "config_parse_fdname", + SequenceCombinator( + RegexTerminal("[ -9;-~]{1,255}", "[ -9;-~]{1,255}"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseJobRunningTimeoutSecOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseJobRunningTimeoutSecOptionValue.kt new file mode 100644 index 0000000..eb8916d --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseJobRunningTimeoutSecOptionValue.kt @@ -0,0 +1,26 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for Unit.JobRunningTimeoutSec + * C Function: config_parse_job_running_timeout_sec + * + * Parses a time value via parse_sec_fix_0(), accepting either the literal + * "infinity" or an integer optionally followed by a time unit suffix + * (ms, us, µs, s, m, h, d, w, y, min, sec, hour, day, week, year). + */ +class ConfigParseJobRunningTimeoutSecOptionValue : SimpleGrammarOptionValues( + "config_parse_job_running_timeout_sec", + SequenceCombinator( + AlternativeCombinator( + FlexibleLiteralChoiceTerminal("infinity"), + RegexTerminal( + "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?", + "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?" + ) + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseNamespaceFlagsOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseNamespaceFlagsOptionValue.kt new file mode 100644 index 0000000..f0fc9cc --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseNamespaceFlagsOptionValue.kt @@ -0,0 +1,51 @@ +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.AlternativeCombinator +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.BOOLEAN +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.EOF +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.FlexibleLiteralChoiceTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.LiteralChoiceTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.WhitespaceTerminal +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.ZeroOrMore +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.ZeroOrOne + +private val NAMESPACE_TYPE = FlexibleLiteralChoiceTerminal( + "cgroup", + "ipc", + "net", + "mnt", + "pid", + "user", + "uts", + "time" +) + +private val NAMESPACE_LIST = SequenceCombinator( + ZeroOrOne(LiteralChoiceTerminal("~")), + NAMESPACE_TYPE, + ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), NAMESPACE_TYPE)) +) + +/** + * Validator for Service.RestrictNamespaces, Service.DelegateNamespaces, Socket.RestrictNamespaces, + * Socket.DelegateNamespaces, Mount.RestrictNamespaces, Mount.DelegateNamespaces, Swap.RestrictNamespaces, + * Swap.DelegateNamespaces. + * + * C Function: config_parse_namespace_flags(0) + * + * Accepts either a boolean (yes/no/on/off/...) or a whitespace-separated list of namespace types, + * optionally preceded by '~' to invert the meaning. Valid namespace types are: cgroup, ipc, net, + * mnt, pid, user, uts, time. + */ +class ConfigParseNamespaceFlagsOptionValue : SimpleGrammarOptionValues( + "config_parse_namespace_flags", + SequenceCombinator( + AlternativeCombinator( + NAMESPACE_LIST, + BOOLEAN + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutAbortOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutAbortOptionValue.kt new file mode 100644 index 0000000..83a2433 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutAbortOptionValue.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 Service.TimeoutAbortSec. + * C Function: config_parse_service_timeout_abort(0) + * + * Delegates to config_parse_timeout_abort, which calls parse_sec on the value. An + * empty value clears the setting (handled elsewhere); otherwise the value is a + * non-negative integer optionally suffixed with a time unit, or the literal + * "infinity". + */ +class ConfigParseServiceTimeoutAbortOptionValue : SimpleGrammarOptionValues( + "config_parse_service_timeout_abort", + SequenceCombinator( + AlternativeCombinator( + FlexibleLiteralChoiceTerminal("infinity"), + RegexTerminal( + "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?", + "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?" + ) + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutOptionValue.kt new file mode 100644 index 0000000..20fe3a2 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutOptionValue.kt @@ -0,0 +1,26 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for Service.TimeoutSec, Service.TimeoutStartSec. + * C Function: config_parse_service_timeout(0) + * + * Per parse_sec_fix_0 -> parse_sec: accepts a time value (with optional unit + * suffix, possibly compound like "1min 30s") OR the literal "infinity". + * A bare integer with no suffix (including 0) is interpreted as seconds. + */ +class ConfigParseServiceTimeoutOptionValue : SimpleGrammarOptionValues( + "config_parse_service_timeout", + SequenceCombinator( + AlternativeCombinator( + FlexibleLiteralChoiceTerminal("infinity"), + RegexTerminal( + "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?(?:\\s+[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?)*", + "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?(?:\\s+[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?)*" + ) + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSiUint64OptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSiUint64OptionValue.kt new file mode 100644 index 0000000..a13cb6b --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSiUint64OptionValue.kt @@ -0,0 +1,23 @@ +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 Link.BitsPerSecond. + * + * C function: config_parse_si_uint64 in src/shared/conf-parser.c. Internally calls + * parse_size(rvalue, 1000, &sz), so the value is an unsigned decimal optionally followed by + * a case-sensitive SI suffix (K, M, G, T, P, E or B) — base 1000, not 1024. Negative values + * and lowercase suffixes (e.g. "1k") are rejected. Decimal fractions (e.g. "1.5G") are + * accepted by parse_size. + */ +class ConfigParseSiUint64OptionValue : SimpleGrammarOptionValues( + "config_parse_si_uint64", + SequenceCombinator( + OptionalWhitespacePrefix( + RegexTerminal("[0-9]+(\\.[0-9]+)?[a-zA-Z]?\\s*", "[0-9]+(\\.[0-9]+)?[KMGTPEB]?\\s*") + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTokenBucketFilterLatencyOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTokenBucketFilterLatencyOptionValue.kt new file mode 100644 index 0000000..b232de6 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTokenBucketFilterLatencyOptionValue.kt @@ -0,0 +1,22 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.* +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.SimpleGrammarOptionValues + +/** + * Validator for TokenBucketFilter.LatencySec + * C Function: config_parse_tbf_latency (QDISC_KIND_TBF) + * + * Uses parse_sec(), which accepts a non-negative integer optionally followed by + * a systemd time unit suffix (ms, us, µs, s, m, h, d, w, y). + */ +class ConfigParseTokenBucketFilterLatencyOptionValue : SimpleGrammarOptionValues( + "config_parse_tbf_latency", + SequenceCombinator( + RegexTerminal( + "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?", + "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?" + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseUdevPropertyNameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseUdevPropertyNameOptionValue.kt new file mode 100644 index 0000000..c1dea29 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseUdevPropertyNameOptionValue.kt @@ -0,0 +1,28 @@ +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 Link.ImportProperty and Link.UnsetProperty + * C Function: config_parse_udev_property_name(0) + * + * A whitespace-separated list of udev property names. A udev property name is + * validated by udev_property_name_is_valid which delegates to env_name_is_valid: + * - non-empty + * - first character must not be a digit + * - all characters must be ASCII letters, digits, or underscore + */ +class ConfigParseUdevPropertyNameOptionValue : SimpleGrammarOptionValues( + "config_parse_udev_property_name", + SequenceCombinator( + RegexTerminal("[A-Za-z_][A-Za-z0-9_]*", "[A-Za-z_][A-Za-z0-9_]*"), + ZeroOrMore( + SequenceCombinator( + WhitespaceTerminal(), + RegexTerminal("[A-Za-z_][A-Za-z0-9_]*", "[A-Za-z_][A-Za-z0-9_]*") + ) + ), + EOF() + ) +) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakeRttOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakeRttOptionValueTest.kt new file mode 100644 index 0000000..3f7915a --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakeRttOptionValueTest.kt @@ -0,0 +1,40 @@ +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 ConfigParseCakeRttOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [CAKE] + RTTSec=100ms + RTTSec=1s + RTTSec=100us + RTTSec=5m + RTTSec=200 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testInvalidValues() { + // language="unit file (systemd)" + val file = """ + [CAKE] + RTTSec=abc + RTTSec=10.5s + RTTSec=-1 + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(3, myFixture.doHighlighting()) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanBitrateOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanBitrateOptionValueTest.kt new file mode 100644 index 0000000..649afb2 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanBitrateOptionValueTest.kt @@ -0,0 +1,51 @@ +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 ConfigParseCanBitrateOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [CAN] + BitRate=100 + BitRate=1000000 + BitRate=500K + BitRate=1M + DataBitRate=2M + DataBitRate=125000 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [CAN] + BitRate=abc + BitRate=1.5M + DataBitRate=foo + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(3, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanTimeQuantaOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanTimeQuantaOptionValueTest.kt new file mode 100644 index 0000000..b39dde1 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanTimeQuantaOptionValueTest.kt @@ -0,0 +1,51 @@ +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 ConfigParseCanTimeQuantaOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [CAN] + TimeQuantaNSec=100 + TimeQuantaNSec=500ns + TimeQuantaNSec=1us + TimeQuantaNSec=1ms + DataTimeQuantaNSec=infinity + DataTimeQuantaNSec=1ms 500us + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [CAN] + TimeQuantaNSec=invalid + TimeQuantaNSec=-10ns + DataTimeQuantaNSec=10.5ms + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(3, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCoalesceSecOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCoalesceSecOptionValueTest.kt new file mode 100644 index 0000000..8225433 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCoalesceSecOptionValueTest.kt @@ -0,0 +1,56 @@ +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 ConfigParseCoalesceSecOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + RxCoalesceSec=0 + RxCoalesceIrqSec=1 + TxCoalesceSec=60 + TxCoalesceIrqSec=500ms + StatisticsBlockCoalesceSec=2s + RxCoalesceLowSec=10us + TxCoalesceLowSec=100µs + RxCoalesceHighSec=1m + TxCoalesceHighSec=1h + CoalescePacketRateSampleIntervalSec=1 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + RxCoalesceSec=-1 + TxCoalesceSec=abc + RxCoalesceIrqSec=1.5s + TxCoalesceIrqSec=5z + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(4, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseControlledDelayUsecOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseControlledDelayUsecOptionValueTest.kt new file mode 100644 index 0000000..44d447a --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseControlledDelayUsecOptionValueTest.kt @@ -0,0 +1,51 @@ +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 ConfigParseControlledDelayUsecOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [ControlledDelay] + TargetSec=10s + IntervalSec=100ms + CEThresholdSec=5 + TargetSec=1m + IntervalSec=2h + CEThresholdSec=500us + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [ControlledDelay] + TargetSec=invalid + IntervalSec=-10s + CEThresholdSec=10.5s + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(3, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDhcpSocketPriorityOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDhcpSocketPriorityOptionValueTest.kt new file mode 100644 index 0000000..35dc952 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDhcpSocketPriorityOptionValueTest.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 ConfigParseDhcpSocketPriorityOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [DHCPv4] + SocketPriority=0 + SocketPriority=6 + SocketPriority=-1 + SocketPriority=2147483647 + SocketPriority=-2147483648 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [DHCPv4] + SocketPriority=forever + SocketPriority=infinity + SocketPriority=abc + SocketPriority=1.5 + SocketPriority=2147483648 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(5, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsNameOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsNameOptionValueTest.kt new file mode 100644 index 0000000..2fb89ad --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsNameOptionValueTest.kt @@ -0,0 +1,50 @@ +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 ConfigParseDnsNameOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidDnsNames() { + // language="unit file (systemd)" + val file = """ + [DHCPServer] + BootServerName=valid-hostname.example.com + BootServerName=valid.hostname + BootServerName=single + BootServerName=name_with_underscore + BootServerName=mixed_chars-123.example.com + BootServerName=trailing.dot. + Domain=example.org + LocalLeaseDomain=lan.local + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(0, highlights) + } + + @Test + fun testInvalidDnsNames() { + // language="unit file (systemd)" + val file = """ + [DHCPServer] + BootServerName=-leading.hyphen + BootServerName=trailing-.com + BootServerName=double..dot + BootServerName=has space + BootServerName=has!bang + Domain=.leading.dot + """.trimIndent() + + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(6, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSecureBitsOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSecureBitsOptionValueTest.kt new file mode 100644 index 0000000..fecf3ff --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseExecSecureBitsOptionValueTest.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 ConfigParseExecSecureBitsOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Service] + SecureBits=keep-caps + SecureBits=keep-caps-locked + SecureBits=no-setuid-fixup + SecureBits=no-setuid-fixup-locked + SecureBits=noroot + SecureBits=noroot-locked + SecureBits=keep-caps keep-caps-locked no-setuid-fixup no-setuid-fixup-locked noroot noroot-locked + SecureBits=keep-caps noroot + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Service] + SecureBits=invalid_value + SecureBits=keep-caps invalid_value + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingBoolOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingBoolOptionValueTest.kt new file mode 100644 index 0000000..fc800eb --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingBoolOptionValueTest.kt @@ -0,0 +1,58 @@ +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 ConfigParseFairQueueingBoolOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [FairQueueing] + Pacing=yes + Pacing=no + Pacing=true + Pacing=false + Pacing=on + Pacing=off + Pacing=y + Pacing=n + Pacing=t + Pacing=f + Pacing=1 + Pacing=0 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [FairQueueing] + Pacing=invalid + Pacing=2 + Pacing=yesno + Pacing=auto + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(4, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingSizeOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingSizeOptionValueTest.kt new file mode 100644 index 0000000..dab1423 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingSizeOptionValueTest.kt @@ -0,0 +1,55 @@ +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 ConfigParseFairQueueingSizeOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [FairQueueing] + QuantumBytes=1024 + InitialQuantumBytes=4096 + QuantumBytes=1K + InitialQuantumBytes=64K + QuantumBytes=1M + InitialQuantumBytes=2G + QuantumBytes=1T + InitialQuantumBytes=0 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [FairQueueing] + QuantumBytes=abc + InitialQuantumBytes=1024L + QuantumBytes=10.5M + InitialQuantumBytes=0x1000 + QuantumBytes=K + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(5, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingU32OptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingU32OptionValueTest.kt new file mode 100644 index 0000000..0970672 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFairQueueingU32OptionValueTest.kt @@ -0,0 +1,50 @@ +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 ConfigParseFairQueueingU32OptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [FairQueueing] + PacketLimit=0 + FlowLimit=100 + Buckets=1024 + OrphanMask=4294967295 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [FairQueueing] + PacketLimit=-1 + FlowLimit=4294967296 + Buckets=abc + OrphanMask=3.14 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(4, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFdbVlanIdOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFdbVlanIdOptionValueTest.kt new file mode 100644 index 0000000..58d4349 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFdbVlanIdOptionValueTest.kt @@ -0,0 +1,89 @@ +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 ConfigParseFdbVlanIdOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [BridgeFDB] + VLANId=0 + VLANId=1 + VLANId=42 + VLANId=1000 + VLANId=4094 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [BridgeFDB] + VLANId=-1 + VLANId=4095 + VLANId=4096 + VLANId=abc + VLANId=1.5 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(5, highlights) + } + + @Test + fun testBoundaryValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [BridgeFDB] + VLANId=0 + VLANId=4094 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidBoundary() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [BridgeFDB] + VLANId=4095 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(1, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFdnameOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFdnameOptionValueTest.kt new file mode 100644 index 0000000..d7878ce --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseFdnameOptionValueTest.kt @@ -0,0 +1,48 @@ +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 ConfigParseFdnameOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Socket] + FileDescriptorName=valid_name_1 + FileDescriptorName=valid-name-2 + FileDescriptorName=AnotherFd + FileDescriptorName=fd.with.dots + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup - both lines contain a ':' which fdname_is_valid rejects + // language="unit file (systemd)" + val file = """ + [Socket] + FileDescriptorName=invalid:name_1 + FileDescriptorName=invalid:name_2 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.socket", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(2, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseJobRunningTimeoutSecOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseJobRunningTimeoutSecOptionValueTest.kt new file mode 100644 index 0000000..b06e82d --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseJobRunningTimeoutSecOptionValueTest.kt @@ -0,0 +1,53 @@ +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 ConfigParseJobRunningTimeoutSecOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Unit] + JobRunningTimeoutSec=10 + JobRunningTimeoutSec=10s + JobRunningTimeoutSec=100ms + JobRunningTimeoutSec=1m + JobRunningTimeoutSec=1h + JobRunningTimeoutSec=1d + JobRunningTimeoutSec=infinity + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Unit] + JobRunningTimeoutSec=invalid + JobRunningTimeoutSec=10x + JobRunningTimeoutSec=10 s + JobRunningTimeoutSec=10.5.2s + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(4, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseNamespaceFlagsOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseNamespaceFlagsOptionValueTest.kt new file mode 100644 index 0000000..343ddd2 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseNamespaceFlagsOptionValueTest.kt @@ -0,0 +1,122 @@ +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 ConfigParseNamespaceFlagsOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidSingleNamespaceTypes() { + // language="unit file (systemd)" + val file = """ + [Service] + RestrictNamespaces=cgroup + RestrictNamespaces=ipc + RestrictNamespaces=net + RestrictNamespaces=mnt + RestrictNamespaces=pid + RestrictNamespaces=user + RestrictNamespaces=uts + RestrictNamespaces=time + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidWhitespaceSeparatedList() { + // language="unit file (systemd)" + val file = """ + [Service] + RestrictNamespaces=cgroup ipc + RestrictNamespaces=mnt uts ipc + RestrictNamespaces=cgroup ipc net mnt pid user uts time + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidInvertedList() { + // language="unit file (systemd)" + val file = """ + [Service] + RestrictNamespaces=~cgroup + RestrictNamespaces=~cgroup net + RestrictNamespaces=~mnt uts ipc + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidBooleans() { + // language="unit file (systemd)" + val file = """ + [Service] + RestrictNamespaces=yes + RestrictNamespaces=no + RestrictNamespaces=true + RestrictNamespaces=false + RestrictNamespaces=on + RestrictNamespaces=off + RestrictNamespaces=1 + RestrictNamespaces=0 + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testValidDelegateNamespaces() { + // language="unit file (systemd)" + val file = """ + [Service] + DelegateNamespaces=mnt + DelegateNamespaces=~user + DelegateNamespaces=yes + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(0, myFixture.doHighlighting()) + } + + @Test + fun testInvalidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + RestrictNamespaces=invalid_value_1 + RestrictNamespaces=foo bar + RestrictNamespaces=cgroup,ipc + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(3, myFixture.doHighlighting()) + } + + @Test + fun testInvalidTrailingTilde() { + // language="unit file (systemd)" + val file = """ + [Service] + RestrictNamespaces=cgroup~ + RestrictNamespaces=cgroup ~ipc + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + assertSize(2, myFixture.doHighlighting()) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseServiceTimeoutAbortOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseServiceTimeoutAbortOptionValueTest.kt new file mode 100644 index 0000000..7ca97cf --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseServiceTimeoutAbortOptionValueTest.kt @@ -0,0 +1,45 @@ +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 ConfigParseServiceTimeoutAbortOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + TimeoutAbortSec=10 + TimeoutAbortSec=10s + TimeoutAbortSec=500ms + TimeoutAbortSec=5min + TimeoutAbortSec=2hour + TimeoutAbortSec=infinity + """.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] + TimeoutAbortSec=abc + TimeoutAbortSec=-10 + TimeoutAbortSec=10x + """.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/ConfigParseServiceTimeoutOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseServiceTimeoutOptionValueTest.kt new file mode 100644 index 0000000..a746988 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseServiceTimeoutOptionValueTest.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 ConfigParseServiceTimeoutOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Service] + TimeoutSec=infinity + TimeoutStartSec=5s + TimeoutSec=10m + TimeoutStartSec=30 + TimeoutSec=0 + TimeoutStartSec=500ms + TimeoutSec=2h + TimeoutStartSec=1min 30s + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Service] + TimeoutStartSec=abc + TimeoutSec=-10s + TimeoutStartSec=INFINITY + TimeoutSec=forever + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(4, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSiUint64OptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSiUint64OptionValueTest.kt new file mode 100644 index 0000000..3ecffdf --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSiUint64OptionValueTest.kt @@ -0,0 +1,51 @@ +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 ConfigParseSiUint64OptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + BitsPerSecond=1000 + BitsPerSecond=1K + BitsPerSecond=1M + BitsPerSecond=1.5G + BitsPerSecond=2T + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + BitsPerSecond=abc + BitsPerSecond=-100 + BitsPerSecond=1k + BitsPerSecond=1Kb + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(4, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTokenBucketFilterLatencyOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTokenBucketFilterLatencyOptionValueTest.kt new file mode 100644 index 0000000..b7f97dc --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTokenBucketFilterLatencyOptionValueTest.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 ConfigParseTokenBucketFilterLatencyOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [TokenBucketFilter] + LatencySec=10s + LatencySec=100ms + LatencySec=1m + LatencySec=1h + LatencySec=500us + LatencySec=42 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [TokenBucketFilter] + LatencySec=invalid + LatencySec=-10 + LatencySec=10x + LatencySec=1.5s + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.network", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(4, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUdevPropertyNameOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUdevPropertyNameOptionValueTest.kt new file mode 100644 index 0000000..a78439b --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseUdevPropertyNameOptionValueTest.kt @@ -0,0 +1,116 @@ +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 ConfigParseUdevPropertyNameOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidSingleProperty() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + UnsetProperty=ID_NET_NAME + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testValidMultipleProperties() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + UnsetProperty=ID_NET_NAME ID_NET_NAME_PATH ID_NET_NAME_SLOT + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testValidLowercaseAndUnderscoreLeading() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + ImportProperty=lowercase_ok _leading_underscore MIXED_Case123 + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + @Test + fun testInvalidLeadingDigit() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + UnsetProperty=1INVALID + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(1, highlights) + } + + @Test + fun testInvalidContainsHyphen() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + UnsetProperty=ID-NET-NAME + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(1, highlights) + } + + @Test + fun testInvalidContainsDot() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Link] + ImportProperty=ID.NET.NAME + """.trimIndent() + + // Execute SUT + setupFileInEditor("file.link", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(1, highlights) + } +} From 6f3cb9f49a4b1591a6ca826bfff708216878446e Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Fri, 1 May 2026 06:16:57 -0700 Subject: [PATCH 4/5] fix: align AI validators with actual systemd C parser semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds shared TIME_VALUE combinator (mirrors extract_multiplier in time-util.c) that accepts "infinity", fractional decimals, all long-form suffixes (seconds/minutes/hours/days/months/years/etc.), whitespace between number and suffix, and compound forms like "1h 30s". Eight time-related validators now use it: cake_rtt, coalesce_sec, codel_usec, tbf_latency, service_timeout, service_timeout_abort, job_running_timeout_sec, can_time_quanta. can_bitrate now accepts the full parse_size suffix set (B/K/M/G/T/P/E) with optional fractional part. dns_name regex relaxed to match dns_label_unescape's permissive behavior — leading/trailing hyphens, underscores and most printable punctuation are now accepted; only empty labels are rejected. ifname tightened to reject ':', '%', the reserved names "all"/"default"/"."/"..", and purely-numeric strings (interpreted as ifindex by the kernel). Tests updated to match the corrected semantics. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ai/ConfigParseCakeRttOptionValue.kt | 12 +++------ .../ai/ConfigParseCanBitrateOptionValue.kt | 11 +++++--- .../ai/ConfigParseCanTimeQuantaOptionValue.kt | 17 +++---------- .../ai/ConfigParseCoalesceSecOptionValue.kt | 15 +++-------- ...nfigParseControlledDelayUsecOptionValue.kt | 12 +++------ .../ai/ConfigParseDnsNameOptionValue.kt | 20 +++++++-------- .../ai/ConfigParseIfnameOptionValue.kt | 15 +++++------ ...figParseJobRunningTimeoutSecOptionValue.kt | 17 +++---------- ...nfigParseServiceTimeoutAbortOptionValue.kt | 18 +++---------- .../ConfigParseServiceTimeoutOptionValue.kt | 17 +++---------- ...arseTokenBucketFilterLatencyOptionValue.kt | 12 +++------ .../optionvalues/grammar/Combinators.kt | 11 ++++++++ .../ai/ConfigParseCakeRttOptionValueTest.kt | 6 +++-- .../ConfigParseCanBitrateOptionValueTest.kt | 18 ++++--------- ...ConfigParseCanTimeQuantaOptionValueTest.kt | 16 +++--------- .../ConfigParseCoalesceSecOptionValueTest.kt | 25 +++++++------------ ...ParseControlledDelayUsecOptionValueTest.kt | 18 ++++--------- .../ai/ConfigParseDnsNameOptionValueTest.kt | 9 +++---- .../ai/ConfigParseIfnameOptionValueTest.kt | 14 ++++++----- ...arseJobRunningTimeoutSecOptionValueTest.kt | 19 +++++--------- ...TokenBucketFilterLatencyOptionValueTest.kt | 24 ++++++------------ 21 files changed, 115 insertions(+), 211 deletions(-) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakeRttOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakeRttOptionValue.kt index b74d695..8459883 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakeRttOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCakeRttOptionValue.kt @@ -7,16 +7,10 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.Simp * Validator for CAKE.RTTSec (.network). * C Function: config_parse_cake_rtt(QDISC_KIND_CAKE) * - * Calls parse_sec; accepts a positive systemd time value (ms/us/s/m/h/d/...). - * Empty resets and is always valid (handled outside the grammar). + * Calls parse_sec, which accepts "infinity", a fractional or integer number + * with any of systemd's time-unit suffixes, and compound forms like "1h 30s". */ class ConfigParseCakeRttOptionValue : SimpleGrammarOptionValues( "config_parse_cake_rtt", - SequenceCombinator( - RegexTerminal( - "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?", - "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?" - ), - EOF() - ) + SequenceCombinator(TIME_VALUE, EOF()) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanBitrateOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanBitrateOptionValue.kt index 45038a9..f4b845f 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanBitrateOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanBitrateOptionValue.kt @@ -7,14 +7,17 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.Simp * Validator for CAN.BitRate and CAN.DataBitRate. * C Function: config_parse_can_bitrate(0) in src/network/networkd-can.c. * - * Internally calls parse_size(rvalue, 1000, &sz), so the value is a decimal bit count - * optionally suffixed with an SI unit (K, M, G, ...). The result must fit in a uint32_t, - * i.e. be in the range 1..4294967295. + * Internally calls parse_size(rvalue, 1000, &sz). parse_size accepts a decimal + * number (with optional fractional part) optionally suffixed with B/K/M/G/T/P/E. + * The result must fit in a uint32_t; the range check is not enforced here. */ class ConfigParseCanBitrateOptionValue : SimpleGrammarOptionValues( "config_parse_can_bitrate", SequenceCombinator( - RegexTerminal("[0-9]+(?:K|M|G)?", "[0-9]+(?:K|M|G)?"), + RegexTerminal( + "[0-9]+(?:\\.[0-9]+)?\\s*[BKMGTPE]?", + "[0-9]+(?:\\.[0-9]+)?\\s*[BKMGTPE]?" + ), EOF() ) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanTimeQuantaOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanTimeQuantaOptionValue.kt index d083fc0..b914cfc 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanTimeQuantaOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCanTimeQuantaOptionValue.kt @@ -7,20 +7,11 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.Simp * Validator for CAN.TimeQuantaNSec, CAN.DataTimeQuantaNSec. * C Function: config_parse_can_time_quanta(0) * - * Per parse_nsec: accepts a time value (with optional unit suffix, possibly - * compound like "1ms 500us") OR the literal "infinity". A bare integer with no - * suffix is interpreted as nanoseconds. + * Per parse_nsec, accepts "infinity", a fractional or integer number with any of + * systemd's time-unit suffixes (default unit: nanoseconds), and compound forms + * like "1ms 500us". */ class ConfigParseCanTimeQuantaOptionValue : SimpleGrammarOptionValues( "config_parse_can_time_quanta", - SequenceCombinator( - AlternativeCombinator( - FlexibleLiteralChoiceTerminal("infinity"), - RegexTerminal( - "[0-9]+(?:ns|nsec|us|µs|μs|ms|msec|s|sec|seconds?|m|min|minutes?|h|hr|hours?|d|days?|w|weeks?|M|months?|y|years?)?(?:\\s+[0-9]+(?:ns|nsec|us|µs|μs|ms|msec|s|sec|seconds?|m|min|minutes?|h|hr|hours?|d|days?|w|weeks?|M|months?|y|years?)?)*", - "[0-9]+(?:ns|nsec|us|µs|μs|ms|msec|s|sec|seconds?|m|min|minutes?|h|hr|hours?|d|days?|w|weeks?|M|months?|y|years?)?(?:\\s+[0-9]+(?:ns|nsec|us|µs|μs|ms|msec|s|sec|seconds?|m|min|minutes?|h|hr|hours?|d|days?|w|weeks?|M|months?|y|years?)?)*" - ) - ), - EOF() - ) + SequenceCombinator(TIME_VALUE, EOF()) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCoalesceSecOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCoalesceSecOptionValue.kt index d0b2c19..0a8452b 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCoalesceSecOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCoalesceSecOptionValue.kt @@ -9,19 +9,10 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.Simp * RxCoalesceHighSec, TxCoalesceHighSec, CoalescePacketRateSampleIntervalSec}. * C Function: config_parse_coalesce_sec(0) * - * Per src/shared/ethtool-util.c, the rvalue is parsed via parse_sec() which accepts a - * non-negative integer with an optional time unit suffix (defaulting to seconds when - * no suffix is present). The C code further rejects values exceeding UINT32_MAX usec - * and zero values for two specific keys, but those numeric checks cannot be enforced - * at the grammar level here (the same validator is shared across all ten keys). + * Calls parse_sec, which accepts "infinity", a fractional or integer number with any + * of systemd's time-unit suffixes, and compound forms like "1h 30s". */ class ConfigParseCoalesceSecOptionValue : SimpleGrammarOptionValues( "config_parse_coalesce_sec", - SequenceCombinator( - RegexTerminal( - "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?", - "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?" - ), - EOF() - ) + SequenceCombinator(TIME_VALUE, EOF()) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseControlledDelayUsecOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseControlledDelayUsecOptionValue.kt index 8c1c408..91ddf84 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseControlledDelayUsecOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseControlledDelayUsecOptionValue.kt @@ -7,16 +7,10 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.Simp * Validator for ControlledDelay.TargetSec, ControlledDelay.IntervalSec, ControlledDelay.CEThresholdSec * C Function: config_parse_codel_usec(QDISC_KIND_CODEL) * - * Parses a time value via parse_sec(), accepting an integer optionally followed - * by a time unit suffix (ms, us, µs, s, m, h, d, w, y). + * Calls parse_sec, which accepts "infinity", a fractional or integer number with any + * of systemd's time-unit suffixes, and compound forms like "1h 30s". */ class ConfigParseControlledDelayUsecOptionValue : SimpleGrammarOptionValues( "config_parse_codel_usec", - SequenceCombinator( - RegexTerminal( - "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?", - "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?" - ), - EOF() - ) + SequenceCombinator(TIME_VALUE, EOF()) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt index a492f10..d7e41a2 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt @@ -10,22 +10,20 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.gram * * Used by .network DHCPServer.Domain, DHCPServer.BootServerName, DHCPServer.LocalLeaseDomain. * - * Mirrors dns_name_is_valid(s) (no DNS_LABEL_LDH flag), which validates via - * dns_name_concat -> dns_label_unescape. Without the LDH flag, label characters - * are more permissive than hostname_is_valid (e.g. underscores are allowed and - * leading/trailing hyphens are not specifically rejected). A single optional - * trailing dot is accepted to denote a fully-qualified name. - * - * Note: dns_label_unescape also supports backslash-escaped characters and - * decimal-escaped octets ("\NNN"); these are extremely rare in DHCP server - * configuration values and are intentionally not modelled here. + * Mirrors dns_name_is_valid (no DNS_LABEL_LDH flag): each label is a non-empty + * sequence of any characters except '.' (label separator) and '\' (escape + * introducer); labels are joined by single dots; an optional trailing dot + * denotes a fully-qualified name. Empty labels (e.g. ".." or a leading ".") + * are rejected. This is intentionally far more permissive than hostname + * validation — leading/trailing hyphens, underscores, spaces, and most + * printable punctuation are all accepted by the C validator. */ class ConfigParseDnsNameOptionValue : SimpleGrammarOptionValues( "config_parse_dns_name", SequenceCombinator( RegexTerminal( - "[a-zA-Z0-9_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])?(?:\\.[a-zA-Z0-9_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])?)*\\.?", - "[a-zA-Z0-9_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])?(?:\\.[a-zA-Z0-9_](?:[a-zA-Z0-9_-]*[a-zA-Z0-9_])?)*\\.?" + "[^.\\\\]+(?:\\.[^.\\\\]+)*\\.?", + "[^.\\\\]+(?:\\.[^.\\\\]+)*\\.?" ), EOF() ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt index df4181e..27d4a6c 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt @@ -8,19 +8,20 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.Simp * Network.VRF, Network.BatmanAdvanced in .network; NetDev.Name, Peer.Name, VXCAN.Peer in * .netdev; Link.Name in .link; Network.Bridge in .nspawn). * - * The C function delegates to ifname_valid(), which requires a Linux interface name: + * Delegates to ifname_valid, which requires a Linux interface name: * - 1..15 characters - * - no whitespace, no '/' + * - no whitespace, no '/', no ':', no '%' (rejected by ifname_valid_char) * - cannot be "." or ".." - * - must be valid UTF-8 (effectively printable ASCII in practice) - * - * The grammar below is a reasonable approximation: 1..15 characters that are not - * whitespace and not '/'. The "." / ".." edge cases are not modelled. + * - cannot be the reserved names "all" or "default" + * - cannot be a purely-numeric string (interpreted as ifindex) */ class ConfigParseIfnameOptionValue : SimpleGrammarOptionValues( "config_parse_ifname", SequenceCombinator( - RegexTerminal("[^\\s/]{1,15}", "[^\\s/]{1,15}"), + RegexTerminal( + "(?!(?:all|default|\\.{1,2}|[0-9]+)\\Z)[^\\s:/%]{1,15}", + "(?!(?:all|default|\\.{1,2}|[0-9]+)\\Z)[^\\s:/%]{1,15}" + ), EOF() ) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseJobRunningTimeoutSecOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseJobRunningTimeoutSecOptionValue.kt index eb8916d..f41d1df 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseJobRunningTimeoutSecOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseJobRunningTimeoutSecOptionValue.kt @@ -7,20 +7,11 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.Simp * Validator for Unit.JobRunningTimeoutSec * C Function: config_parse_job_running_timeout_sec * - * Parses a time value via parse_sec_fix_0(), accepting either the literal - * "infinity" or an integer optionally followed by a time unit suffix - * (ms, us, µs, s, m, h, d, w, y, min, sec, hour, day, week, year). + * Parses a time value via parse_sec_fix_0 -> parse_sec, which accepts "infinity", + * a fractional or integer number with any of systemd's time-unit suffixes, and + * compound forms like "1h 30s". */ class ConfigParseJobRunningTimeoutSecOptionValue : SimpleGrammarOptionValues( "config_parse_job_running_timeout_sec", - SequenceCombinator( - AlternativeCombinator( - FlexibleLiteralChoiceTerminal("infinity"), - RegexTerminal( - "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?", - "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?" - ) - ), - EOF() - ) + SequenceCombinator(TIME_VALUE, EOF()) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutAbortOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutAbortOptionValue.kt index 83a2433..907a59c 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutAbortOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutAbortOptionValue.kt @@ -7,21 +7,11 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.gram * Validator for Service.TimeoutAbortSec. * C Function: config_parse_service_timeout_abort(0) * - * Delegates to config_parse_timeout_abort, which calls parse_sec on the value. An - * empty value clears the setting (handled elsewhere); otherwise the value is a - * non-negative integer optionally suffixed with a time unit, or the literal - * "infinity". + * Delegates to config_parse_timeout_abort, which calls parse_sec — accepts + * "infinity", a fractional or integer number with any of systemd's time-unit + * suffixes, and compound forms like "1h 30s". */ class ConfigParseServiceTimeoutAbortOptionValue : SimpleGrammarOptionValues( "config_parse_service_timeout_abort", - SequenceCombinator( - AlternativeCombinator( - FlexibleLiteralChoiceTerminal("infinity"), - RegexTerminal( - "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?", - "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?" - ) - ), - EOF() - ) + SequenceCombinator(TIME_VALUE, EOF()) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutOptionValue.kt index 20fe3a2..b961d7b 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseServiceTimeoutOptionValue.kt @@ -7,20 +7,11 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.Simp * Validator for Service.TimeoutSec, Service.TimeoutStartSec. * C Function: config_parse_service_timeout(0) * - * Per parse_sec_fix_0 -> parse_sec: accepts a time value (with optional unit - * suffix, possibly compound like "1min 30s") OR the literal "infinity". - * A bare integer with no suffix (including 0) is interpreted as seconds. + * Calls parse_sec_fix_0 -> parse_sec, which accepts "infinity", a fractional or + * integer number with any of systemd's time-unit suffixes, and compound forms + * like "1min 30s". */ class ConfigParseServiceTimeoutOptionValue : SimpleGrammarOptionValues( "config_parse_service_timeout", - SequenceCombinator( - AlternativeCombinator( - FlexibleLiteralChoiceTerminal("infinity"), - RegexTerminal( - "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?(?:\\s+[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?)*", - "[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?(?:\\s+[0-9]+(?:year|week|hour|day|min|sec|ms|us|µs|s|m|h|d|w|y)?)*" - ) - ), - EOF() - ) + SequenceCombinator(TIME_VALUE, EOF()) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTokenBucketFilterLatencyOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTokenBucketFilterLatencyOptionValue.kt index b232de6..b43f936 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTokenBucketFilterLatencyOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseTokenBucketFilterLatencyOptionValue.kt @@ -7,16 +7,10 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.Simp * Validator for TokenBucketFilter.LatencySec * C Function: config_parse_tbf_latency (QDISC_KIND_TBF) * - * Uses parse_sec(), which accepts a non-negative integer optionally followed by - * a systemd time unit suffix (ms, us, µs, s, m, h, d, w, y). + * Calls parse_sec, which accepts "infinity", a fractional or integer number with any + * of systemd's time-unit suffixes, and compound forms like "1h 30s". */ class ConfigParseTokenBucketFilterLatencyOptionValue : SimpleGrammarOptionValues( "config_parse_tbf_latency", - SequenceCombinator( - RegexTerminal( - "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?", - "[0-9]+(?:ms|us|µs|s|m|h|d|w|y)?" - ), - EOF() - ) + SequenceCombinator(TIME_VALUE, EOF()) ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/Combinators.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/Combinators.kt index 5bf6141..825f0c2 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/Combinators.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/Combinators.kt @@ -5,6 +5,17 @@ val BYTES = RegexTerminal("[0-9]+[a-zA-Z]*\\s*", "[0-9]+[KMGT]?\\s*") val DEVICE = RegexTerminal("\\S+\\s*", "/[^\\u0000. ]+\\s*") val IOPS = RegexTerminal("[0-9]+[a-zA-Z]*\\s*", "[0-9]+[KMGT]?\\s*") +// Time-suffix list mirrors systemd's extract_multiplier (parse_sec, parse_nsec). +// Longer alternatives MUST come first so that `min` is tried before `m`, etc. +private const val TIME_SUFFIX = "(?:seconds|minutes|months|second|minute|month|years|weeks|hours|usec|msec|nsec|year|week|hour|days|min|sec|day|hr|µs|μs|ns|us|ms|s|m|h|d|w|y|M)" +private const val TIME_NUMBER = "[0-9]+(?:\\.[0-9]+)?" +private const val TIME_ELEMENT = "$TIME_NUMBER\\s*$TIME_SUFFIX?" +private const val TIME_COMPOUND = "$TIME_ELEMENT(?:\\s+$TIME_ELEMENT)*" +val TIME_VALUE = AlternativeCombinator( + FlexibleLiteralChoiceTerminal("infinity"), + RegexTerminal(TIME_COMPOUND, TIME_COMPOUND) +) + var IPV4_OCTET = IntegerTerminal(0, 256) val DOT = LiteralChoiceTerminal(".") var IPV4_ADDR = SequenceCombinator(IPV4_OCTET, DOT, IPV4_OCTET, DOT, IPV4_OCTET, DOT, IPV4_OCTET) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakeRttOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakeRttOptionValueTest.kt index 3f7915a..22e9c76 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakeRttOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCakeRttOptionValueTest.kt @@ -16,6 +16,9 @@ class ConfigParseCakeRttOptionValueTest : AbstractUnitFileTest() { RTTSec=100us RTTSec=5m RTTSec=200 + RTTSec=10.5s + RTTSec=infinity + RTTSec=2hour """.trimIndent() setupFileInEditor("file.network", file) @@ -29,12 +32,11 @@ class ConfigParseCakeRttOptionValueTest : AbstractUnitFileTest() { val file = """ [CAKE] RTTSec=abc - RTTSec=10.5s RTTSec=-1 """.trimIndent() setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - assertSize(3, myFixture.doHighlighting()) + assertSize(2, myFixture.doHighlighting()) } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanBitrateOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanBitrateOptionValueTest.kt index 649afb2..e8ba2b2 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanBitrateOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanBitrateOptionValueTest.kt @@ -8,7 +8,6 @@ class ConfigParseCanBitrateOptionValueTest : AbstractUnitFileTest() { @Test fun testValidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [CAN] @@ -18,34 +17,27 @@ class ConfigParseCanBitrateOptionValueTest : AbstractUnitFileTest() { BitRate=1M DataBitRate=2M DataBitRate=125000 + BitRate=1.5M + DataBitRate=1G + BitRate=42B """.trimIndent() - // Execute SUT setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(0, highlights) + assertSize(0, myFixture.doHighlighting()) } @Test fun testInvalidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [CAN] BitRate=abc - BitRate=1.5M DataBitRate=foo """.trimIndent() - // Execute SUT setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(3, highlights) + assertSize(2, myFixture.doHighlighting()) } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanTimeQuantaOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanTimeQuantaOptionValueTest.kt index b39dde1..54141ae 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanTimeQuantaOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCanTimeQuantaOptionValueTest.kt @@ -8,7 +8,6 @@ class ConfigParseCanTimeQuantaOptionValueTest : AbstractUnitFileTest() { @Test fun testValidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [CAN] @@ -18,34 +17,25 @@ class ConfigParseCanTimeQuantaOptionValueTest : AbstractUnitFileTest() { TimeQuantaNSec=1ms DataTimeQuantaNSec=infinity DataTimeQuantaNSec=1ms 500us + DataTimeQuantaNSec=10.5ms """.trimIndent() - // Execute SUT setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(0, highlights) + assertSize(0, myFixture.doHighlighting()) } @Test fun testInvalidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [CAN] TimeQuantaNSec=invalid TimeQuantaNSec=-10ns - DataTimeQuantaNSec=10.5ms """.trimIndent() - // Execute SUT setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(3, highlights) + assertSize(2, myFixture.doHighlighting()) } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCoalesceSecOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCoalesceSecOptionValueTest.kt index 8225433..cdcec36 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCoalesceSecOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCoalesceSecOptionValueTest.kt @@ -8,7 +8,6 @@ class ConfigParseCoalesceSecOptionValueTest : AbstractUnitFileTest() { @Test fun testValidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [Link] @@ -22,35 +21,29 @@ class ConfigParseCoalesceSecOptionValueTest : AbstractUnitFileTest() { RxCoalesceHighSec=1m TxCoalesceHighSec=1h CoalescePacketRateSampleIntervalSec=1 + RxCoalesceSec=1.5s + TxCoalesceSec=1min 30sec + RxCoalesceSec=infinity + TxCoalesceSec=2hour """.trimIndent() - // Execute SUT setupFileInEditor("file.link", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(0, highlights) + assertSize(0, myFixture.doHighlighting()) } @Test fun testInvalidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [Link] - RxCoalesceSec=-1 - TxCoalesceSec=abc - RxCoalesceIrqSec=1.5s - TxCoalesceIrqSec=5z + RxCoalesceSec=-1 + TxCoalesceSec=abc + TxCoalesceIrqSec=5z """.trimIndent() - // Execute SUT setupFileInEditor("file.link", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(4, highlights) + assertSize(3, myFixture.doHighlighting()) } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseControlledDelayUsecOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseControlledDelayUsecOptionValueTest.kt index 44d447a..ee0af9e 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseControlledDelayUsecOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseControlledDelayUsecOptionValueTest.kt @@ -8,7 +8,6 @@ class ConfigParseControlledDelayUsecOptionValueTest : AbstractUnitFileTest() { @Test fun testValidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [ControlledDelay] @@ -18,34 +17,27 @@ class ConfigParseControlledDelayUsecOptionValueTest : AbstractUnitFileTest() { TargetSec=1m IntervalSec=2h CEThresholdSec=500us + TargetSec=10.5s + IntervalSec=infinity + CEThresholdSec=1min 30s """.trimIndent() - // Execute SUT setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(0, highlights) + assertSize(0, myFixture.doHighlighting()) } @Test fun testInvalidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [ControlledDelay] TargetSec=invalid IntervalSec=-10s - CEThresholdSec=10.5s """.trimIndent() - // Execute SUT setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(3, highlights) + assertSize(2, myFixture.doHighlighting()) } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsNameOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsNameOptionValueTest.kt index 2fb89ad..617f541 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsNameOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDnsNameOptionValueTest.kt @@ -17,6 +17,9 @@ class ConfigParseDnsNameOptionValueTest : AbstractUnitFileTest() { BootServerName=name_with_underscore BootServerName=mixed_chars-123.example.com BootServerName=trailing.dot. + BootServerName=-leading.hyphen + BootServerName=trailing-.com + BootServerName=has!bang Domain=example.org LocalLeaseDomain=lan.local """.trimIndent() @@ -33,11 +36,7 @@ class ConfigParseDnsNameOptionValueTest : AbstractUnitFileTest() { // language="unit file (systemd)" val file = """ [DHCPServer] - BootServerName=-leading.hyphen - BootServerName=trailing-.com BootServerName=double..dot - BootServerName=has space - BootServerName=has!bang Domain=.leading.dot """.trimIndent() @@ -45,6 +44,6 @@ class ConfigParseDnsNameOptionValueTest : AbstractUnitFileTest() { enableInspection(InvalidValueInspection::class.java) val highlights = myFixture.doHighlighting() - assertSize(6, highlights) + assertSize(2, highlights) } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt index d943ac5..2338397 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt @@ -16,28 +16,30 @@ class ConfigParseIfnameOptionValueTest : AbstractUnitFileTest() { Bridge=my-iface Bridge=iface_1 Bridge=abcdefghijklmno + Bridge=12eth """.trimIndent() setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - assertSize(0, highlights) + assertSize(0, myFixture.doHighlighting()) } @Test fun testInvalidInterfaceNames() { - // Names containing '/' are rejected, and names longer than 15 characters - // are rejected by ifname_valid(). // language="unit file (systemd)" val file = """ [Network] Bridge=bad/name Bridge=thisnameiswaytoolong + Bridge=eth0:1 + Bridge=eth%d + Bridge=all + Bridge=default + Bridge=1234 """.trimIndent() setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - assertSize(2, highlights) + assertSize(7, myFixture.doHighlighting()) } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseJobRunningTimeoutSecOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseJobRunningTimeoutSecOptionValueTest.kt index b06e82d..2630909 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseJobRunningTimeoutSecOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseJobRunningTimeoutSecOptionValueTest.kt @@ -8,7 +8,6 @@ class ConfigParseJobRunningTimeoutSecOptionValueTest : AbstractUnitFileTest() { @Test fun testValidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [Unit] @@ -19,35 +18,29 @@ class ConfigParseJobRunningTimeoutSecOptionValueTest : AbstractUnitFileTest() { JobRunningTimeoutSec=1h JobRunningTimeoutSec=1d JobRunningTimeoutSec=infinity + JobRunningTimeoutSec=10 s + JobRunningTimeoutSec=1.5h + JobRunningTimeoutSec=1min 30s + JobRunningTimeoutSec=2hour """.trimIndent() - // Execute SUT setupFileInEditor("file.service", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(0, highlights) + assertSize(0, myFixture.doHighlighting()) } @Test fun testInvalidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [Unit] JobRunningTimeoutSec=invalid JobRunningTimeoutSec=10x - JobRunningTimeoutSec=10 s JobRunningTimeoutSec=10.5.2s """.trimIndent() - // Execute SUT setupFileInEditor("file.service", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(4, highlights) + assertSize(3, myFixture.doHighlighting()) } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTokenBucketFilterLatencyOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTokenBucketFilterLatencyOptionValueTest.kt index b7f97dc..8b52230 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTokenBucketFilterLatencyOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseTokenBucketFilterLatencyOptionValueTest.kt @@ -8,7 +8,6 @@ class ConfigParseTokenBucketFilterLatencyOptionValueTest : AbstractUnitFileTest( @Test fun testValidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [TokenBucketFilter] @@ -18,35 +17,28 @@ class ConfigParseTokenBucketFilterLatencyOptionValueTest : AbstractUnitFileTest( LatencySec=1h LatencySec=500us LatencySec=42 + LatencySec=1.5s + LatencySec=infinity + LatencySec=2hour """.trimIndent() - // Execute SUT setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(0, highlights) + assertSize(0, myFixture.doHighlighting()) } @Test fun testInvalidValues() { - // Fixture Setup // language="unit file (systemd)" val file = """ [TokenBucketFilter] - LatencySec=invalid - LatencySec=-10 - LatencySec=10x - LatencySec=1.5s + LatencySec=invalid + LatencySec=-10 + LatencySec=10x """.trimIndent() - // Execute SUT setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - val highlights = myFixture.doHighlighting() - - // Verification - assertSize(4, highlights) + assertSize(3, myFixture.doHighlighting()) } } From 62ece03e23237d88a9a719447aafc3c11dfd598c Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Fri, 1 May 2026 08:32:06 -0700 Subject: [PATCH 5/5] fix: tighten dns_name and ifname validators to match C parser more closely dns_name regex now rejects ASCII control characters (U+0000-U+001F, U+007F) that dns_label_unescape rejects via the (uint8_t)*n >= ' ' check. ifname regex now rejects 0x-prefixed hex strings, which safe_atoi32 (called by parse_ifindex via base 0 strtol) treats as ifindex. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../optionvalues/ai/ConfigParseDnsNameOptionValue.kt | 4 ++-- .../optionvalues/ai/ConfigParseIfnameOptionValue.kt | 4 ++-- .../inspections/ai/ConfigParseIfnameOptionValueTest.kt | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt index d7e41a2..41a1ea2 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDnsNameOptionValue.kt @@ -22,8 +22,8 @@ class ConfigParseDnsNameOptionValue : SimpleGrammarOptionValues( "config_parse_dns_name", SequenceCombinator( RegexTerminal( - "[^.\\\\]+(?:\\.[^.\\\\]+)*\\.?", - "[^.\\\\]+(?:\\.[^.\\\\]+)*\\.?" + "[^.\\\\\\p{Cntrl}]+(?:\\.[^.\\\\\\p{Cntrl}]+)*\\.?", + "[^.\\\\\\p{Cntrl}]+(?:\\.[^.\\\\\\p{Cntrl}]+)*\\.?" ), EOF() ) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt index 27d4a6c..4953504 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseIfnameOptionValue.kt @@ -19,8 +19,8 @@ class ConfigParseIfnameOptionValue : SimpleGrammarOptionValues( "config_parse_ifname", SequenceCombinator( RegexTerminal( - "(?!(?:all|default|\\.{1,2}|[0-9]+)\\Z)[^\\s:/%]{1,15}", - "(?!(?:all|default|\\.{1,2}|[0-9]+)\\Z)[^\\s:/%]{1,15}" + "(?!(?:all|default|\\.{1,2}|0[xX][0-9a-fA-F]+|[0-9]+)\\Z)[^\\s:/%]{1,15}", + "(?!(?:all|default|\\.{1,2}|0[xX][0-9a-fA-F]+|[0-9]+)\\Z)[^\\s:/%]{1,15}" ), EOF() ) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt index 2338397..df56bc6 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseIfnameOptionValueTest.kt @@ -36,10 +36,11 @@ class ConfigParseIfnameOptionValueTest : AbstractUnitFileTest() { Bridge=all Bridge=default Bridge=1234 + Bridge=0x10 """.trimIndent() setupFileInEditor("file.network", file) enableInspection(InvalidValueInspection::class.java) - assertSize(7, myFixture.doHighlighting()) + assertSize(8, myFixture.doHighlighting()) } }