From 3f843b6e4eb77688754b62c05c14824a8315f4d7 Mon Sep 17 00:00:00 2001 From: Steve Ramage Date: Sat, 23 May 2026 10:27:08 -0700 Subject: [PATCH] feat: add 6 validators - cpu_quota, htb_class_size, delegate, syscall_errno, address_families, pass_environ (Resolves #446) Batch 2/5 of the burn-down extension. Each grammar mirrors the systemd C parser: - config_parse_cpu_quota: parse_permyriad_unbounded, percent form unbounded (200% valid) - config_parse_htb_class_size (TCLASS_KIND_HTB): IEC byte size, same shape as tbf_size/fq_size - config_parse_delegate: boolean OR whitespace-separated cgroup controller names - config_parse_syscall_errno: "kill" OR errno name (E*) OR integer - config_parse_address_families: "none" OR optional ~ + list of AF_* names - config_parse_pass_environ: list of env var names (specifier-aware) Note for future validators: when an Alternative contains both a FlexibleLiteralChoiceTerminal (like BOOLEAN) and a more specific structured branch, put the structured branch first. FlexibleLiteralChoice's syntactic phase accepts any short alphanumeric token in its character class, so a single-token input like "cpu" would syntactically pass BOOLEAN and AlternativeCombinator would short-circuit before reaching the structured branch. OptionValueTest missing-function count drops 394 -> 388; found-key count rises 1780 -> 1812. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../semanticdata/optionvalues/AiGenerated.kt | 6 +++ .../ConfigParseAddressFamiliesOptionValue.kt | 35 +++++++++++++ .../ai/ConfigParseCpuQuotaOptionValue.kt | 25 ++++++++++ .../ai/ConfigParseDelegateOptionValue.kt | 41 +++++++++++++++ .../ai/ConfigParseHtbClassSizeOptionValue.kt | 24 +++++++++ .../ai/ConfigParsePassEnvironOptionValue.kt | 29 +++++++++++ .../ai/ConfigParseSyscallErrnoOptionValue.kt | 29 +++++++++++ ...nfigParseAddressFamiliesOptionValueTest.kt | 48 ++++++++++++++++++ .../ai/ConfigParseCpuQuotaOptionValueTest.kt | 49 ++++++++++++++++++ .../ai/ConfigParseDelegateOptionValueTest.kt | 49 ++++++++++++++++++ .../ConfigParseHtbClassSizeOptionValueTest.kt | 45 +++++++++++++++++ .../ConfigParsePassEnvironOptionValueTest.kt | 48 ++++++++++++++++++ .../ConfigParseSyscallErrnoOptionValueTest.kt | 50 +++++++++++++++++++ 13 files changed, 478 insertions(+) create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseAddressFamiliesOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCpuQuotaOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDelegateOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseHtbClassSizeOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParsePassEnvironOptionValue.kt create mode 100644 src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSyscallErrnoOptionValue.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseAddressFamiliesOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCpuQuotaOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDelegateOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseHtbClassSizeOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParsePassEnvironOptionValueTest.kt create mode 100644 src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSyscallErrnoOptionValueTest.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 6f5b38d..20538c8 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 @@ -12,6 +12,7 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_6rd_prefix", "0") to ConfigParse6rdPrefixOptionValue() as OptionValueInformation, Validator("config_parse_ad_actor_sys_prio", "0") to ConfigParseAdActorSysPrioOptionValue() as OptionValueInformation, Validator("config_parse_ad_user_port_key", "0") to ConfigParseAdUserPortKeyOptionValue() as OptionValueInformation, + Validator("config_parse_address_families", "0") to ConfigParseAddressFamiliesOptionValue() as OptionValueInformation, Validator("config_parse_address_section", "ADDRESS_ADD_PREFIX_ROUTE") to ConfigParseAddressSectionOptionValue() as OptionValueInformation, Validator("config_parse_address_section", "ADDRESS_AUTO_JOIN") to ConfigParseAddressSectionOptionValue() as OptionValueInformation, Validator("config_parse_address_section", "ADDRESS_DAD") to ConfigParseAddressSectionOptionValue() as OptionValueInformation, @@ -54,7 +55,9 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_codel_bool", "QDISC_KIND_CODEL") to ConfigParseCodelBoolOptionValue() as OptionValueInformation, Validator("config_parse_codel_u32", "QDISC_KIND_CODEL") to ConfigParseCodelU32OptionValue() as OptionValueInformation, Validator("config_parse_collect_mode", "0") to ConfigParseCollectModeOptionValue() as OptionValueInformation, + Validator("config_parse_cpu_quota", "0") to ConfigParseCpuQuotaOptionValue() as OptionValueInformation, Validator("config_parse_cpuset_partition", "0") to ConfigParseCpusetPartitionOptionValue() as OptionValueInformation, + Validator("config_parse_delegate", "0") to ConfigParseDelegateOptionValue() as OptionValueInformation, Validator("config_parse_df", "0") to ConfigParseDfOptionValue() as OptionValueInformation, Validator("config_parse_dhcp6_client_start_mode", "0") to ConfigParseDhcp6ClientStartModeOptionValue() as OptionValueInformation, Validator("config_parse_dhcp6_pd_prefix_hint", "0") to ConfigParseDhcp6PdPrefixHintOptionValue() as OptionValueInformation, @@ -96,6 +99,7 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_geneve_ttl", "0") to ConfigParseGeneveTtlOptionValue() as OptionValueInformation, Validator("config_parse_geneve_vni", "0") to ConfigParseGeneveVniOptionValue() as OptionValueInformation, Validator("config_parse_hhf_packet_limit", "QDISC_KIND_HHF") to ConfigParseHhfPacketLimitOptionValue() as OptionValueInformation, + Validator("config_parse_htb_class_size", "TCLASS_KIND_HTB") to ConfigParseHtbClassSizeOptionValue() as OptionValueInformation, Validator("config_parse_iaid", "AF_INET6") to ConfigParseIaidOptionValue() as OptionValueInformation, Validator("config_parse_iaid", "AF_INET") to ConfigParseIaidOptionValue() as OptionValueInformation, Validator("config_parse_in_addr_non_null", "AF_INET") to ConfigParseInAddrNonNullOptionValue() as OptionValueInformation, @@ -138,6 +142,7 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_nexthop_section", "NEXTHOP_ID") to ConfigParseNexthopSectionOptionValue() as OptionValueInformation, Validator("config_parse_nexthop_section", "NEXTHOP_ONLINK") to ConfigParseNexthopSectionOptionValue() as OptionValueInformation, Validator("config_parse_nsec", "0") to ConfigParseNsecOptionValue() as OptionValueInformation, + Validator("config_parse_pass_environ", "0") to ConfigParsePassEnvironOptionValue() as OptionValueInformation, Validator("config_parse_permille", "0") to ConfigParsePermilleOptionValue() as OptionValueInformation, Validator("config_parse_pfifo_size", "QDISC_KIND_PFIFO") to ConfigParsePfifoSizeOptionValue() as OptionValueInformation, Validator("config_parse_pfifo_size", "QDISC_KIND_PFIFO_HEAD_DROP") to ConfigParsePfifoSizeOptionValue() as OptionValueInformation, @@ -175,6 +180,7 @@ fun getAllAIGeneratedValidators(): Map { Validator("config_parse_sr_iov_uint32", "0") to ConfigParseSrIovUint32OptionValue() 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_syscall_errno", "0") to ConfigParseSyscallErrnoOptionValue() as OptionValueInformation, Validator("config_parse_tasks_max", "0") to ConfigParseTasksMaxOptionValue() as OptionValueInformation, Validator("config_parse_tbf_size", "QDISC_KIND_TBF") to ConfigParseTbfSizeOptionValue() as OptionValueInformation, Validator("config_parse_tcp_window", "0") to ConfigParseTcpWindowOptionValue() as OptionValueInformation, diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseAddressFamiliesOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseAddressFamiliesOptionValue.kt new file mode 100644 index 0000000..186890f --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseAddressFamiliesOptionValue.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.* + +/** + * Validator for RestrictAddressFamilies=. + * + * C function: config_parse_address_families in src/core/load-fragment.c → parse_address_families + * in src/shared/parse-helpers.c:90. Accepts: + * - "none" (clears the set, sets allowlist) + * - optional leading "~" (invert / denylist mode), followed by a whitespace-separated list + * of address family names from af_from_name (AF_UNIX, AF_INET, AF_INET6, AF_NETLINK, + * AF_PACKET, …) + * + * The grammar matches the "AF_" prefix loosely (any uppercase/digit/underscore tail); unknown + * names slip past the grammar but fail at runtime. This is the same tradeoff as syscall_errno. + */ +class ConfigParseAddressFamiliesOptionValue : SimpleGrammarOptionValues( + "config_parse_address_families", + SequenceCombinator( + AlternativeCombinator( + LiteralChoiceTerminal("none"), + SequenceCombinator( + ZeroOrOne(LiteralChoiceTerminal("~")), + RegexTerminal("AF_[A-Z0-9_]+", "AF_[A-Z0-9_]+"), + ZeroOrMore(SequenceCombinator( + WhitespaceTerminal(), + RegexTerminal("AF_[A-Z0-9_]+", "AF_[A-Z0-9_]+") + )) + ) + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCpuQuotaOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCpuQuotaOptionValue.kt new file mode 100644 index 0000000..e8d25ab --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseCpuQuotaOptionValue.kt @@ -0,0 +1,25 @@ +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 CPUQuota=. + * + * C function: config_parse_cpu_quota in src/core/load-fragment.c. After the empty-input check + * it calls parse_permyriad_unbounded(rvalue), which accepts N% / N.N% / N.NN% (with no upper + * bound — values over 100% are meaningful here, e.g. "200%" means 2 cores). The result must + * be strictly greater than zero, so "0%", "0.0%", "0.00%" are rejected at the C semantic + * layer; the grammar accepts them but the runtime parser will warn. + * + * Only the ASCII percent form is matched here; the ‰/‱ Unicode suffixes are vanishingly rare + * in unit files. + */ +class ConfigParseCpuQuotaOptionValue : SimpleGrammarOptionValues( + "config_parse_cpu_quota", + SequenceCombinator( + RegexTerminal("[0-9]+(\\.[0-9]{1,2})?", "[0-9]+(\\.[0-9]{1,2})?"), + LiteralChoiceTerminal("%"), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDelegateOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDelegateOptionValue.kt new file mode 100644 index 0000000..ce1db4f --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseDelegateOptionValue.kt @@ -0,0 +1,41 @@ +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 Delegate=. + * + * C function: config_parse_delegate in src/core/load-fragment.c. Either a boolean (toggles + * delegation for all controllers) or a whitespace-separated list of cgroup controller names + * from cgroup_controller_table in src/basic/cgroup-util.c. + * + * Note: the controller list is the first alternative because BOOLEAN is a + * FlexibleLiteralChoiceTerminal whose syntactic phase accepts any short alphanumeric token — + * if it ran first, single-controller inputs like "cpu" would syntactically consume the value + * as a (fake) boolean and AlternativeCombinator would short-circuit there. + */ +class ConfigParseDelegateOptionValue : SimpleGrammarOptionValues( + "config_parse_delegate", + SequenceCombinator( + AlternativeCombinator( + SequenceCombinator( + LiteralChoiceTerminal( + "cpu", "cpuacct", "cpuset", "io", "blkio", "memory", "devices", "pids", + "bpf-firewall", "bpf-devices", "bpf-foreign", "bpf-socket-bind", + "bpf-restrict-network-interfaces", "bpf-bind-network-interface" + ), + ZeroOrMore(SequenceCombinator( + WhitespaceTerminal(), + LiteralChoiceTerminal( + "cpu", "cpuacct", "cpuset", "io", "blkio", "memory", "devices", "pids", + "bpf-firewall", "bpf-devices", "bpf-foreign", "bpf-socket-bind", + "bpf-restrict-network-interfaces", "bpf-bind-network-interface" + ) + )) + ), + BOOLEAN + ), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseHtbClassSizeOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseHtbClassSizeOptionValue.kt new file mode 100644 index 0000000..5e4a561 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseHtbClassSizeOptionValue.kt @@ -0,0 +1,24 @@ +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 the HTB class size-style options: QuantumBytes=, MTUBytes=, OverheadBytes=, + * BufferBytes=, CeilBufferBytes=. + * + * C function: config_parse_htb_class_size(TCLASS_KIND_HTB) in src/network/tc/htb.c. After + * branching on the lvalue, it calls parse_size(rvalue, 1024, &v), so the value is a decimal + * byte count optionally suffixed with an IEC unit. Same shape as config_parse_tbf_size and + * config_parse_fq_size. + * + * The per-lvalue OverheadBytes <= UINT16_MAX bound and the general v <= UINT32_MAX bound are + * semantic checks that can't be expressed at the grammar level without lvalue access. + */ +class ConfigParseHtbClassSizeOptionValue : SimpleGrammarOptionValues( + "config_parse_htb_class_size", + SequenceCombinator( + OptionalWhitespacePrefix(BYTES), + EOF() + ) +) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParsePassEnvironOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParsePassEnvironOptionValue.kt new file mode 100644 index 0000000..0e5142b --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParsePassEnvironOptionValue.kt @@ -0,0 +1,29 @@ +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 PassEnvironment=. + * + * C function: config_parse_pass_environ in src/core/load-fragment.c. Whitespace-separated list + * of environment variable names. Each word is first run through unit_env_printf (resolves + * %p / %n / etc.), then env_name_is_valid: must not be empty, must not start with a digit, + * and may contain only characters in VALID_BASH_ENV_NAME_CHARS (alphanumerics and underscore). + * + * The grammar tolerates "%X" specifiers inline by including "%" in the allowed-character set, + * since the C parser resolves them before the validity check. This means a few unresolved + * specifier-heavy inputs that the runtime parser would reject will pass here — preferred over + * false positives for legitimate `PassEnvironment=%n_LOG` style usage. + */ +class ConfigParsePassEnvironOptionValue : SimpleGrammarOptionValues( + "config_parse_pass_environ", + 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/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSyscallErrnoOptionValue.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSyscallErrnoOptionValue.kt new file mode 100644 index 0000000..ebde9b3 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/ai/ConfigParseSyscallErrnoOptionValue.kt @@ -0,0 +1,29 @@ +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 SystemCallErrorNumber=. + * + * C function: config_parse_syscall_errno in src/core/load-fragment.c → parse_errno in + * src/basic/parse-util.c. Accepts: + * - "kill" (special, resets to SECCOMP_ERROR_NUMBER_KILL) + * - an errno name (uppercase "E"-prefixed, e.g. EPERM, ENOENT) via errno_from_name + * - an integer that maps to a valid errno via errno_is_valid (or 0) + * + * The grammar matches the errno-name pattern loosely (any uppercase E-prefixed token); + * unknown names like "ENOIENT" would slip past the grammar but fail at runtime. Numeric + * range here is 0..4095 — wider than typical valid errnos but enough to catch obvious typos. + */ +class ConfigParseSyscallErrnoOptionValue : SimpleGrammarOptionValues( + "config_parse_syscall_errno", + SequenceCombinator( + AlternativeCombinator( + LiteralChoiceTerminal("kill"), + RegexTerminal("E[A-Z][A-Z0-9]*", "E[A-Z][A-Z0-9]*"), + IntegerTerminal(0, 4096) + ), + EOF() + ) +) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseAddressFamiliesOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseAddressFamiliesOptionValueTest.kt new file mode 100644 index 0000000..8d37381 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseAddressFamiliesOptionValueTest.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 ConfigParseAddressFamiliesOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + RestrictAddressFamilies=none + RestrictAddressFamilies=AF_INET + RestrictAddressFamilies=AF_INET AF_INET6 + RestrictAddressFamilies=AF_UNIX AF_NETLINK + RestrictAddressFamilies=~AF_PACKET + RestrictAddressFamilies=~AF_INET AF_INET6 + RestrictAddressFamilies=AF_BRIDGE AF_X25 AF_AX25 + """.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] + RestrictAddressFamilies=inet + RestrictAddressFamilies=AF_inet + RestrictAddressFamilies=AF_INET, AF_INET6 + RestrictAddressFamilies=~ AF_PACKET + RestrictAddressFamilies=NONE + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(5, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCpuQuotaOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCpuQuotaOptionValueTest.kt new file mode 100644 index 0000000..fab7c8c --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseCpuQuotaOptionValueTest.kt @@ -0,0 +1,49 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest +import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection +import org.junit.Test + +class ConfigParseCpuQuotaOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + CPUQuota=50% + CPUQuota=100% + CPUQuota=200% + CPUQuota=12.34% + CPUQuota=1.5% + CPUQuota=0.01% + CPUQuota=99.99% + CPUQuota=1000% + """.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] + CPUQuota=50 + CPUQuota=abc + CPUQuota=-5% + CPUQuota=12.345% + CPUQuota=50% 60% + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(5, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDelegateOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDelegateOptionValueTest.kt new file mode 100644 index 0000000..74f18a0 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseDelegateOptionValueTest.kt @@ -0,0 +1,49 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.inspections.ai + +import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest +import net.sjrx.intellij.plugins.systemdunitfiles.inspections.InvalidValueInspection +import org.junit.Test + +class ConfigParseDelegateOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + Delegate=yes + Delegate=no + Delegate=true + Delegate=false + Delegate=cpu + Delegate=cpu memory io + Delegate=cpu cpuacct cpuset io blkio memory devices pids + Delegate=bpf-firewall bpf-devices bpf-foreign + Delegate=bpf-socket-bind bpf-restrict-network-interfaces bpf-bind-network-interface + """.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] + Delegate=maybe + Delegate=cpu unknown + Delegate=CPU + Delegate=cpu,memory + """.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/ConfigParseHtbClassSizeOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseHtbClassSizeOptionValueTest.kt new file mode 100644 index 0000000..b07c523 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseHtbClassSizeOptionValueTest.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 ConfigParseHtbClassSizeOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [HierarchyTokenBucketClass] + QuantumBytes=1500 + MTUBytes=1500 + OverheadBytes=64 + BufferBytes=8K + CeilBufferBytes=16K + BufferBytes=1M + """.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 = """ + [HierarchyTokenBucketClass] + QuantumBytes=abc + MTUBytes=-1 + BufferBytes=8X + """.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/ConfigParsePassEnvironOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParsePassEnvironOptionValueTest.kt new file mode 100644 index 0000000..823d1c1 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParsePassEnvironOptionValueTest.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 ConfigParsePassEnvironOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + PassEnvironment=PATH + PassEnvironment=HOME LANG TERM + PassEnvironment=MY_VAR + PassEnvironment=_LEADING_UNDERSCORE + PassEnvironment=A B C D E + PassEnvironment=%n_LOG_LEVEL + PassEnvironment=PREFIX_%i + """.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] + PassEnvironment=1STARTS_WITH_DIGIT + PassEnvironment=HAS-DASH + PassEnvironment=HAS.DOT + PassEnvironment=NAME=value + PassEnvironment=PATH,HOME + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(5, highlights) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSyscallErrnoOptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSyscallErrnoOptionValueTest.kt new file mode 100644 index 0000000..dd93c81 --- /dev/null +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ai/ConfigParseSyscallErrnoOptionValueTest.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 ConfigParseSyscallErrnoOptionValueTest : AbstractUnitFileTest() { + + @Test + fun testValidValues() { + // language="unit file (systemd)" + val file = """ + [Service] + SystemCallErrorNumber=kill + SystemCallErrorNumber=EPERM + SystemCallErrorNumber=ENOENT + SystemCallErrorNumber=EACCES + SystemCallErrorNumber=EAGAIN + SystemCallErrorNumber=1 + SystemCallErrorNumber=13 + SystemCallErrorNumber=0 + """.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] + SystemCallErrorNumber=KILL + SystemCallErrorNumber=eperm + SystemCallErrorNumber=PERM + SystemCallErrorNumber=-1 + SystemCallErrorNumber=99999 + SystemCallErrorNumber=EPERM ENOENT + """.trimIndent() + + setupFileInEditor("file.service", file) + enableInspection(InvalidValueInspection::class.java) + val highlights = myFixture.doHighlighting() + + assertSize(6, highlights) + } +}