diff --git a/buildSrc/src/main/groovy/GenerateDataFromManPages.groovy b/buildSrc/src/main/groovy/GenerateDataFromManPages.groovy index 4a59a592..badffceb 100644 --- a/buildSrc/src/main/groovy/GenerateDataFromManPages.groovy +++ b/buildSrc/src/main/groovy/GenerateDataFromManPages.groovy @@ -5,13 +5,17 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.Internal import org.w3c.dom.Document +import org.w3c.dom.Element import org.w3c.dom.Node import org.w3c.dom.NodeList import org.w3c.dom.ls.DOMImplementationLS +import org.w3c.dom.ls.LSParser import org.w3c.dom.ls.LSSerializer +import org.xml.sax.InputSource import javax.xml.parsers.DocumentBuilder import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.transform.OutputKeys import javax.xml.transform.Transformer import javax.xml.transform.TransformerFactory import javax.xml.transform.dom.DOMSource @@ -20,6 +24,7 @@ import javax.xml.transform.stream.StreamSource import javax.xml.xpath.XPath import javax.xml.xpath.XPathConstants import javax.xml.xpath.XPathFactory +import java.util.concurrent.ConcurrentHashMap import java.util.regex.Matcher /** @@ -129,8 +134,116 @@ class GenerateDataFromManPages extends DefaultTask { '[Files] Section Options' : ['Files'], '[Network] Section Options': ['Network'], ] - ]] - + ]], + //cat systemd.netdev.xml | grep "Section Options" | sed -e "s///g" | sed -e 's###g' | sed -E "s/\[(.+)] Section Options/'\0': ['\1'],/" + 'netdev': [ + 'systemd.netdev.xml': + ['sections': + [ + '[Match] Section Options': ['Match'], + '[NetDev] Section Options': ['NetDev'], + '[Bridge] Section Options': ['Bridge'], + '[VLAN] Section Options': ['VLAN'], + '[MACVLAN] Section Options': ['MACVLAN'], + '[MACVTAP] Section Options': ['MACVTAP'], + '[IPVLAN] Section Options': ['IPVLAN'], + '[IPVTAP] Section Options': ['IPVTAP'], + '[VXLAN] Section Options': ['VXLAN'], + '[GENEVE] Section Options': ['GENEVE'], + '[BareUDP] Section Options': ['BareUDP'], + '[L2TP] Section Options': ['L2TP'], + '[L2TPSession] Section Options': ['L2TPSession'], + '[MACsec] Section Options': ['MACsec'], + '[MACsecReceiveChannel] Section Options': ['MACsecReceiveChannel'], + '[MACsecTransmitAssociation] Section Options': ['MACsecTransmitAssociation'], + '[MACsecReceiveAssociation] Section Options': ['MACsecReceiveAssociation'], + '[Tunnel] Section Options': ['Tunnel'], + '[FooOverUDP] Section Options': ['FooOverUDP'], + '[Peer] Section Options': ['Peer'], + '[VXCAN] Section Options': ['VXCAN'], + '[Tun] Section Options': ['Tun'], + '[Tap] Section Options': ['Tap'], + '[WireGuard] Section Options': ['WireGuard'], + '[WireGuardPeer] Section Options': ['WireGuardPeer'], + '[Bond] Section Options': ['Bond'], + '[Xfrm] Section Options': ['Xfrm'], + '[VRF] Section Options': ['VRF'], + '[BatmanAdvanced] Section Options': ['BatmanAdvanced'], + '[IPoIB] Section Options': ['IPoIB'], + '[WLAN] Section Options': ['WLAN'], + ] + ] + ], + 'network': [ + 'systemd.network.xml': + ['sections': + [ + '[Match] Section Options' : ['Match'], + '[Link] Section Options' : ['Link'], + '[SR-IOV] Section Options' : ['SR-IOV'], + '[Network] Section Options' : ['Network'], + '[Address] Section Options' : ['Address'], + '[Neighbor] Section Options': ['Neighbor'], + '[IPv6AddressLabel] Section Options': ['IPv6AddressLabel'], + '[RoutingPolicyRule] Section Options': ['RoutingPolicyRule'], + '[NextHop] Section Options': ['NextHop'], + '[Route] Section Options': ['Route'], + '[DHCPv4] Section Options': ['DHCPv4'], + '[DHCPv6] Section Options': ['DHCPv6'], + '[DHCPPrefixDelegation] Section Options': ['DHCPPrefixDelegation'], + '[IPv6AcceptRA] Section Options': ['IPv6AcceptRA'], + '[DHCPServer] Section Options': ['DHCPServer'], + '[DHCPServerStaticLease] Section Options': ['DHCPServerStaticLease'], + '[IPv6SendRA] Section Options': ['IPv6SendRA'], + '[IPv6Prefix] Section Options': ['IPv6Prefix'], + '[IPv6RoutePrefix] Section Options': ['IPv6RoutePrefix'], + '[IPv6PREF64Prefix] Section Options': ['IPv6PREF64Prefix'], + '[Bridge] Section Options': ['Bridge'], + '[BridgeFDB] Section Options': ['BridgeFDB'], + '[LLDP] Section Options': ['LLDP'], + '[CAN] Section Options': ['CAN'], + '[IPoIB] Section Options': ['IPoIB'], + '[QDisc] Section Options': ['QDisc'], + '[NetworkEmulator] Section Options': ['NetworkEmulator'], + '[TokenBucketFilter] Section Options': ['TokenBucketFilter'], + '[PIE] Section Options': ['PIE'], + '[FlowQueuePIE] Section Options': ['FlowQueuePIE'], + '[StockchasticFairBlue] Section Options': ['StochasticFairBlue'], + '[StockchasticFairnessQueueing] Section Options': ['StochasticFairnessQueueing'], + '[BFIFO] Section Options': ['BFIFO'], + '[PFIFO] Section Options': ['PFIFO'], + '[PFIFOHeadDrop] Section Options': ['PFIFOHeadDrop'], + '[PFIFOFast] Section Options': ['PFIFOFast'], + '[CAKE] Section Options': ['CAKE'], + '[ControlledDelay] Section Options': ['ControlledDelay'], + '[DeficitRoundRobinScheduler] Section Options': ['DeficitRoundRobinScheduler'], + '[DeficitRoundRobinSchedulerClass] Section Options': ['DeficitRoundRobinSchedulerClass'], + '[EnhancedTransmissionSelection] Section Options': ['EnhancedTransmissionSelection'], + '[GenericRandomEarlyDetection] Section Options': ['GenericRandomEarlyDetection'], + '[FairQueueingControlledDelay] Section Options': ['FairQueueingControlledDelay'], + '[FairQueueing] Section Options': ['FairQueueing'], + '[TrivialLinkEqualizer] Section Options': ['TrivialLinkEqualizer'], + '[HierarchyTokenBucket] Section Options': ['HierarchyTokenBucket'], + '[HierarchyTokenBucketClass] Section Options': ['HierarchyTokenBucketClass'], + '[ClassfulMultiQueueing] Section Options': ['ClassfulMultiQueueing'], + '[BandMultiQueueing] Section Options': ['BandMultiQueueing'], + '[HeavyHitterFilter] Section Options': ['HeavyHitterFilter'], + '[QuickFairQueueing] Section Options': ['QuickFairQueueing'], + '[QuickFairQueueingClass] Section Options': ['QuickFairQueueingClass'], + '[BridgeVLAN] Section Options': ['BridgeVLAN'], + + ] + ] + ], + 'link':[ + 'systemd.link.xml': + ['sections': [ + '[Match] Section Options': ['Match'], + '[Link] Section Options': ['Link'], + '[SR-IOV] Section Options': ['SR-IOV'], + ] + ], + ] ] @Internal @@ -142,18 +255,6 @@ class GenerateDataFromManPages extends DefaultTask { @Internal final DocumentBuilderFactory dbf - public GenerateDataFromManPages() { - xpath = XPathFactory.newInstance().newXPath() - - dbf = DocumentBuilderFactory.newInstance() - dbf.setValidating(false) - dbf.setExpandEntityReferences(false) - dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false) - dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) - dbf.setFeature("http://xml.org/sax/features/external-general-entities", false) - dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false) - } - @TaskAction void start() { @@ -209,9 +310,9 @@ class GenerateDataFromManPages extends DefaultTask { String filename = file.getName() - def builder = dbf.newDocumentBuilder() + Document document = buildDocumentProcessingIncludes(file) - def records = builder.parse(file).documentElement + def records = document.documentElement /* We technically should be looking for variablelist element with class 'unit-directives' however some sections in @@ -229,15 +330,26 @@ class GenerateDataFromManPages extends DefaultTask { result = (NodeList)xpath.evaluate( "//variablelist[(contains(@class,'nspawn-directives'))]/varlistentry", records, XPathConstants.NODESET); - } - else { + } else if (file.getAbsolutePath().endsWith("systemd.netdev.xml")) { + result = (NodeList)xpath.evaluate( + "//variablelist[(contains(@class,'network-directives'))]/varlistentry", + records, XPathConstants.NODESET); + } else if (file.getAbsolutePath().endsWith("systemd.network.xml")) { + result = (NodeList)xpath.evaluate( + "//variablelist[(contains(@class,'network-directives'))]/varlistentry", + records, XPathConstants.NODESET); + } else if (file.getAbsolutePath().endsWith("systemd.link.xml")) { + result = (NodeList)xpath.evaluate( + "//variablelist[(contains(@class,'network-directives'))]/varlistentry", + records, XPathConstants.NODESET); + } else { result = (NodeList)xpath.evaluate( "//variablelist[(contains(@class,'unit-directives'))]/varlistentry", records, XPathConstants.NODESET); } if (result.getLength() == 0) { - throw new IllegalStateException("Could not find variables under $filename") + throw new IllegalStateException("Could not find variables under $filename, this file type isn't handled") } @@ -279,7 +391,7 @@ class GenerateDataFromManPages extends DefaultTask { } private static List getOptionNameAndValue(String option, String filename) { - Matcher match = (option =~ /(\w+)=(.*)/) + Matcher match = (option =~ /^([a-zA-Z0-9_-]+)=(.*)/) match.find() if (match.groupCount() != 2) { @@ -293,6 +405,21 @@ class GenerateDataFromManPages extends DefaultTask { [name, value] } + public GenerateDataFromManPages() { + xpath = XPathFactory.newInstance().newXPath() + + dbf = DocumentBuilderFactory.newInstance() + dbf.setXIncludeAware(true) + //dbf.setNamespaceAware(true) + dbf.setValidating(false) + dbf.setExpandEntityReferences(false) + + dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false) + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + dbf.setFeature("http://xml.org/sax/features/external-general-entities", false) + dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false) + } + /** * Generates individual HTML files for use as inline documentation * @@ -302,8 +429,9 @@ class GenerateDataFromManPages extends DefaultTask { * @return */ private generateDocumentationHtmlFromManPages(String fileType, File sourceFile) { - DocumentBuilder builder = dbf.newDocumentBuilder() - Document document = builder.parse(sourceFile) + + + Document document = buildDocumentProcessingIncludes(sourceFile) Transformer transformer = getXsltTransformer() String xsltOutput = transformDocument(document, transformer) @@ -311,6 +439,134 @@ class GenerateDataFromManPages extends DefaultTask { segmentParametersIntoFiles(fileType, sourceFile.getName(), xsltOutput) } + private Document buildDocumentProcessingIncludes(File sourceFile) { + DocumentBuilder builder = dbf.newDocumentBuilder() + String xmlContent = sourceFile.text + + + // I spent an hour with ChatGPT trying to get + // Xincludes to work properly (without spending 6 hours to understand them). + // Couldn't get it to work, due to Java not liking things like includes with no href, for local references. + // 💣 Step 1: Replace XInclude elements using regex + xmlContent = processXIncludesWithRegex(xmlContent, sourceFile.parentFile) + + + File outputDir = project.layout.buildDirectory.dir("tmp/rendered-xincludes").get().asFile + if (!outputDir.exists()) { + outputDir.mkdirs() + } + + File outputFile = new File(outputDir, sourceFile.getName()) + outputFile.text = xmlContent // Save to file + + Document document = builder.parse(outputFile) + document + } + + private String processXIncludesWithRegex(String xmlContent, File baseDir) { + // 🔥 Regex to match (xpointer is optional) + def includePattern = // + + return xmlContent.replaceAll(includePattern) { match, href, xpointer -> + File includedFile = new File(baseDir, href) + + if (!includedFile.exists()) { + println "⚠️ WARNING: Included file '${includedFile.absolutePath}' not found!" + return "" + } + + + // ✅ Load XML properly instead of using regex + String xptr = xpointer + String includedContent = GenerateDataFromManPages.extractElementById(includedFile, xptr) + + return includedContent ?: "" + + } + } + + // 🔥 Static cache for storing extracted XML elements + private static final Map> fileCache = new ConcurrentHashMap<>() + + static String extractElementById(File xmlFile, String elementId) { + // ✅ Check if the entire file has already been cached + String filePath = xmlFile.getAbsolutePath() + if (!fileCache.containsKey(filePath)) { + // 🚀 Populate the cache for this file + cacheAllElements(xmlFile) + } + + // ✅ Retrieve element from cache + Map cachedElements = fileCache.get(filePath) + + if (elementId == null) { + elementId = DOCUMENT_CACHE_KEY + } + + if (cachedElements.containsKey(elementId)) { + return cachedElements.get(elementId) + } else { + println "⚠️ WARNING: Element with id='$elementId' not found in ${xmlFile.name}" + return null + } + } + + private static final String DOCUMENT_CACHE_KEY = "[[[ROOT_ELEMENT]]]" + + private static void cacheAllElements(File xmlFile) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance() + factory.setNamespaceAware(true) // Needed for XML ID lookup + DocumentBuilder builder = factory.newDocumentBuilder() + + Document document = builder.parse(new InputSource(xmlFile.newReader())) + document.getDocumentElement().normalize() + + // ✅ Create a new cache for this file + Map elementCache = new ConcurrentHashMap<>() + + String rootDoc = nodeToString(document.documentElement) + elementCache.put(DOCUMENT_CACHE_KEY, rootDoc) + // 🚀 Find all elements with an `id` attribute and store them in cache + NodeList elements = document.getElementsByTagName("*") + for (int i = 0; i < elements.length; i++) { + Element element = elements.item(i) + if (element.hasAttribute("id")) { + String elementId = element.getAttribute("id") + String extractedXml = nodeToString(element) + + String wrappedXml = "" + + extractedXml + + "" + + elementCache.put(elementId, wrappedXml) + } + } + + // ✅ Store parsed elements in the file cache + fileCache.put(xmlFile.getAbsolutePath(), elementCache) + + println "✅ Cached ${elementCache.size()} elements from ${xmlFile.name}" + + } catch (Exception e) { + println "❌ ERROR: Failed to parse ${xmlFile.name}: ${e.message}" + } + } + + private static String nodeToString(Node node) { + try { + TransformerFactory transformerFactory = TransformerFactory.newInstance() + Transformer transformer = transformerFactory.newTransformer() + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes") + StringWriter writer = new StringWriter() + transformer.transform(new DOMSource(node), new StreamResult(writer)) + return writer.toString() + } catch (Exception e) { + println "❌ ERROR: Failed to convert node to string: ${e.message}" + return "" + } + } + /** * Transforms the supplied document with the supplied transformer * @param document - XML Document to transform @@ -386,7 +642,7 @@ class GenerateDataFromManPages extends DefaultTask { NodeList paragraphList = (NodeList)xpath.evaluate("description/paragraph", parameterNode, XPathConstants.NODESET) - Matcher match = (variableName =~ /(\w+)=(.*)/) + Matcher match = (variableName =~ /([^=]+)=(.*)/) match.find() if (match.groupCount() != 2) { diff --git a/buildSrc/src/main/resources/resolve-xinclude.xslt b/buildSrc/src/main/resources/resolve-xinclude.xslt new file mode 100644 index 00000000..1ece24cc --- /dev/null +++ b/buildSrc/src/main/resources/resolve-xinclude.xslt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/UnitFileIcon.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/UnitFileIcon.kt index 676879a6..134dc016 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/UnitFileIcon.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/UnitFileIcon.kt @@ -17,4 +17,7 @@ object UnitFileIcon { val SWAP = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/swap.svg", UnitFileIcon::class.java) val TARGET = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/target.svg", UnitFileIcon::class.java) val TIMER = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/timer.svg", UnitFileIcon::class.java) + val LINK = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/link.svg", UnitFileIcon::class.java) + val NETDEV = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/netdev.svg", UnitFileIcon::class.java) + val NETWORK = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/network.svg", UnitFileIcon::class.java) } diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/LinkFileType.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/LinkFileType.kt new file mode 100644 index 00000000..6f5154ee --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/LinkFileType.kt @@ -0,0 +1,32 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.filetypes + +import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileIcon +import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileLanguage +import org.jetbrains.annotations.Nls +import javax.swing.Icon + +class LinkFileType private constructor() : AbstractUnitFileType(UnitFileLanguage.INSTANCE) { + override fun getName(): String { + return "Network Device Configuration (systemd-networkd)" + } + + override fun getDescription(): String { + return displayName + } + + override fun getDefaultExtension(): String { + return "link" + } + + override fun getIcon(): Icon? { + return UnitFileIcon.LINK + } + + override fun getDisplayName(): @Nls String { + return "Network Device Configuration (systemd-networkd)" + } + + companion object { + val INSTANCE = LinkFileType() + } +} diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/NetDevFileType.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/NetDevFileType.kt new file mode 100644 index 00000000..4669b746 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/NetDevFileType.kt @@ -0,0 +1,32 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.filetypes + +import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileIcon +import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileLanguage +import org.jetbrains.annotations.Nls +import javax.swing.Icon + +class NetDevFileType private constructor() : AbstractUnitFileType(UnitFileLanguage.INSTANCE) { + override fun getName(): String { + return "Virtual Network Device Configuration (systemd-networkd)" + } + + override fun getDescription(): String { + return displayName + } + + override fun getDefaultExtension(): String { + return "netdev" + } + + override fun getIcon(): Icon? { + return UnitFileIcon.NETDEV + } + + override fun getDisplayName(): @Nls String { + return "Virtual Network Device Configuration (systemd-networkd)" + } + + companion object { + val INSTANCE = NetDevFileType() + } +} diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/NetworkFileType.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/NetworkFileType.kt new file mode 100644 index 00000000..aa5a8ac0 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/NetworkFileType.kt @@ -0,0 +1,32 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.filetypes + +import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileIcon +import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileLanguage +import org.jetbrains.annotations.Nls +import javax.swing.Icon + +class NetworkFileType private constructor() : AbstractUnitFileType(UnitFileLanguage.INSTANCE) { + override fun getName(): String { + return "Network Configuration (systemd-networkd)" + } + + override fun getDescription(): String { + return displayName + } + + override fun getDefaultExtension(): String { + return "network" + } + + override fun getIcon(): Icon? { + return UnitFileIcon.NETWORK + } + + override fun getDisplayName(): @Nls String { + return "Network Configuration (systemd-networkd)" + } + + companion object { + val INSTANCE = NetworkFileType() + } +} diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt index c6ebdf46..48493864 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt @@ -21,15 +21,17 @@ import java.io.InputStreamReader import java.nio.charset.StandardCharsets import java.util.* import java.util.regex.Pattern -import kotlin.collections.HashMap enum class FileClass(val fileClass: String, val gperfFile: String) { UNIT_FILE("unit", "load-fragment-gperf.gperf"), - NSPAWN("nspawn", "nspawn-gperf.gperf"); + NSPAWN("nspawn", "nspawn-gperf.gperf"), + NETDEV("netdev", "netdev-gperf.gperf"), + NETWORK("network", "networkd-network-gperf.gperf"), + LINK("link", "link-config-gperf.gperf") } -fun PsiFile.fileClass() : FileClass { +fun PsiFile.fileClass(): FileClass { return SemanticDataRepository.getFileClassForFilename(this.name) } @@ -42,6 +44,10 @@ class SemanticDataRepository private constructor() { private val fileClassToUndocumentedOptionInfo: MutableMap>> + private val fileClassToValidatorConfigToSectionKeys: MutableMap>> = HashMap() + + private val fileClassToSectionKeyToValidatorConfig: MutableMap> = HashMap() + class KeywordData { var declaredUnderKeyword: String? = null var declaredInFile: String? = null @@ -65,17 +71,22 @@ class SemanticDataRepository private constructor() { for (fileClass in FileClass.entries) { val name = SEMANTIC_DATA_ROOT + fileClass.gperfFile val resource = this.javaClass.classLoader.getResourceAsStream(name) + fileClassToValidatorConfigToSectionKeys.put(fileClass.fileClass, HashMap()) + fileClassToSectionKeyToValidatorConfig.put(fileClass.fileClass, HashMap()) + if (resource != null) { try { BufferedReader(InputStreamReader(resource)).use { fr -> var line: String? while (fr.readLine().also { line = it } != null) { - val m = LINE_MATCHER.matcher(line) + val lineWithoutComments = line?.replace(Regex("/\\*.*?\\*/"), "") + val m = LINE_MATCHER.matcher(lineWithoutComments) if (m.find()) { val section = m.group("Section") val key = m.group("Key") val validator = m.group("Validator") - val mysteryValue = m.group("MysteryColumn") + val mysteryValue = m.group("MysteryColumn1") + val mysteryValue2 = m.group("MysteryColumn2") when (mysteryValue) { LEGACY_PARAMETERS_KEY, EXPERIMENTAL_PARAMETERS_KEY -> {} @@ -85,6 +96,22 @@ class SemanticDataRepository private constructor() { fileClassToSectionToKeyAndValidatorMap.computeIfAbsent(fileClass.fileClass, { _: String? -> HashMap() }).computeIfAbsent(intern(cache, section)) { _: String? -> HashMap() }[intern(cache, key)] = intern(cache2, myValidator) } } + + if (mysteryValue2 != "0") { + + val validatorConfigKey = mysteryValue + "/" + mysteryValue2 + val validatorConfigToSectionKey = fileClassToValidatorConfigToSectionKeys.get(fileClass.fileClass) + validatorConfigToSectionKey?.putIfAbsent(validatorConfigKey, HashSet()) + + val set = validatorConfigToSectionKey?.get(validatorConfigKey) + + set?.add("$section.$key") + + val sectionKeyToValidatorConfig = fileClassToSectionKeyToValidatorConfig.get(fileClass.fileClass) + + sectionKeyToValidatorConfig?.put("$section.$key", validatorConfigKey) + } + } } } @@ -96,6 +123,7 @@ class SemanticDataRepository private constructor() { } } + validatorMap = HashMap() validatorMap.putAll(BooleanOptionValue.validators) validatorMap.putAll(TriStateOptionValue.validators) @@ -123,8 +151,7 @@ class SemanticDataRepository private constructor() { validatorMap.putAll(CPUWeightOptionValue.validators) validatorMap.putAll(CPUSharesOptionValue.validators) validatorMap.putAll(CgroupSocketBindOptionValue.validators) - validatorMap.putAll(RlimitOptionValue.validators) - // Scopes are not supported since they aren't standard unit files. + validatorMap.putAll(RlimitOptionValue.validators) // Scopes are not supported since they aren't standard unit files. fileClassToSectionNameToKeyValuesFromDoc["unit"]?.remove(SCOPE_KEYWORD) fileClassToSectionToKeyAndValidatorMap["unit"]?.remove(SCOPE_KEYWORD) } @@ -140,23 +167,23 @@ class SemanticDataRepository private constructor() { mapper.registerModule(KotlinModule.Builder().build()) try { - val read : Map>> = mapper.readValue(sectionToKeywordMapFromDocJsonFile) + val read: Map>> = mapper.readValue(sectionToKeywordMapFromDocJsonFile) - // intern strings in maps - val output: MutableMap>> = HashMap() + // intern strings in maps + val output: MutableMap>> = HashMap() - read.forEach { (fileClass: String, m0: Map>) -> - val fileClassMap = HashMap>() - output[fileClass] = fileClassMap + read.forEach { (fileClass: String, m0: Map>) -> + val fileClassMap = HashMap>() + output[fileClass] = fileClassMap - m0.forEach { (section: String, m1: Map) -> - val converted = HashMap() - m1.forEach { (keyword: String, m2: KeywordData) -> converted[intern(cache, keyword)] = m2 } - fileClassMap[intern(cache, section)] = converted + m0.forEach { (section: String, m1: Map) -> + val converted = HashMap() + m1.forEach { (keyword: String, m2: KeywordData) -> converted[intern(cache, keyword)] = m2 } + fileClassMap[intern(cache, section)] = converted + } } - } - return output + return output } catch (e: IOException) { LOG.error("Unable to initialize data for systemd inspections plugin", e) @@ -170,6 +197,16 @@ class SemanticDataRepository private constructor() { return Collections.unmodifiableSet(fileClassToSectionNameToKeyValuesFromDoc.getOrDefault(fileClass, emptyMap()).keys) } + fun getAliasesForSectionKey(fileClass: String, sectionKey: String): Set { + + val validatorConfig = fileClassToSectionKeyToValidatorConfig.getOrDefault(fileClass, emptyMap())[sectionKey] + + if (validatorConfig == null) { + return emptySet() + } + + return Collections.unmodifiableSet(fileClassToValidatorConfigToSectionKeys.getOrDefault(fileClass, emptyMap()).getOrDefault(validatorConfig, emptySet())) + } /** * Returns the allowed keywords by a given section name. @@ -262,6 +299,99 @@ class SemanticDataRepository private constructor() { "Exec" -> "https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#%5BExec%5D%20Section%20Options" "Files" -> "https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#%5BFiles%5D%20Section%20Options" "Network" -> "https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#%5BNetwork%5D%20Section%20Options" + // network files + // cat systemd.network.xml | grep "Section Options" | sed -E 's/^.+\[(.+)\].+$/"\1" -> """https:\/\/www.freedesktop.org\/software\/systemd\/man\/latest\/systemd.network.html#%5B\1%5D%20Section%20Options"""/g' | sort + "Address" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BAddress%5D%20Section%20Options""" + "BandMultiQueueing" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BBandMultiQueueing%5D%20Section%20Options""" + "BFIFO" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BBFIFO%5D%20Section%20Options""" + "BridgeFDB" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BBridgeFDB%5D%20Section%20Options""" + "Bridge" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BBridge%5D%20Section%20Options""" + "BridgeMDB" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BBridgeMDB%5D%20Section%20Options""" + "BridgeVLAN" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BBridgeVLAN%5D%20Section%20Options""" + "CAKE" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BCAKE%5D%20Section%20Options""" + "CAN" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BCAN%5D%20Section%20Options""" + "ClassfulMultiQueueing" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BClassfulMultiQueueing%5D%20Section%20Options""" + "ControlledDelay" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BControlledDelay%5D%20Section%20Options""" + "DeficitRoundRobinSchedulerClass" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BDeficitRoundRobinSchedulerClass%5D%20Section%20Options""" + "DeficitRoundRobinScheduler" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BDeficitRoundRobinScheduler%5D%20Section%20Options""" + "DHCPPrefixDelegation" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BDHCPPrefixDelegation%5D%20Section%20Options""" + "DHCPServer" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BDHCPServer%5D%20Section%20Options""" + "DHCPServerStaticLease" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BDHCPServerStaticLease%5D%20Section%20Options""" + "DHCPv4" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BDHCPv4%5D%20Section%20Options""" + "DHCPv6" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BDHCPv6%5D%20Section%20Options""" + "EnhancedTransmissionSelection" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BEnhancedTransmissionSelection%5D%20Section%20Options""" + "FairQueueingControlledDelay" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BFairQueueingControlledDelay%5D%20Section%20Options""" + "FairQueueing" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BFairQueueing%5D%20Section%20Options""" + "FlowQueuePIE" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BFlowQueuePIE%5D%20Section%20Options""" + "GenericRandomEarlyDetection" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BGenericRandomEarlyDetection%5D%20Section%20Options""" + "HeavyHitterFilter" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BHeavyHitterFilter%5D%20Section%20Options""" + "HierarchyTokenBucketClass" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BHierarchyTokenBucketClass%5D%20Section%20Options""" + "HierarchyTokenBucket" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BHierarchyTokenBucket%5D%20Section%20Options""" + "IPoIB" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BIPoIB%5D%20Section%20Options""" + "IPv6AcceptRA" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BIPv6AcceptRA%5D%20Section%20Options""" + "IPv6AddressLabel" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BIPv6AddressLabel%5D%20Section%20Options""" + "IPv6PREF64Prefix" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BIPv6PREF64Prefix%5D%20Section%20Options""" + "IPv6Prefix" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BIPv6Prefix%5D%20Section%20Options""" + "IPv6RoutePrefix" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BIPv6RoutePrefix%5D%20Section%20Options""" + "IPv6SendRA" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BIPv6SendRA%5D%20Section%20Options""" + "Link" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BLink%5D%20Section%20Options""" + "LLDP" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BLLDP%5D%20Section%20Options""" + "Match" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BMatch%5D%20Section%20Options""" + "Neighbor" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BNeighbor%5D%20Section%20Options""" + "NetworkEmulator" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BNetworkEmulator%5D%20Section%20Options""" + //"Network" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BNetwork%5D%20Section%20Options""" + "NextHop" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BNextHop%5D%20Section%20Options""" + "PFIFOFast" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BPFIFOFast%5D%20Section%20Options""" + "PFIFOHeadDrop" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BPFIFOHeadDrop%5D%20Section%20Options""" + "PFIFO" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BPFIFO%5D%20Section%20Options""" + "PIE" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BPIE%5D%20Section%20Options""" + "QDisc" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BQDisc%5D%20Section%20Options""" + "QuickFairQueueingClass" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BQuickFairQueueingClass%5D%20Section%20Options""" + "QuickFairQueueing" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BQuickFairQueueing%5D%20Section%20Options""" + "Route" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BRoute%5D%20Section%20Options""" + "RoutingPolicyRule" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BRoutingPolicyRule%5D%20Section%20Options""" + "StochasticFairBlue" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BStochasticFairBlue%5D%20Section%20Options""" + "StochasticFairnessQueueing" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BStochasticFairnessQueueing%5D%20Section%20Options""" + "TokenBucketFilter" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BTokenBucketFilter%5D%20Section%20Options""" + "TrivialLinkEqualizer" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.network.html#%5BTrivialLinkEqualizer%5D%20Section%20Options""" + // Netdev + "BareUDP" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BBareUDP%5D%20Section%20Options""" + "BatmanAdvanced" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BBatmanAdvanced%5D%20Section%20Options""" + "Bond" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BBond%5D%20Section%20Options""" + //"Bridge" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BBridge%5D%20Section%20Options""" + "FooOverUDP" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BFooOverUDP%5D%20Section%20Options""" + "GENEVE" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BGENEVE%5D%20Section%20Options""" + //"IPoIB" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BIPoIB%5D%20Section%20Options""" + "IPVLAN" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BIPVLAN%5D%20Section%20Options""" + "IPVTAP" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BIPVTAP%5D%20Section%20Options""" + "L2TP" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BL2TP%5D%20Section%20Options""" + "L2TPSession" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BL2TPSession%5D%20Section%20Options""" + "MACsec" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BMACsec%5D%20Section%20Options""" + "MACsecReceiveAssociation" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BMACsecReceiveAssociation%5D%20Section%20Options""" + "MACsecReceiveChannel" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BMACsecReceiveChannel%5D%20Section%20Options""" + "MACsecTransmitAssociation" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BMACsecTransmitAssociation%5D%20Section%20Options""" + "MACVLAN" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BMACVLAN%5D%20Section%20Options""" + "MACVTAP" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BMACVTAP%5D%20Section%20Options""" + //"Match" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BMatch%5D%20Section%20Options""" + "NetDev" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BNetDev%5D%20Section%20Options""" + "Peer" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BPeer%5D%20Section%20Options""" + "Tap" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BTap%5D%20Section%20Options""" + "Tun" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BTun%5D%20Section%20Options""" + "Tunnel" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BTunnel%5D%20Section%20Options""" + "VLAN" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BVLAN%5D%20Section%20Options""" + "VRF" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BVRF%5D%20Section%20Options""" + "VXCAN" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BVXCAN%5D%20Section%20Options""" + "VXLAN" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BVXLAN%5D%20Section%20Options""" + "WireGuard" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BWireGuard%5D%20Section%20Options""" + "WireGuardPeer" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BWireGuardPeer%5D%20Section%20Options""" + "WLAN" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BWLAN%5D%20Section%20Options""" + "Xfrm" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.netdev.html#%5BXfrm%5D%20Section%20Options""" + + // link + //"Link" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.link.html#%5BLink%5D%20Section%20Options""" + //"Match" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.link.html#%5BMatch%5D%20Section%20Options""" + "SR-IOV" -> """https://www.freedesktop.org/software/systemd/man/latest/systemd.link.html#%5BSR-IOV%5D%20Section%20Options""" + else -> null } } @@ -318,9 +448,94 @@ unit types. These options are documented in """The unit file may include a [Unit] section, which carries generic information about the unit that is not dependent on the type of unit.""" + // nspawn "Exec" -> """The [Exec] section controls various execution parameters.""" "Files" -> """The [Files] section, carries various parameters configuring the filesystem of the container.""" "Network" -> """The [Network] section carries various parameters configuring the network connectivity of the container.""" + + // netdev + "BareUDP" -> """The [BareUDP] section applies only for netdevs of kind "bareudp". Bare UDP tunnels provide a generic L3 encapsulation support for tunnelling different L3 protocols like MPLS, IP etc. inside of a UDP tunnel.""" + "BatmanAdvanced" -> """The [BatmanAdvanced] section applies only for netdevs of kind "batadv". B.A.T.M.A.N. Advanced is a routing protocol for multi-hop mobile ad-hoc networks which operates on layer 2.""" + "Bond" -> """The [Bond] section applies only for netdevs of kind "bond". A bond device is an aggregation of all its slave devices. See Linux Ethernet Bonding Driver HOWTO for details. ... .""" + "Bridge" -> """The [Bridge] section applies only for netdevs of kind "bridge". A bridge device is a software switch, and each of its slave devices and the bridge itself are ports of the switch.""" + "FooOverUDP" -> """The [FooOverUDP] section applies only for netdevs of kind "fou". See Foo over UDP for more details.""" + "GENEVE" -> """The [GENEVE] section applies only for netdevs of kind "geneve". GENEVE (GEneric NEtwork Virtualization Encapsulation) is a flexible tunneling protocol used in virtualized networking environments. See RFC 8926 for details.""" + "IPoIB" -> """The [IPoIB] section applies only for netdevs of kind "ipoib". IP over InfiniBand (IPoIB) enables IP networking over high-performance InfiniBand fabric.""" + "IPVLAN" -> """The [IPVLAN] section applies only for netdevs of kind "ipvlan". IPVLAN devices allow multiple IP addresses on a single physical interface while sharing a MAC address, reducing broadcast domain size.""" + "IPVTAP" -> """The [IPVTAP] section applies only for netdevs of kind "ipvtap". Similar to IPVLAN but integrates with the tap interface, allowing userspace applications to receive traffic.""" + "L2TPSession" -> """The [L2TPSession] section applies only for netdevs of kind "l2tp". Defines Layer 2 Tunneling Protocol (L2TP) session parameters, used to create VPNs or encapsulate other protocols.""" + "L2TP" -> """The [L2TP] section applies only for netdevs of kind "l2tp". Configures L2TP tunnels, which are used for VPNs and ISP service delivery. L2TP itself does not provide encryption.""" + "MACsecReceiveAssociation" -> """The [MACsecReceiveAssociation] section applies only for netdevs of kind "macsec". Defines receive-side security association settings for MACsec-secured Ethernet links.""" + "MACsecReceiveChannel" -> """The [MACsecReceiveChannel] section applies only for netdevs of kind "macsec". Configures reception channels for MACsec-secured communication over Ethernet links.""" + "MACsec" -> """The [MACsec] section applies only for netdevs of kind "macsec". MACsec (IEEE 802.1AE) provides encryption and authentication for Ethernet frames at the link layer.""" + "MACsecTransmitAssociation" -> """The [MACsecTransmitAssociation] section applies only for netdevs of kind "macsec". Defines transmit-side security association settings for MACsec-protected links.""" + "MACVLAN" -> """The [MACVLAN] section applies only for netdevs of kind "macvlan". MACVLAN allows multiple MAC addresses to be assigned to a single physical interface, enabling network isolation.""" + "MACVTAP" -> """The [MACVTAP] section applies only for netdevs of kind "macvtap". MACVTAP extends MACVLAN with a tap interface, allowing userspace applications to interact with the virtual interface.""" + "Match" -> """The [Match] section is used to filter which network devices a .netdev file should apply to, based on criteria such as interface name, MAC address, or driver.""" + "NetDev" -> """The [NetDev] section defines a virtual network device. Each .netdev file describes a single virtual network device and its properties.""" + "Peer" -> """The [Peer] section applies only for netdevs of kind "veth". Defines the peer interface of a veth (virtual Ethernet) tunnel, which is commonly used to connect network namespaces.""" + "Tap" -> """The [Tap] section applies only for netdevs of kind "tap". A TAP device is a virtual network interface that operates at Layer 2, used for bridging and tunneling.""" + "Tunnel" -> """The [Tunnel] section applies only for netdevs of tunneling kinds (e.g., "ipip", "sit", "gre", "gretap", "ip6gre", "ip6gretap", "vti", "vti6", "ip6tnl", and "erspan"). Configures tunneling parameters for encapsulating traffic over another network layer.""" + "Tun" -> """The [Tun] section applies only for netdevs of kind "tun". A TUN device is a virtual network interface that operates at Layer 3, used for IP tunneling.""" + "VLAN" -> """The [VLAN] section applies only for netdevs of kind "vlan". VLAN interfaces allow traffic segmentation using IEEE 802.1Q tagging. See IEEE 802.1Q.""" + "VRF" -> """The [VRF] section applies only for netdevs of kind "vrf". A Virtual Routing and Forwarding (VRF) interface provides separate routing domains within a system. See VRF documentation.""" + "VXCAN" -> """The [VXCAN] section applies only for netdevs of kind "vxcan". VXCAN is a virtual CAN tunnel, similar to veth, creating a peer-to-peer link between namespaces for CAN traffic.""" + "VXLAN" -> """The [VXLAN] section applies only for netdevs of kind "vxlan". VXLAN (Virtual eXtensible LAN) is a tunneling protocol for overlay networks, commonly used in cloud and datacenter environments.""" + "WireGuardPeer" -> """The [WireGuardPeer] section applies only for netdevs of kind "wireguard". Defines peer-specific settings for WireGuard tunnels, such as allowed IPs and public keys.""" + "WireGuard" -> """The [WireGuard] section applies only for netdevs of kind "wireguard". WireGuard is a modern, secure VPN protocol designed for simplicity and high performance.""" + "WLAN" -> """The [WLAN] section applies only for netdevs of kind "wlan". Configures a virtual wireless LAN (WLAN) interface, useful for virtualized network environments.""" + "Xfrm" -> """The [Xfrm] section applies only for netdevs of kind "xfrm". Configures XFRM-based virtual interfaces, which handle IPsec and other transformation-based networking policies.""" + "Link" -> """The [Link] section specifies how the link should be configured. .""" + "SR-IOV" -> """The [SR-IOV] section specifies how to configure several Single Root I/O Virtualization (SR-IOV).""" + // network files + // cat systemd.network.xml | grep "Section Options" | sed -E 's/^.+\[(.+)\].+$/"\1" -> """The [\1] section ... """ /g' | sort + "Address" -> """The [Address] section defines IP addresses assigned to the interface. It supports settings such as the main address, peer address, and broadcast address configuration.""" + "BandMultiQueueing" -> """The [BandMultiQueueing] section manages the queueing discipline (qdisc) of Band Multi Queueing (multiq).""" + "BFIFO" -> """The [BFIFO] section manages the queueing discipline (qdisc) of Byte limited Packet First In First Out (bfifo). It sets limits on the FIFO buffer size in bytes.""" + "BridgeFDB" -> """The [BridgeFDB] section manages the forwarding database table of a port, allowing static MAC table entries for bridges.""" + "BridgeMDB" -> """The [BridgeMDB] section configures multicast membership entries in the bridge multicast database (MDB), enabling multicast group management.""" + "BridgeVLAN" -> """The [BridgeVLAN] section manages VLAN ID configurations for a bridge, requiring VLAN filtering to be enabled.""" + "CAKE" -> """The [CAKE] section manages the queueing discipline of Common Applications Kept Enhanced (CAKE), a QoS system that improves bandwidth fairness and latency.""" + "CAN" -> """The [CAN] section configures Controller Area Network (CAN bus) settings, including bitrate and sample points.""" + "ClassfulMultiQueueing" -> """The [ClassfulMultiQueueing] section manages the queueing discipline (qdisc) of Classful Multi Queueing (mq).""" + "ControlledDelay" -> """The [ControlledDelay] section manages the queueing discipline (qdisc) of Controlled Delay (CoDel), used for active queue management.""" + "DeficitRoundRobinSchedulerClass" -> """The [DeficitRoundRobinSchedulerClass] section manages the traffic control class of Deficit Round Robin Scheduler (DRR).""" + "DeficitRoundRobinScheduler" -> """The [DeficitRoundRobinScheduler] section manages the queueing discipline (qdisc) of Deficit Round Robin Scheduler (DRR).""" + "DHCPPrefixDelegation" -> """The [DHCPPrefixDelegation] section configures IPv6 prefix delegation settings for DHCPv6.""" + "DHCPServerStaticLease" -> """The [DHCPServerStaticLease] section configures static DHCP leases, assigning a fixed IPv4 address to a MAC address.""" + "DHCPServer" -> """The [DHCPServer] section configures the DHCPv4 server, allowing settings such as address pools and lease times.""" + "DHCPv4" -> """The [DHCPv4] section configures the DHCPv4 client, supporting options such as IAID, DUID, and requested DHCP options.""" + "DHCPv6" -> """The [DHCPv6] section configures the DHCPv6 client, controlling settings for IPv6 address assignment.""" + "EnhancedTransmissionSelection" -> """The [EnhancedTransmissionSelection] section manages the queueing discipline (qdisc) of Enhanced Transmission Selection (ETS).""" + "FairQueueingControlledDelay" -> """The [FairQueueingControlledDelay] section manages the queueing discipline of FQ-CoDel, a fair queueing version of CoDel.""" + "FairQueueing" -> """The [FairQueueing] section manages fair queueing algorithms, ensuring balanced traffic distribution.""" + "FlowQueuePIE" -> """The [FlowQueuePIE] section configures the queueing discipline (qdisc) of Flow Queue Proportional Integral controller-Enhanced (fq_pie).""" + "GenericRandomEarlyDetection" -> """The [GenericRandomEarlyDetection] section configures the Random Early Detection (RED) algorithm for congestion control.""" + "HeavyHitterFilter" -> """The [HeavyHitterFilter] section configures the queueing discipline (qdisc) of Heavy Hitter Filter (hhf).""" + "HierarchyTokenBucketClass" -> """The [HierarchyTokenBucketClass] section manages the traffic control class of Hierarchy Token Bucket (htb).""" + "HierarchyTokenBucket" -> """The [HierarchyTokenBucket] section manages the queueing discipline (qdisc) of Hierarchy Token Bucket (htb).""" + "IPv6AcceptRA" -> """The [IPv6AcceptRA] section configures the reception of IPv6 Router Advertisements (RA) for autoconfiguration.""" + "IPv6AddressLabel" -> """The [IPv6AddressLabel] section configures IPv6 address labels for precedence in address selection.""" + "IPv6PREF64Prefix" -> """The [IPv6PREF64Prefix] section configures IPv6 PREF64 (NAT64) prefixes for translation.""" + "IPv6Prefix" -> """The [IPv6Prefix] section defines IPv6 prefixes for network configuration.""" + "IPv6RoutePrefix" -> """The [IPv6RoutePrefix] section defines IPv6 route prefixes distributed via Router Advertisements.""" + "IPv6SendRA" -> """The [IPv6SendRA] section configures IPv6 Router Advertisement (RA) sending settings.""" + "LLDP" -> """The [LLDP] section manages Link Layer Discovery Protocol (LLDP) settings, including MUD URLs.""" + "Neighbor" -> """The [Neighbor] section configures static neighbor entries in the ARP or IPv6 neighbor table.""" + "NetworkEmulator" -> """The [NetworkEmulator] section configures a network emulation qdisc, allowing simulation of delays and packet loss.""" + "NextHop" -> """The [NextHop] section defines next-hop entries for routing configuration.""" + "PFIFOFast" -> """The [PFIFOFast] section manages the queueing discipline (qdisc) of Packet First In First Out Fast (pfifo_fast).""" + "PFIFOHeadDrop" -> """The [PFIFOHeadDrop] section manages the queueing discipline (qdisc) of Packet First In First Out Head Drop (pfifo_head_drop).""" + "PFIFO" -> """The [PFIFO] section manages the queueing discipline (qdisc) of Packet First In First Out (pfifo).""" + "PIE" -> """The [PIE] section manages the queueing discipline (qdisc) of Proportional Integral Enhanced (PIE) for congestion control.""" + "QDisc" -> """The [QDisc] section configures generic queueing disciplines for traffic control.""" + "QuickFairQueueingClass" -> """The [QuickFairQueueingClass] section manages the traffic control class of Quick Fair Queueing (qfq).""" + "QuickFairQueueing" -> """The [QuickFairQueueing] section manages the queueing discipline (qdisc) of Quick Fair Queueing (QFQ).""" + "Route" -> """The [Route] section configures static routing entries for interfaces.""" + "RoutingPolicyRule" -> """The [RoutingPolicyRule] section configures rules for routing policy decisions.""" + "TokenBucketFilter" -> """The [TokenBucketFilter] section manages the queueing discipline (qdisc) of the token bucket filter (tbf).""" + "TrivialLinkEqualizer" -> """The [TrivialLinkEqualizer] section manages the queueing discipline (qdisc) of Trivial Link Equalizer (teql).""" + else -> null } } @@ -335,10 +550,8 @@ unit types. These options are documented in { - if (!html) String.format("'%s' in section '%s' is not officially supported", keyName, sectionName) else ("

" + keyName + " in section " + sectionName + " is not officially supported.

" - + "More information is available here") + if (!html) String.format( + "'%s' in section '%s' is not officially supported", + keyName, + sectionName + ) else ("

" + keyName + " in section " + sectionName + " is not officially supported.

" + "More information is available here") } "moved" -> { val newKeyName = StringUtil.notNullize(options.replacedWithKey, keyName) val newSectionName = StringUtil.notNullize(options.replacedWithSection, sectionName) - val semanticsNote = if (!html) "" else ("

NOTE: The semantics of the new value may not match the existing value.

" - + "More information is available here") + val semanticsNote = if (!html) "" else ("

NOTE: The semantics of the new value may not match the existing value.

" + "More information is available here") if (newSectionName == sectionName) { LOG.assertTrue(newKeyName != keyName, String.format("Meaningless move/rename of %s.%s", sectionName, keyName)) return if (!html) { String.format("'%s' in section '%s' has been renamed to '%s'", keyName, sectionName, newKeyName) - } else ("

The key " + keyName + " in section " + sectionName + " has been renamed to " - + "" + newKeyName + "" + semanticsNote) + } else ("

The key " + keyName + " in section " + sectionName + " has been renamed to " + "" + newKeyName + "" + semanticsNote) } if (newKeyName == keyName) { return if (!html) { String.format("'%s' has been moved to section '%s'", keyName, newSectionName) - } else ("

The key " + keyName + " in section " + sectionName + " has been moved to " - + "section " + newSectionName + "" + semanticsNote) + } else ("

The key " + keyName + " in section " + sectionName + " has been moved to " + "section " + newSectionName + "" + semanticsNote) } if (!html) { String.format("'%s' in section '%s' has been moved to '%s' in section '%s'", keyName, sectionName, newKeyName, newSectionName) - } else ("

The key " + keyName + " in section " + sectionName + " has been moved to " - + "" + newKeyName + " in section " + newSectionName + "" - + semanticsNote) + } else ("

The key " + keyName + " in section " + sectionName + " has been moved to " + "" + newKeyName + " in section " + newSectionName + "" + semanticsNote) } "manual" -> { @@ -423,7 +634,7 @@ unit types. These options are documented in { - val completeSections = when(getUnitType(fileName)) { - "Automount" -> setOf("Unit", "Install", "Automount") + val completeSections = when (getUnitType(fileName)) { + "Automount" -> setOf("Unit", "Install", "Automount") "Device", "Target" -> setOf("Unit", "Install") - "Mount" -> setOf("Unit", "Install", "Mount") + "Mount" -> setOf("Unit", "Install", "Mount") "Path" -> setOf("Unit", "Install", "Path") "Service" -> setOf("Unit", "Install", "Service") "Slice" -> setOf("Unit", "Install", "Slice") "Socket" -> setOf("Unit", "Install", "Socket") - "Swap" -> setOf("Unit", "Install", "Swap") + "Swap" -> setOf("Unit", "Install", "Swap") "Timer" -> setOf("Unit", "Install", "Timer") "Nspawn" -> setOf("Exec", "Files", "Network") + "Network" -> setOf( + "Address", "BandMultiQueueing", "BFIFO", "Bridge", "BridgeFDB", "BridgeMDB", "BridgeVLAN", "CAKE", "CAN", "ClassfulMultiQueueing", "ControlledDelay", "DeficitRoundRobinScheduler", "DeficitRoundRobinSchedulerClass", "DHCPPrefixDelegation", "DHCPServer", + "DHCPServerStaticLease", "DHCPv4", "DHCPv6", "EnhancedTransmissionSelection", "FairQueueing", "FairQueueingControlledDelay", "FlowQueuePIE", "GenericRandomEarlyDetection", "HeavyHitterFilter", "HierarchyTokenBucket", "HierarchyTokenBucketClass", + "IPoIB", "IPv6AcceptRA", "IPv6AddressLabel", "IPv6PREF64Prefix", "IPv6Prefix", "IPv6RoutePrefix", "IPv6SendRA", "Link", "LLDP", "Match", "Neighbor", + "Network", "NetworkEmulator", "NextHop", "PFIFO", "PFIFOFast", "PFIFOHeadDrop", "PIE", "QDisc", "QuickFairQueueing", + "QuickFairQueueingClass", "Route", "RoutingPolicyRule", "StochasticFairBlue", "StochasticFairnessQueueing", "TokenBucketFilter", "TrivialLinkEqualizer", + ) + "Link" -> setOf( + "Link", "Match", "SR-IOV", + ) + "Netdev" -> setOf( + "BareUDP", "BatmanAdvanced", "Bond", "Bridge", "FooOverUDP", "GENEVE", "IPoIB", "IPVLAN", "IPVTAP", + "L2TP", "L2TPSession", "MACsec", "MACsecReceiveAssociation", "MACsecReceiveChannel", "MACsecTransmitAssociation", "MACVLAN", + "MACVTAP", "Match", "NetDev", "Peer", "Tap", "Tun", "Tunnel", "VLAN", + "VRF", "VXCAN", "VXLAN", "WireGuard", "WireGuardPeer", "WLAN", "Xfrm", "NetDev" + ) else -> setOf() } @@ -495,23 +722,20 @@ unit types. These options are documented in - setOf("Unit.SuccessAction", "Service.ExecStart", "Service.ExecStop") - "Path" -> - setOf("Path.PathExists", "Path.PathExistsGlob", "Path.PathChanged", "Path.PathModified", "Path.DirectoryNotEmpty") - "Timer" -> - setOf("Timer.OnActiveSec", "Timer.OnBootSec", "Timer.OnStartupSec", "Timer.OnUnitActiveSec", "Timer.OnUnitInactiveSec", "Timer.OnCalendar", "Timer.OnClockChange", "Timer.OnTimezoneChange") - "Swap" -> - setOf("Swap.What") - "Mount" -> - // Both of these are required, but we only error out on one :( + return when (unitType) { + "Service" -> setOf("Unit.SuccessAction", "Service.ExecStart", "Service.ExecStop") + "Path" -> setOf("Path.PathExists", "Path.PathExistsGlob", "Path.PathChanged", "Path.PathModified", "Path.DirectoryNotEmpty") + "Timer" -> setOf("Timer.OnActiveSec", "Timer.OnBootSec", "Timer.OnStartupSec", "Timer.OnUnitActiveSec", "Timer.OnUnitInactiveSec", "Timer.OnCalendar", "Timer.OnClockChange", "Timer.OnTimezoneChange") + "Swap" -> setOf("Swap.What") + "Mount" -> // Both of these are required, but we only error out on one :( // systemd-analyze only complains about what though, so ... we can ignore it until a bug comes. setOf("Mount.What", "Mount.Where") - "Socket" -> - setOf("Socket.ListenStream", "Socket.ListenDatagram", "Socket.ListenSequentialPacket", "Socket.ListenFIFO", "Socket.ListenSpecial", "Socket.ListenNetlink", "Socket.ListenMessageQueue", "Socket.ListenUSBFunction") - "Automount" -> - setOf("Automount.Where") + + "Socket" -> setOf("Socket.ListenStream", "Socket.ListenDatagram", "Socket.ListenSequentialPacket", "Socket.ListenFIFO", "Socket.ListenSpecial", "Socket.ListenNetlink", "Socket.ListenMessageQueue", "Socket.ListenUSBFunction") + "Automount" -> setOf("Automount.Where") // NSpawn? + // Network + // Link + // NetDev else -> setOf() } @@ -520,7 +744,7 @@ unit types. These options are documented in [a-zA-Z0-9_-]+)\.(?[a-zA-Z0-9_-]+)\s*,\s*(?\w+)\s*,\s*(?\w+)\s*,\s*(?.+)$""" private val LINE_MATCHER = Pattern.compile(GPERF_REGEX) private val NULL_VALIDATOR = Validator("NULL", "0") @@ -538,10 +762,12 @@ unit types. These options are documented in FileClass.NSPAWN - "service", "socket", "target", "device", "mount", "automount", "path", "swap", "timer", "slice"-> FileClass.UNIT_FILE - + "service", "socket", "target", "device", "mount", "automount", "path", "swap", "timer", "slice" -> FileClass.UNIT_FILE + "link" -> FileClass.LINK + "netdev" -> FileClass.NETDEV + "network" -> FileClass.NETWORK else -> FileClass.UNIT_FILE } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 39925a99..86d8fa14 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -48,6 +48,9 @@ + + + @@ -58,26 +61,25 @@ - diff --git a/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/link.svg b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/link.svg new file mode 100644 index 00000000..9f635a39 --- /dev/null +++ b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/link.svg @@ -0,0 +1,187 @@ + + + + + + image/svg+xml + + xml + + + + + + + + xml + + + + + + + + + OK + + + + + + + + + + + + + + diff --git a/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/netdev.svg b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/netdev.svg new file mode 100644 index 00000000..79a5ba57 --- /dev/null +++ b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/netdev.svg @@ -0,0 +1,168 @@ + + + + + + image/svg+xml + + xml + + + + + + + + xml + + + + + + + + + OK + + + + + + + + + + diff --git a/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/netlink.svg b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/netlink.svg new file mode 100644 index 00000000..21f47979 --- /dev/null +++ b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/netlink.svg @@ -0,0 +1,147 @@ + + + + + + image/svg+xml + + xml + + + + + + + + xml + + + + + + + + + + + + + OK + + + diff --git a/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/network.svg b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/network.svg new file mode 100644 index 00000000..0c8809f7 --- /dev/null +++ b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/network.svg @@ -0,0 +1,188 @@ + + + + + + image/svg+xml + + xml + + + + + + + + xml + + + + + + + + + OK + + + + + + + + + + diff --git a/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/undocumentedSectionToKeywordMap.json b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/undocumentedSectionToKeywordMap.json index ff48311a..c0a22918 100644 --- a/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/undocumentedSectionToKeywordMap.json +++ b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/undocumentedSectionToKeywordMap.json @@ -8,6 +8,210 @@ } } }, + "netdev": { + "BatmanAdvanced": { + "GatewayBandwithDown":{ + "reason": "moved", + "replacedWithKey": "GatewayBandwidthDown", + "documentationLink": "https://github.com/systemd/systemd/pull/19539" + }, + "GatewayBandwithUp":{ + "reason": "moved", + "replacedWithKey": "GatewayBandwidthUp", + "documentationLink": "https://github.com/systemd/systemd/pull/19539" + } + }, + "GENEVE": { + "UDP6ZeroCheckSumRx": { + "reason": "moved", + "replacedWithKey": "UDP6ZeroChecksumRx", + "documentationLink": "https://github.com/systemd/systemd/commit/6f1a96ded4ee225b17586a3862a4f10fdf126040" + }, + "UDP6ZeroCheckSumTx": { + "reason": "moved", + "replacedWithKey": "UDP6ZeroChecksumTx", + "documentationLink": "https://github.com/systemd/systemd/commit/6f1a96ded4ee225b17586a3862a4f10fdf126040" + } + }, + "VXLAN": { + "UDP6ZeroCheckSumRx": { + "reason": "moved", + "replacedWithKey": "UDP6ZeroChecksumRx", + "documentationLink": "https://github.com/systemd/systemd/pull/4312/files" + }, + "UDP6ZeroCheckSumTx": { + "reason": "moved", + "replacedWithKey": "UDP6ZeroChecksumTx", + "documentationLink": "https://github.com/systemd/systemd/pull/4312/files" + }, + "UDPCheckSum": { + "reason": "moved", + "replacedWithKey": "UDPChecksum", + "documentationLink": "https://github.com/systemd/systemd/pull/4312/files" + }, + "ARPProxy": { + "reason": "moved", + "replacedWithKey": "ReducedARPProxy", + "documentationLink": "https://github.com/systemd/systemd/blob/cf4deeaf1e822ade5c1fbbe2584b23a2d0988439/src/network/netdev/netdev-gperf.gperf#L134" + }, + "Id": { + "reason": "moved", + "replacedWithKey": "VNI", + "documentationLink": "https://github.com/systemd/systemd/blob/cf4deeaf1e822ade5c1fbbe2584b23a2d0988439/src/network/netdev/netdev-gperf.gperf#L127" + } + + }, + "VRF": { + "TableId": { + "reason": "moved", + "replacedWithKey": "Table", + "documentationLink": "https://github.com/systemd/systemd/blob/cf4deeaf1e822ade5c1fbbe2584b23a2d0988439/src/network/netdev/netdev-gperf.gperf#L240" + } + }, + "WireGuard":{ + "FwMark": { + "reason": "moved", + "replacedWithKey": "FirewallMark", + "documentationLink": "https://github.com/systemd/systemd/blob/cf4deeaf1e822ade5c1fbbe2584b23a2d0988439/src/network/netdev/netdev-gperf.gperf#L246" + } + }, + "L2TP": { + "UDP6CheckSumRx": { + "reason": "moved", + "replacedWithKey": "UDP6ChecksumRx" + }, + "UDP6CheckSumTx": { + "reason": "moved", + "replacedWithKey": "UDP6ChecksumTx" + }, + "UDPCheckSum": { + "reason": "moved", + "replacedWithKey": "UDPChecksum" + } + } + }, + "network": { + "Address": { + "PrefixRoute": { + "reason": "moved", + "replacedWithKey": "AddPrefixRoute" + } + }, + "DHCP": { + "CriticalConnection": { + "reason": "moved", + "replacedWithKey": "KeepConfiguration", + "replacedWithSection": "Network", + "documentationLink": "https://github.com/systemd/systemd/issues/13555#issuecomment-531444829" + } + }, + "DHCPv4": { + "BlackList": { + "reason": "moved", + "replacedWithKey": "DenyList" + }, + "CriticalConnection": { + "reason": "moved", + "replacedWithKey": "KeepConfiguration", + "replacedWithSection": "Network", + "documentationLink": "https://github.com/systemd/systemd/issues/13555#issuecomment-531444829" + }, + "UseDomainName": { + "reason": "moved", + "replacedWithKey": "UseDomains" + } + }, + "DHCPv6": { + "RouteMetric": { + "reason": "moved", + "replacedWithKey": "RouteMetric", + "replacedWithSection": "IPv6AcceptRA" + } + }, + "IPv6AcceptRA": { + "DenyList": { + "reason": "moved", + "replacedWithKey": "PrefixDenyList" + }, + "BlackList": { + "reason": "moved", + "replacedWithKey": "PrefixDenyList" + } + }, + "FairQueueingControlledDelay": { + "MemoryLimit": { + "reason": "moved", + "replacedWithKey": "MemoryLimitBytes" + }, + "Quantum": { + "reason": "moved", + "replacedWithKey": "QuantumBytes" + } + }, + "Neighbor": { + "MACAddress": { + "reason": "moved", + "replacedWithKey": "LinkLayerAddress" + } + }, + "Network": { + "IPv4LL":{ + "reason": "moved", + "replacedWithKey": "LinkLocalAddressing" + }, + "IPForward": { + "reason": "moved", + "replacedWithKey": "IPv4Forwarding" + }, + "DHCPv6PrefixDelegation":{ + "reason": "moved", + "replacedWithKey": "DHCPPrefixDelegation" + }, + "IPv6Token":{ + "reason": "moved", + "replacedWithKey": "Token", + "replacedWithSection": "IPv6AcceptRA" + }, + "IPv6AcceptRouterAdvertisements": { + "reason": "moved", + "replacedWithKey": "IPv6AcceptRA" + }, + "IPv6PrefixDelegation": { + "reason": "moved", + "replacedWithKey": "IPv6SendRA" + }, + "ProxyARP": { + "reason": "moved", + "replacedWithKey": "IPv4ProxyARP" + } + }, + "FairQueueing": { + "Quantum":{ + "reason": "moved", + "replacedWithKey": "QuantumBytes" + }, + "InitialQuantum": { + "reason": "moved", + "replacedWithKey": "InitialQuantumBytes" + } + }, + "Route": { + "GatewayOnlink": { + "reason": "moved", + "replacedWithKey": "GatewayOnLink" + } + }, + "TokenBucketFilter": { + "Burst": { + "reason": "moved", + "replacedWithKey": "BurstBytes" + }, + "LimitSize": { + "reason": "moved", + "replacedWithKey": "LimitBytes" + } + } + }, "unit": { "Mount": { "BlockIOAccounting": { diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/InvalidSectionHeaderNameAnnotatorTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/InvalidSectionHeaderNameAnnotatorTest.kt index a8acea32..52996978 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/InvalidSectionHeaderNameAnnotatorTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/InvalidSectionHeaderNameAnnotatorTest.kt @@ -91,7 +91,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Automount] @@ -116,7 +116,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Install] @@ -140,7 +140,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Mount] @@ -165,7 +165,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Path] @@ -191,7 +191,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Service] @@ -217,7 +217,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Slice] @@ -242,7 +242,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Socket] @@ -267,7 +267,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Swap] @@ -292,7 +292,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Install] @@ -317,7 +317,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Timer] @@ -342,7 +342,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Automount] [Mount] @@ -384,7 +384,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Exec] @@ -409,7 +409,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Exec] @@ -440,7 +440,7 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { /* * Fixture Setup */ - //language=unit file (systemd) + // language="unit file (systemd)" val file = """ [Unit] @@ -467,5 +467,99 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { assertContainsElements(highlightTexts, "The section Service is not allowed in Nspawn files, only the following are allowed: [Exec, Files, Network]") } + fun testServiceSectionsInLinkFileHasWarnings() { + /* + * Fixture Setup + */ + // language="unit file (systemd)" + val file = """ + [Unit] + + [Install] + + [Service] + """.trimIndent() + + /* + * Exercise SUT + */ + setupFileInEditor("file.link", file) + val highlights = myFixture.doHighlighting() + + /* + * Verification + */ + assertSize(3, highlights) + + val highlightTexts = highlights.map { it.description } + + assertContainsElements(highlightTexts, "The section Unit is not allowed in Link files, only the following are allowed: [Link, Match, SR-IOV]") + assertContainsElements(highlightTexts, "The section Install is not allowed in Link files, only the following are allowed: [Link, Match, SR-IOV]") + assertContainsElements(highlightTexts, "The section Service is not allowed in Link files, only the following are allowed: [Link, Match, SR-IOV]") + } + + fun testServiceSectionsInNetDevFileHasWarnings() { + /* + * Fixture Setup + */ + // language="unit file (systemd)" + val file = """ + [Unit] + + [Install] + + [Service] + """.trimIndent() + + /* + * Exercise SUT + */ + setupFileInEditor("file.netdev", file) + val highlights = myFixture.doHighlighting() + + /* + * Verification + */ + assertSize(3, highlights) + + val highlightTexts = highlights.map { it.description } + + assertContainsElements(highlightTexts, "The section Unit is not allowed in Netdev files, only the following are allowed: [BareUDP, BatmanAdvanced, Bond, Bridge, FooOverUDP, GENEVE, IPoIB, IPVLAN, IPVTAP, L2TP, L2TPSession, MACsec, MACsecReceiveAssociation, MACsecReceiveChannel, MACsecTransmitAssociation, MACVLAN, MACVTAP, Match, NetDev, Peer, Tap, Tun, Tunnel, VLAN, VRF, VXCAN, VXLAN, WireGuard, WireGuardPeer, WLAN, Xfrm]") + assertContainsElements(highlightTexts, "The section Install is not allowed in Netdev files, only the following are allowed: [BareUDP, BatmanAdvanced, Bond, Bridge, FooOverUDP, GENEVE, IPoIB, IPVLAN, IPVTAP, L2TP, L2TPSession, MACsec, MACsecReceiveAssociation, MACsecReceiveChannel, MACsecTransmitAssociation, MACVLAN, MACVTAP, Match, NetDev, Peer, Tap, Tun, Tunnel, VLAN, VRF, VXCAN, VXLAN, WireGuard, WireGuardPeer, WLAN, Xfrm]") + assertContainsElements(highlightTexts, "The section Service is not allowed in Netdev files, only the following are allowed: [BareUDP, BatmanAdvanced, Bond, Bridge, FooOverUDP, GENEVE, IPoIB, IPVLAN, IPVTAP, L2TP, L2TPSession, MACsec, MACsecReceiveAssociation, MACsecReceiveChannel, MACsecTransmitAssociation, MACVLAN, MACVTAP, Match, NetDev, Peer, Tap, Tun, Tunnel, VLAN, VRF, VXCAN, VXLAN, WireGuard, WireGuardPeer, WLAN, Xfrm]") + } + + fun testServiceSectionsInNetworkFileHasWarnings() { + /* + * Fixture Setup + */ + // language="unit file (systemd)" + val file = """ + [Unit] + + [Install] + + [Service] + """.trimIndent() + + /* + * Exercise SUT + */ + setupFileInEditor("file.network", file) + val highlights = myFixture.doHighlighting() + + /* + * Verification + */ + assertSize(3, highlights) + + val highlightTexts = highlights.map { it.description } + + assertContainsElements(highlightTexts, "The section Unit is not allowed in Network files, only the following are allowed: [Address, BandMultiQueueing, BFIFO, Bridge, BridgeFDB, BridgeMDB, BridgeVLAN, CAKE, CAN, ClassfulMultiQueueing, ControlledDelay, DeficitRoundRobinScheduler, DeficitRoundRobinSchedulerClass, DHCPPrefixDelegation, DHCPServer, DHCPServerStaticLease, DHCPv4, DHCPv6, EnhancedTransmissionSelection, FairQueueing, FairQueueingControlledDelay, FlowQueuePIE, GenericRandomEarlyDetection, HeavyHitterFilter, HierarchyTokenBucket, HierarchyTokenBucketClass, IPoIB, IPv6AcceptRA, IPv6AddressLabel, IPv6PREF64Prefix, IPv6Prefix, IPv6RoutePrefix, IPv6SendRA, Link, LLDP, Match, Neighbor, Network, NetworkEmulator, NextHop, PFIFO, PFIFOFast, PFIFOHeadDrop, PIE, QDisc, QuickFairQueueing, QuickFairQueueingClass, Route, RoutingPolicyRule, StochasticFairBlue, StochasticFairnessQueueing, TokenBucketFilter, TrivialLinkEqualizer]") + assertContainsElements(highlightTexts, "The section Install is not allowed in Network files, only the following are allowed: [Address, BandMultiQueueing, BFIFO, Bridge, BridgeFDB, BridgeMDB, BridgeVLAN, CAKE, CAN, ClassfulMultiQueueing, ControlledDelay, DeficitRoundRobinScheduler, DeficitRoundRobinSchedulerClass, DHCPPrefixDelegation, DHCPServer, DHCPServerStaticLease, DHCPv4, DHCPv6, EnhancedTransmissionSelection, FairQueueing, FairQueueingControlledDelay, FlowQueuePIE, GenericRandomEarlyDetection, HeavyHitterFilter, HierarchyTokenBucket, HierarchyTokenBucketClass, IPoIB, IPv6AcceptRA, IPv6AddressLabel, IPv6PREF64Prefix, IPv6Prefix, IPv6RoutePrefix, IPv6SendRA, Link, LLDP, Match, Neighbor, Network, NetworkEmulator, NextHop, PFIFO, PFIFOFast, PFIFOHeadDrop, PIE, QDisc, QuickFairQueueing, QuickFairQueueingClass, Route, RoutingPolicyRule, StochasticFairBlue, StochasticFairnessQueueing, TokenBucketFilter, TrivialLinkEqualizer]") + assertContainsElements(highlightTexts, "The section Service is not allowed in Network files, only the following are allowed: [Address, BandMultiQueueing, BFIFO, Bridge, BridgeFDB, BridgeMDB, BridgeVLAN, CAKE, CAN, ClassfulMultiQueueing, ControlledDelay, DeficitRoundRobinScheduler, DeficitRoundRobinSchedulerClass, DHCPPrefixDelegation, DHCPServer, DHCPServerStaticLease, DHCPv4, DHCPv6, EnhancedTransmissionSelection, FairQueueing, FairQueueingControlledDelay, FlowQueuePIE, GenericRandomEarlyDetection, HeavyHitterFilter, HierarchyTokenBucket, HierarchyTokenBucketClass, IPoIB, IPv6AcceptRA, IPv6AddressLabel, IPv6PREF64Prefix, IPv6Prefix, IPv6RoutePrefix, IPv6SendRA, Link, LLDP, Match, Neighbor, Network, NetworkEmulator, NextHop, PFIFO, PFIFOFast, PFIFOHeadDrop, PIE, QDisc, QuickFairQueueing, QuickFairQueueingClass, Route, RoutingPolicyRule, StochasticFairBlue, StochasticFairnessQueueing, TokenBucketFilter, TrivialLinkEqualizer]") + } + + } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributorTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributorTest.kt index 0ce47e8d..17b1dfba 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributorTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributorTest.kt @@ -5,6 +5,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { fun testCompletionInInstallSectionReturnsExpectedValues() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Install] Al$COMPLETION_POSITION @@ -21,6 +22,7 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { fun testCompletionInInstallSectionReturnsExpectedValuesWhenAtEndOfFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Install] Al$COMPLETION_POSITION @@ -37,6 +39,7 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { fun testCompletionOfImpossibleToMatchKeyReturnsEmpty() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Install] ZzzZZZZ$COMPLETION_POSITION @@ -52,6 +55,7 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { fun testCompletionInUnknownSectionReturnsEmpty() { // Fixture Setup + // language="unit file (systemd)" val file = """ [X-Unknown] Al$COMPLETION_POSITION @@ -67,6 +71,7 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { fun testCompletionInPathSectionReturnsExpectedValuesWhenAtEndOfFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Path] Pat$COMPLETION_POSITION @@ -83,6 +88,7 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { fun testCompletionAfterKeyWithEmptyValue() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Swap] M @@ -105,6 +111,7 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { fun testCompletionBeforeKeyWithEmptyValue() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Swap] M @@ -127,6 +134,7 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { fun testCompletionAfterComment() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Swap] M @@ -148,6 +156,7 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { fun testCompletionForNSpawnFileInFilesSectionReturnsExpectedValues() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Files] Bi$COMPLETION_POSITION @@ -162,4 +171,52 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Bind", "BindReadOnly", "BindUser") } + fun testCompletionForLinkFileInMatchSectionReturnsExpectedValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Match] + Ke$COMPLETION_POSITION + """.trimIndent() + myFixture.configureByText("file.link", file) + + // Exercise SUT + val completions = basicCompletionResultStrings + + // Verification + assertContainsElements(completions, "KernelCommandLine", "KernelVersion") + } + + fun testCompletionForNetworkFileInMatchSectionReturnsExpectedValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Match] + Ke$COMPLETION_POSITION + """.trimIndent() + myFixture.configureByText("file.network", file) + + // Exercise SUT + val completions = basicCompletionResultStrings + + // Verification + assertContainsElements(completions, "KernelCommandLine", "KernelVersion") + } + + fun testCompletionForNetDevFileInMatchSectionReturnsExpectedValues() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Match] + Ke$COMPLETION_POSITION + """.trimIndent() + myFixture.configureByText("file.netdev", file) + + // Exercise SUT + val completions = basicCompletionResultStrings + + // Verification + assertContainsElements(completions, "KernelCommandLine", "KernelVersion") + } + } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileSectionCompletionContributorTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileSectionCompletionContributorTest.kt index 66cc406c..30db4e35 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileSectionCompletionContributorTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileSectionCompletionContributorTest.kt @@ -4,8 +4,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { - fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInAutomount() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInAutomount() { // Fixture Setup val file = """ [Install] Whatevs=Foo @@ -17,8 +16,7 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Install", "Unit", "Automount") } - fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInDevice() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInDevice() { // Fixture Setup val file = """ [Install] Whatevs=Foo @@ -30,8 +28,7 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Install", "Unit") } - fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInTarget() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInTarget() { // Fixture Setup val file = """ [Install] Whatevs=Foo @@ -43,8 +40,7 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Install", "Unit") } - fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInMount() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInMount() { // Fixture Setup val file = """ [Install] Whatevs=Foo @@ -56,8 +52,7 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Install", "Unit", "Mount") } - fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInPath() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInPath() { // Fixture Setup val file = """ [Install] Whatevs=Foo @@ -69,8 +64,7 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Install", "Unit", "Path") } - fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInService() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInService() { // Fixture Setup val file = """ [Install] Whatevs=Foo @@ -82,8 +76,7 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Install", "Unit", "Service") } - fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInSocket() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInSocket() { // Fixture Setup val file = """ [Install] Whatevs=Foo @@ -95,8 +88,7 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Install", "Unit", "Socket") } - fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInSwap() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInSwap() { // Fixture Setup val file = """ [Install] Whatevs=Foo @@ -108,8 +100,7 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Install", "Unit", "Swap") } - fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInTimer() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInTimer() { // Fixture Setup val file = """ [Install] Whatevs=Foo @@ -129,13 +120,124 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { [$COMPLETION_POSITION """.trimIndent() - myFixture.configureByText("file.nspawn", file) + myFixture.configureByText("file.link", file) val completions = basicCompletionResultStrings - assertContainsElements(completions, "Exec", "Files", "Network") + assertContainsElements(completions, "Link", "Match", "SR-IOV") } - fun testCompletionOfNewSectionInUnknownFileTypeIsEmpty() { - // Fixture Setup + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInNetDev() { // Fixture Setup + val file = """ + [Files] + Whatevs=Foo + + [$COMPLETION_POSITION + """.trimIndent() + myFixture.configureByText("file.netdev", file) + val completions = basicCompletionResultStrings + assertContainsElements( + completions, + "BareUDP", + "BatmanAdvanced", + "Bond", + "Bridge", + "FooOverUDP", + "GENEVE", + "IPoIB", + "IPVLAN", + "IPVTAP", + "L2TP", + "L2TPSession", + "MACsec", + "MACsecReceiveAssociation", + "MACsecReceiveChannel", + "MACsecTransmitAssociation", + "MACVLAN", + "MACVTAP", + "Match", + "NetDev", + "Peer", + "Tap", + "Tun", + "Tunnel", + "VLAN", + "VRF", + "VXCAN", + "VXLAN", + "WireGuard", + "WireGuardPeer", + "WLAN", + "Xfrm" + ) + } + + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInNetwork() { // Fixture Setup + val file = """ + [Files] + Whatevs=Foo + + [$COMPLETION_POSITION + """.trimIndent() + myFixture.configureByText("file.network", file) + val completions = basicCompletionResultStrings + assertContainsElements( + completions, + "Address", + "BandMultiQueueing", + "BFIFO", + "Bridge", + "BridgeFDB", + "BridgeMDB", + "BridgeVLAN", + "CAKE", + "CAN", + "ClassfulMultiQueueing", + "ControlledDelay", + "DeficitRoundRobinScheduler", + "DeficitRoundRobinSchedulerClass", + "DHCPPrefixDelegation", + "DHCPServer", + "DHCPServerStaticLease", + "DHCPv4", + "DHCPv6", + "EnhancedTransmissionSelection", + "FairQueueing", + "FairQueueingControlledDelay", + "FlowQueuePIE", + "GenericRandomEarlyDetection", + "HeavyHitterFilter", + "HierarchyTokenBucket", + "HierarchyTokenBucketClass", + "IPoIB", + "IPv6AcceptRA", + "IPv6AddressLabel", + "IPv6PREF64Prefix", + "IPv6Prefix", + "IPv6RoutePrefix", + "IPv6SendRA", + "Link", + "LLDP", + "Match", + "Neighbor", + "Network", + "NetworkEmulator", + "NextHop", + "PFIFO", + "PFIFOFast", + "PFIFOHeadDrop", + "PIE", + "QDisc", + "QuickFairQueueing", + "QuickFairQueueingClass", + "Route", + "RoutingPolicyRule", + "StochasticFairBlue", + "StochasticFairnessQueueing", + "TokenBucketFilter", + "TrivialLinkEqualizer" + ) + } + + fun testCompletionOfNewSectionInUnknownFileTypeIsEmpty() { // Fixture Setup val file = """ [Tester] Whatevs=$COMPLETION_POSITION diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributorTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributorTest.kt index 3af25705..3475d99a 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributorTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributorTest.kt @@ -142,4 +142,52 @@ class UnitFileValueCompletionContributorTest : AbstractUnitFileTest() { // Verification assertContainsElements(completions, "on", "off", "true", "false", "yes", "no") } + + fun testCompletionOfBooleanOptionReturnsValuesInLinkFile() { + // Fixture Setup + val file = """ + [Link] + AutoNegotiation=$COMPLETION_POSITION + + """.trimIndent() + myFixture.configureByText("file.link", file) + + // Execute SUT + val completions = basicCompletionResultStrings + + // Verification + assertContainsElements(completions, "on", "off", "true", "false", "yes", "no") + } + + fun testCompletionOfBooleanOptionReturnsValuesInNetworkFile() { + // Fixture Setup + val file = """ + [Link] + ARP=$COMPLETION_POSITION + + """.trimIndent() + myFixture.configureByText("file.network", file) + + // Execute SUT + val completions = basicCompletionResultStrings + + // Verification + assertContainsElements(completions, "on", "off", "true", "false", "yes", "no") + } + + fun testCompletionOfBooleanOptionReturnsValuesInNetdevFile() { + // Fixture Setup + val file = """ + [Bridge] + MulticastQuerier=$COMPLETION_POSITION + + """.trimIndent() + myFixture.configureByText("file.netdev", file) + + // Execute SUT + val completions = basicCompletionResultStrings + + // Verification + assertContainsElements(completions, "on", "off", "true", "false", "yes", "no") + } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProviderTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProviderTest.kt index 973b48db..13b232c8 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProviderTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProviderTest.kt @@ -3,6 +3,8 @@ package net.sjrx.intellij.plugins.systemdunitfiles.documentation import com.intellij.lang.documentation.DocumentationProviderEx import junit.framework.TestCase import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.FileClass +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository class UnitFileDocumentationProviderTest : AbstractUnitFileTest() { private val sut: DocumentationProviderEx = UnitFileDocumentationProvider() @@ -472,4 +474,48 @@ class UnitFileDocumentationProviderTest : AbstractUnitFileTest() { ) } + fun testAllKnownSectionsHaveDocumentation() { + // Fixture Setup + + val undocumentedSections = mutableSetOf() + + // Execute SUT + for ( fc in FileClass.entries) { + val sections = SemanticDataRepository.instance.getSectionNamesForFile(fc.fileClass) + + for (sec in sections ) { + val text = SemanticDataRepository.instance.getDocumentationContentForSection(sec) + + if (text == null) { + undocumentedSections += """${fc.fileClass}.${sec}""" + } + } + } + + // Verification + assertEmpty(undocumentedSections) + } + + fun testAllKnownSectionsHaveDocumentationUrls() { + // Fixture Setup + + val missingSectionLinks = mutableSetOf() + + // Execute SUT + for ( fc in FileClass.entries) { + val sections = SemanticDataRepository.instance.getSectionNamesForFile(fc.fileClass) + + for (sec in sections ) { + val text = SemanticDataRepository.instance.getUrlForSectionName(sec) + + if (text == null) { + missingSectionLinks += """${fc.fileClass}.${sec}""" + } + } + } + + // Verification + assertEmpty(missingSectionLinks) + } + } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspectionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspectionTest.kt index 1fdc97d7..9c2fe716 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspectionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspectionTest.kt @@ -230,4 +230,57 @@ class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { TestCase.assertNotNull(highlightElement) TestCase.assertEquals("PrivateUsersChown", highlightElement!!.text) } + + fun testSingleExampleInNetdevFileThrowsWarning() { + + // language="unit file (systemd)" + val file = """ + [VRF] + TableId=2145 + """.trimIndent() + + // Exercise SUT + setupFileInEditor("file.netdev", file) + enableInspection(DeprecatedOptionsInspection::class.java) + + // Verification + val highlights = myFixture.doHighlighting() + + + // Verification + assertSize(1, highlights) + val info = highlights[0] + TestCase.assertEquals("'TableId' in section 'VRF' has been renamed to 'Table'", info!!.description) + val highlightElement = myFixture.file.findElementAt(info.getStartOffset()) + TestCase.assertNotNull(highlightElement) + TestCase.assertEquals("TableId", highlightElement!!.text) + } + + + fun testSingleExampleInNetworkFileThrowsWarning() { + + // language="unit file (systemd)" + val file = """ + [Address] + PrefixRoute=10.0.0.1 + """.trimIndent() + + // Exercise SUT + setupFileInEditor("file.network", file) + enableInspection(DeprecatedOptionsInspection::class.java) + + // Verification + val highlights = myFixture.doHighlighting() + + + // Verification + assertSize(1, highlights) + val info = highlights[0] + TestCase.assertEquals("'PrefixRoute' in section 'Address' has been renamed to 'AddPrefixRoute'", info!!.description) + val highlightElement = myFixture.file.findElementAt(info.getStartOffset()) + TestCase.assertNotNull(highlightElement) + TestCase.assertEquals("PrefixRoute", highlightElement!!.text) + } + + } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/MissingRequiredKeyInspectionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/MissingRequiredKeyInspectionTest.kt index 27fd2726..da6a9117 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/MissingRequiredKeyInspectionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/MissingRequiredKeyInspectionTest.kt @@ -366,7 +366,6 @@ class MissingRequiredKeyInspectionTest : AbstractUnitFileTest() { val file = """ # SPDX-License-Identifier: LGPL-2.1-or-later [Files] - [Network] @@ -383,4 +382,229 @@ class MissingRequiredKeyInspectionTest : AbstractUnitFileTest() { assertSize(0, highlights) } + fun testLinkFileHasNoWarningsWhenEmpty() { + + // Fixture Setup + // language="unit file (systemd)" + val file = """ + # SPDX-License-Identifier: LGPL-2.1-or-later + [Match] + + [Link] + + [SR-IOV] + """.trimIndent() + + // Exercise SUT + setupFileInEditor("file.link", file) + enableInspection(MissingRequiredKeyInspection::class.java) + + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + fun testNetDevFileHasNoWarningsWhenEmpty() { + + // Fixture Setup + // language="unit file (systemd)" + val file = """ + # SPDX-License-Identifier: LGPL-2.1-or-later + [Match] + + [NetDev] + + [Bridge] + + [VLAN] + + [MACVLAN] + + [MACVTAP] + + [IPVLAN] + + [IPVTAP] + + [VXLAN] + + [GENEVE] + + [BareUDP] + + [L2TP] + + [L2TPSession] + + [MACsec] + + [MACsecReceiveChannel] + + [MACsecTransmitAssociation] + + [MACsecReceiveAssociation] + + [Tunnel] + + [FooOverUDP] + + [Peer] + + [VXCAN] + + [Tun] + + [Tap] + + [WireGuard] + + [WireGuardPeer] + + [Bond] + + [Xfrm] + + [VRF] + + [BatmanAdvanced] + + [IPoIB] + + [WLAN] + """.trimIndent() + + // Exercise SUT + setupFileInEditor("file.netdev", file) + enableInspection(MissingRequiredKeyInspection::class.java) + + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + + fun testNetworkFileHasNoWarningsWhenEmpty() { + + // Fixture Setup + // language="unit file (systemd)" + val file = """ + # SPDX-License-Identifier: LGPL-2.1-or-later + [Match] + + [Link] + + [Network] + + [Address] + + [Neighbor] + + [IPv6AddressLabel] + + [RoutingPolicyRule] + + [NextHop] + + [Route] + + [DHCPv4] + + [DHCPv6] + + [DHCPPrefixDelegation] + + [IPv6AcceptRA] + + [DHCPServer] + + [DHCPServerStaticLease] + + [IPv6SendRA] + + [IPv6Prefix] + + [IPv6RoutePrefix] + + [IPv6PREF64Prefix] + + [Bridge] + + [BridgeFDB] + + [BridgeMDB] + + [LLDP] + + [CAN] + + [IPoIB] + + [QDisc] + + [NetworkEmulator] + + [TokenBucketFilter] + + [PIE] + + [FlowQueuePIE] + + [StochasticFairBlue] + + [StochasticFairnessQueueing] + + [BFIFO] + + [PFIFO] + + [PFIFOHeadDrop] + + [PFIFOFast] + + [CAKE] + + [ControlledDelay] + + [DeficitRoundRobinScheduler] + + [DeficitRoundRobinSchedulerClass] + + [EnhancedTransmissionSelection] + + [GenericRandomEarlyDetection] + + [FairQueueingControlledDelay] + + [FairQueueing] + + [TrivialLinkEqualizer] + + [HierarchyTokenBucket] + + [HierarchyTokenBucketClass] + + [ClassfulMultiQueueing] + + [BandMultiQueueing] + + [HeavyHitterFilter] + + [QuickFairQueueing] + + [QuickFairQueueingClass] + + [BridgeVLAN] + """.trimIndent() + + // Exercise SUT + setupFileInEditor("file.network", file) + enableInspection(MissingRequiredKeyInspection::class.java) + + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspectionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspectionTest.kt index c0ff9d6d..72e7dd9c 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspectionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspectionTest.kt @@ -7,6 +7,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testValidFileHasNoErrors() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Unit] Description=Hello Good Sir @@ -28,6 +29,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testUnknownKeyInUnitSectionGeneratesWarning() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Unit] BadKey=Hello Good Sir @@ -51,6 +53,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testUnknownKeyInInstallSectionGeneratesWarning() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Install] BadKey=Hello Good Sir @@ -74,6 +77,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testUnknownKeyInServiceSectionGeneratesWarning() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] BadKey=Hello Good Sir @@ -97,6 +101,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testTwoUnknownKeysInSameSectionReturnError() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] BadKey=Hello Good Sir @@ -125,6 +130,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testTwoUnknownKeysInDistinctSectionsReturnErrors() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] BadKey=Hello Good Sir @@ -154,6 +160,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testKeyStartingWithXDashDoesNotReturnError() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] X-BadKey=Hello Good Sir @@ -172,6 +179,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testSectionStartingWithXDashAndBadKeysDoNotCauseError() { // Fixture Setup + // language="unit file (systemd)" val file = """ [X-Service] BadKey=Hello Good Sir @@ -190,6 +198,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testKeyFromInstallSectionThrowsWarningInUnitSection() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Unit] Alias=Hello Good Sir @@ -213,6 +222,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testKeyFromUnitSectionThrowsWarningInInstallSection() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Install] Requires=Hello Good Sir @@ -236,6 +246,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testKeyFromInstallSectionThrowsWarningInServiceSection() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] Alias=Hello Good Sir @@ -259,6 +270,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testKeyFromUnitSectionThrowsWarningInServiceSection() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] Requires=Hello Good Sir @@ -282,6 +294,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testKeyFromResourceControlManPageDoesNotThrowWarningInServiceSection() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] CPUAccounting=on @@ -298,6 +311,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testKeyFromExecManPageDoesNotThrowWarningInServiceSection() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] DynamicUser=yes @@ -314,6 +328,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testAutomountFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Automount] Where=yes @@ -330,6 +345,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testAutomountFileTypeThrowsWarningWithKeyFromServiceFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Automount] BusName=yes @@ -351,6 +367,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testDeviceFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Unit] Description=SomeUnit @@ -367,6 +384,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testDeviceFileTypeThrowsWarningWithKeyFromServiceFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Unit] BusName=yes @@ -388,6 +406,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testMountFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Mount] SloppyOptions=true @@ -404,6 +423,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testMountFileTypeThrowsWarningWithKeyFromServiceFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Mount] BusName=yes @@ -425,6 +445,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testPathFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Path] MakeDirectory=true @@ -441,6 +462,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testPathFileTypeThrowsWarningWithKeyFromServiceFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Path] BusName=yes @@ -462,6 +484,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testServiceFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] BusName=true @@ -478,6 +501,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testServiceFileTypeThrowsWarningWithKeyFromPathFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] MakeDirectory=yes @@ -499,6 +523,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testSliceFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Slice] CPUAccounting=true @@ -515,6 +540,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testSliceFileTypeThrowsWarningWithKeyFromUnitSection() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Slice] Description=yes @@ -536,6 +562,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testSocketFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Socket] Backlog=5 @@ -552,6 +579,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testSocketFileTypeThrowsWarningWithKeyFromServiceFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Socket] BusName=yes @@ -573,6 +601,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testSwapFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Swap] Priority=5 @@ -589,6 +618,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testSwapFileTypeThrowsWarningWithKeyFromServiceFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Swap] BusName=yes @@ -610,6 +640,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testTargetFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Unit] Description=SomeUnit @@ -626,6 +657,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testTargetFileTypeThrowsWarningWithKeyFromServiceFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Unit] BusName=yes @@ -647,6 +679,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testTimerFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Timer] RandomizedDelaySec=50 @@ -663,6 +696,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testTimerFileTypeThrowsWarningWithKeyFromServiceFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Timer] BusName=yes @@ -684,6 +718,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testNSpawnFileTypeThrowsWarningWithKeyFromServiceFile() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Exec] BusName=yes @@ -703,8 +738,10 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { TestCase.assertEquals("BusName", highlightElement!!.text) } + // I think this test was not finished fun testNSpawnFileTypeHasNoWarningsWithKnownKey() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Timer] RandomizedDelaySec=50 @@ -719,9 +756,188 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { assertEmpty(highlights) } + fun testNetDevShowsNoWarnings() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [NetDev] + Name=wg0 + Kind=wireguard + + [WireGuard] + PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong= + ListenPort=51820 + + [WireGuardPeer] + PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= + AllowedIPs=fd31:bf08:57cb::/48,192.168.26.0/24 + Endpoint=wireguard.example.com:51820 + """.trimIndent() + enableInspection(UnknownKeyInSectionInspection::class.java) + setupFileInEditor("some.netdev", file) + + // Exercise SUT + val highlights = myFixture.doHighlighting() + + // Verification + assertEmpty(highlights) + } + + fun testNetDevShowsWarningWithUnknownKey() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [NetDev] + Name=wg0 + Kind=wireguard + + [WireGuard] + TLSEndpoint=tcp://64.45.45.12 + + [WireGuardPeer] + PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= + AllowedIPs=fd31:bf08:57cb::/48,192.168.26.0/24 + Endpoint=wireguard.example.com:51820 + """.trimIndent() + enableInspection(UnknownKeyInSectionInspection::class.java) + setupFileInEditor("some.netdev", file) + + // Exercise SUT + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(1, highlights) + val info = highlights[0] + TestCase.assertEquals(UnknownKeyInSectionInspection.INSPECTION_TOOL_TIP_TEXT, info!!.description) + TestCase.assertEquals(HighlightInfoType.WARNING, info.type) + val highlightElement = myFixture.file.findElementAt(info.getStartOffset()) + TestCase.assertEquals("TLSEndpoint", highlightElement!!.text) + } + + fun testNetworkFileShowsNoWarnings() { + // Fixture Setup + // language="unit file (systemd)" + enableInspection(UnknownKeyInSectionInspection::class.java) + val file = """ + # /etc/systemd/network/25-bridge-static.network + [Match] + Name=bridge0 + + [Network] + Address=192.168.0.15/24 + Gateway=192.168.0.1 + DNS=192.168.0.1 + """.trimIndent() + setupFileInEditor("some.network", file) + + // Exercise SUT + val highlights = myFixture.doHighlighting() + + // Verification + assertEmpty(highlights) + } + + fun testNetworkShowsWarningWithUnknownKey() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + # /etc/systemd/network/25-bridge-static.network + [Match] + NameAlias=bridge0 + + [Network] + Address=192.168.0.15/24 + Gateway=192.168.0.1 + DNS=192.168.0.1 + """.trimIndent() + enableInspection(UnknownKeyInSectionInspection::class.java) + setupFileInEditor("some.network", file) + + // Exercise SUT + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(1, highlights) + val info = highlights[0] + TestCase.assertEquals(UnknownKeyInSectionInspection.INSPECTION_TOOL_TIP_TEXT, info!!.description) + TestCase.assertEquals(HighlightInfoType.WARNING, info.type) + val highlightElement = myFixture.file.findElementAt(info.getStartOffset()) + TestCase.assertEquals("NameAlias", highlightElement!!.text) + } + + + fun testLinkFileShowsNoWarnings() { + // Fixture Setup + // language="unit file (systemd)" + enableInspection(UnknownKeyInSectionInspection::class.java) + val file = """ + [Match] + MACAddress=12:34:56:78:9a:bc + Driver=brcmsmac + Path=pci-0000:02:00.0-* + Type=wlan + Virtualization=no + Host=my-laptop + Architecture=x86-64 + + [Link] + Name=wireless0 + MTUBytes=1450 + BitsPerSecond=10M + WakeOnLan=magic + MACAddress=cb:a9:87:65:43:21 + """.trimIndent() + setupFileInEditor("some.link", file) + + // Exercise SUT + val highlights = myFixture.doHighlighting() + + // Verification + assertEmpty(highlights) + } + + fun testLinkShowsWarningWithUnknownKey() { + // Fixture Setup + // language="unit file (systemd)" + val file = """ + [Match] + MACAddress=12:34:56:78:9a:bc + Driver=brcmsmac + Path=pci-0000:02:00.0-* + Type=wlan + Virtualization=no + VirtualizationKind=qemu + Host=my-laptop + Architecture=x86-64 + + [Link] + Name=wireless0 + MTUBytes=1450 + BitsPerSecond=10M + WakeOnLan=magic + MACAddress=cb:a9:87:65:43:21 + """.trimIndent() + enableInspection(UnknownKeyInSectionInspection::class.java) + setupFileInEditor("some.link", file) + + // Exercise SUT + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(1, highlights) + val info = highlights[0] + TestCase.assertEquals(UnknownKeyInSectionInspection.INSPECTION_TOOL_TIP_TEXT, info!!.description) + TestCase.assertEquals(HighlightInfoType.WARNING, info.type) + val highlightElement = myFixture.file.findElementAt(info.getStartOffset()) + TestCase.assertEquals("VirtualizationKind", highlightElement!!.text) + } + + + fun testSomeNewKeysFromSystemdV240HasNoWarnings() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Unit] FailureActionExitStatus=249 @@ -742,6 +958,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testSomeNewKeysFromSystemdV246HasNoWarnings() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] CoredumpFilter=bar @@ -759,6 +976,7 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { fun testThatMovedKeysFromServiceToUnitInSystemd229ThrowNoWarnings() { // Fixture Setup + // language="unit file (systemd)" val file = """ [Service] StartLimitBurst=24 diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataDocumentationCompletionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataDocumentationCompletionTest.kt index 2d3b9571..4eead76d 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataDocumentationCompletionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataDocumentationCompletionTest.kt @@ -4,7 +4,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest import java.util.* class SemanticDataDocumentationCompletionTest : AbstractUnitFileTest() { - fun testAllOptions() { + fun testAllOptionsHaveDocumentation() { val sdr = SemanticDataRepository.instance val doc: MutableSet = TreeSet() @@ -33,6 +33,14 @@ class SemanticDataDocumentationCompletionTest : AbstractUnitFileTest() { docButNotCode.removeAll(code) System.err.println("***** (Code but not Doc) *****") + for(secKey in codeButNotDoc) { + + val arr = secKey.split(".") + + val aliases = sdr.getAliasesForSectionKey(arr[0], arr[1] + "." + arr[2]) + + System.out.println("Test ${arr[0]} => ${arr[1]}.${arr[2]} => ${aliases}") + } assertEmpty("Expected that everything in the code was in the documentation, but we are missing the following:", codeButNotDoc) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/OptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/OptionValueTest.kt index d80f580e..fbf54967 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/OptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/OptionValueTest.kt @@ -35,7 +35,7 @@ class OptionValueTest : AbstractUnitFileTest() { println("Missing:$totalMissingValidators") println("Found:$totalFoundValidators") - val allowed = 521 + val allowed = 1183 if (totalMissingValidators >= allowed) { assertEquals("Number of missing validators is too high at ${totalMissingValidators} > $allowed vs. found ${totalFoundValidators}", sortedList, "") } diff --git a/systemd-build/Systemd-Builder.Dockerfile b/systemd-build/Systemd-Builder.Dockerfile index 6800da63..735ca695 100644 --- a/systemd-build/Systemd-Builder.Dockerfile +++ b/systemd-build/Systemd-Builder.Dockerfile @@ -21,6 +21,7 @@ ENV BUILDDATE ${BUILDDATE:-notset} RUN git pull # https://github.com/systemd/systemd/commit/8442ac9c0264ac7beb5afd6c3bf922030a6edaf3 +# TODO I think this can be removed we had to upgrade to ubuntu 24.04 and this was something with an old version of the build tool in ubuntu 22.04 RUN find . -type f -name meson.build -exec sed -i 's/install_emptydir(\(.*\), install_tag : .*)/install_emptydir(\1)/g' '{}' '+' RUN meson setup build diff --git a/systemd-build/systemd-build.sh b/systemd-build/systemd-build.sh index fc3fe1c6..9b3904ea 100755 --- a/systemd-build/systemd-build.sh +++ b/systemd-build/systemd-build.sh @@ -21,6 +21,7 @@ echo "Git Pull" && \ cp ./src/network/networkd-network-gperf.gperf /mount/ && \ cp ./src/network/networkd-gperf.gperf /mount/ && \ cp -R ./man /mount/ && \ + /usr/bin/meson --internal exe --capture /mount/man/ethtool-link-mode.xml -- /usr/bin/python3 ./src/shared/ethtool-link-mode.py --xml 'cc -E' ./src/basic/linux/ethtool.h && \ git log --format="%at" | sort | tail -n 1 | xargs -I{} date -d @{} +%Y-%m-%d > last_commit_date && \ git rev-parse --short=10 HEAD > last_commit_hash && \ cp last_commit_date last_commit_hash /mount/ && \