You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ConfigTest.java 54KB

Merge branch 'stable-5.1' into stable-5.2 * stable-5.1: Fix OpenSshConfigTest#config FileSnapshot: fix bug with timestamp thresholding In LockFile#waitForStatChange wait in units of file time resolution Cache FileStoreAttributeCache per directory Fix FileSnapshot#save(long) and FileSnapshot#save(Instant) Persist minimal racy threshold and allow manual configuration Measure minimum racy interval to auto-configure FileSnapshot Reuse FileUtils to recursively delete files created by tests Fix FileAttributeCache.toString() Add test for racy git detection in FileSnapshot Repeat RefDirectoryTest.testGetRef_DiscoversModifiedLoose 100 times Fix org.eclipse.jdt.core.prefs of org.eclipse.jgit.junit Add missing javadoc in org.eclipse.jgit.junit Enhance RepeatRule to report number of failures at the end Fix FileSnapshotTests for filesystem with high timestamp resolution Retry deleting test files in FileBasedConfigTest Measure filesystem timestamp resolution already in test setup Refactor FileSnapshotTest to use NIO APIs Measure stored timestamp resolution instead of time to touch file Handle CancellationException in FileStoreAttributeCache Fix FileSnapshot#saveNoConfig Use Instant for smudge time in DirCache and DirCacheEntry Use Instant instead of milliseconds for filesystem timestamp handling Workaround SecurityException in FS#getFsTimestampResolution Fix NPE in FS$FileStoreAttributeCache.getFsTimestampResolution FS: ignore AccessDeniedException when measuring timestamp resolution Add debug trace for FileSnapshot Use FileChannel.open to touch file and set mtime to now Persist filesystem timestamp resolution and allow manual configuration Increase bazel timeout for long running tests Bazel: Fix lint warning flagged by buildifier Update bazlets to latest version Bazel: Add missing dependencies for ArchiveCommandTest Bazel: Remove FileTreeIteratorWithTimeControl from BUILD file Add support for nanoseconds and microseconds for Config#getTimeUnit Optionally measure filesystem timestamp resolution asynchronously Delete unused FileTreeIteratorWithTimeControl FileSnapshot#equals: consider UNKNOWN_SIZE Timeout measuring file timestamp resolution after 2 seconds Fix RacyGitTests#testRacyGitDetection Change RacyGitTests to create a racy git situation in a stable way Deprecate Constants.CHARACTER_ENCODING in favor of StandardCharsets.UTF_8 Fix non-deterministic hash of archives created by ArchiveCommand Update Maven plugins ecj, plexus, error-prone Update Maven plugins and cleanup Maven warnings Make inner classes static where possible Fix API problem filters Change-Id: Ia57385b2a60f48a5317c8d723721c235d7043a84 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
4 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
Config: Rewrite subsection and value escaping and parsing Previously, Config was using the same method for both escaping and parsing subsection names and config values. The goal was presumably code savings, but unfortunately, these two pieces of the git config format are simply different. In git v2.15.1, Documentation/config.txt says the following about subsection names: "Subsection names are case sensitive and can contain any characters except newline (doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection." And, later in the same documentation section, about values: "A line that defines a value can be continued to the next line by ending it with a `\`; the backquote and the end-of-line are stripped. Leading whitespaces after 'name =', the remainder of the line after the first comment character '#' or ';', and trailing whitespaces of the line are discarded unless they are enclosed in double quotes. Internal whitespaces within the value are retained verbatim. Inside double quotes, double quote `"` and backslash `\` characters must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid." The main important differences are that subsection names have a limited set of supported escape sequences, and do not support newlines at all, either escaped or unescaped. Arguably, it would be easy to support escaped newlines, but C git simply does not: $ git config -f foo.config $'foo.bar\nbaz.quux' value error: invalid key (newline): foo.bar baz.quux I468106ac was an attempt to fix one bug in escapeValue, around leading whitespace, without having to rewrite the whole escaping/parsing code. Unfortunately, because escapeValue was used for escaping subsection names as well, this made it possible to write invalid config files, any time Config#toText is called with a subsection name with trailing whitespace, like {foo }. Rather than pile hacks on top of hacks, fix it for real by largely rewriting the escaping and parsing code. In addition to fixing escape sequences, fix (and write tests for) a few more issues in the old implementation: * Now that we can properly parse it, always emit newlines as "\n" from escapeValue, rather than the weird (but still supported) syntax with a non-quoted trailing literal "\n\" before the newline. In addition to producing more readable output and matching the behavior of C git, this makes the escaping code much simpler. * Disallow '\0' entirely within both subsection names and values, since due to Unix command line argument conventions it is impossible to pass such values to "git config". * Properly preserve intra-value whitespace when parsing, rather than collapsing it all to a single space. Change-Id: I304f626b9d0ad1592c4e4e449a11b136c0f8b3e3
6 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537
  1. /*
  2. * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3. * Copyright (C) 2009-2010, Google Inc.
  4. * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  5. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  6. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  7. * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com> and others
  8. *
  9. * This program and the accompanying materials are made available under the
  10. * terms of the Eclipse Distribution License v. 1.0 which is available at
  11. * https://www.eclipse.org/org/documents/edl-v10.php.
  12. *
  13. * SPDX-License-Identifier: BSD-3-Clause
  14. */
  15. package org.eclipse.jgit.lib;
  16. import static java.nio.charset.StandardCharsets.UTF_8;
  17. import static java.util.concurrent.TimeUnit.DAYS;
  18. import static java.util.concurrent.TimeUnit.HOURS;
  19. import static java.util.concurrent.TimeUnit.MICROSECONDS;
  20. import static java.util.concurrent.TimeUnit.MILLISECONDS;
  21. import static java.util.concurrent.TimeUnit.MINUTES;
  22. import static java.util.concurrent.TimeUnit.NANOSECONDS;
  23. import static java.util.concurrent.TimeUnit.SECONDS;
  24. import static org.eclipse.jgit.util.FileUtils.pathToString;
  25. import static org.junit.Assert.assertArrayEquals;
  26. import static org.junit.Assert.assertEquals;
  27. import static org.junit.Assert.assertFalse;
  28. import static org.junit.Assert.assertNull;
  29. import static org.junit.Assert.assertSame;
  30. import static org.junit.Assert.assertThrows;
  31. import static org.junit.Assert.assertTrue;
  32. import static org.junit.Assert.fail;
  33. import java.io.File;
  34. import java.io.IOException;
  35. import java.nio.file.Files;
  36. import java.text.MessageFormat;
  37. import java.util.ArrayList;
  38. import java.util.Arrays;
  39. import java.util.Collections;
  40. import java.util.Iterator;
  41. import java.util.LinkedList;
  42. import java.util.List;
  43. import java.util.Set;
  44. import java.util.concurrent.TimeUnit;
  45. import java.util.function.Consumer;
  46. import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
  47. import org.eclipse.jgit.errors.ConfigInvalidException;
  48. import org.eclipse.jgit.internal.JGitText;
  49. import org.eclipse.jgit.junit.MockSystemReader;
  50. import org.eclipse.jgit.merge.MergeConfig;
  51. import org.eclipse.jgit.storage.file.FileBasedConfig;
  52. import org.eclipse.jgit.transport.RefSpec;
  53. import org.eclipse.jgit.util.FS;
  54. import org.eclipse.jgit.util.SystemReader;
  55. import org.junit.After;
  56. import org.junit.Rule;
  57. import org.junit.Test;
  58. import org.junit.rules.TemporaryFolder;
  59. /**
  60. * Test reading of git config
  61. */
  62. @SuppressWarnings("boxing")
  63. public class ConfigTest {
  64. // A non-ASCII whitespace character: U+2002 EN QUAD.
  65. private static final char WS = '\u2002';
  66. private static final String REFS_ORIGIN = "+refs/heads/*:refs/remotes/origin/*";
  67. private static final String REFS_UPSTREAM = "+refs/heads/*:refs/remotes/upstream/*";
  68. private static final String REFS_BACKUP = "+refs/heads/*:refs/remotes/backup/*";
  69. @Rule
  70. public TemporaryFolder tmp = new TemporaryFolder();
  71. @After
  72. public void tearDown() {
  73. SystemReader.setInstance(null);
  74. }
  75. @Test
  76. public void test001_ReadBareKey() throws ConfigInvalidException {
  77. final Config c = parse("[foo]\nbar\n");
  78. assertTrue(c.getBoolean("foo", null, "bar", false));
  79. assertEquals("", c.getString("foo", null, "bar"));
  80. }
  81. @Test
  82. public void test002_ReadWithSubsection() throws ConfigInvalidException {
  83. final Config c = parse("[foo \"zip\"]\nbar\n[foo \"zap\"]\nbar=false\nn=3\n");
  84. assertTrue(c.getBoolean("foo", "zip", "bar", false));
  85. assertEquals("", c.getString("foo","zip", "bar"));
  86. assertFalse(c.getBoolean("foo", "zap", "bar", true));
  87. assertEquals("false", c.getString("foo", "zap", "bar"));
  88. assertEquals(3, c.getInt("foo", "zap", "n", 4));
  89. assertEquals(4, c.getInt("foo", "zap","m", 4));
  90. }
  91. @Test
  92. public void test003_PutRemote() {
  93. final Config c = new Config();
  94. c.setString("sec", "ext", "name", "value");
  95. c.setString("sec", "ext", "name2", "value2");
  96. final String expText = "[sec \"ext\"]\n\tname = value\n\tname2 = value2\n";
  97. assertEquals(expText, c.toText());
  98. }
  99. @Test
  100. public void test004_PutGetSimple() {
  101. Config c = new Config();
  102. c.setString("my", null, "somename", "false");
  103. assertEquals("false", c.getString("my", null, "somename"));
  104. assertEquals("[my]\n\tsomename = false\n", c.toText());
  105. }
  106. @Test
  107. public void test005_PutGetStringList() {
  108. Config c = new Config();
  109. final LinkedList<String> values = new LinkedList<>();
  110. values.add("value1");
  111. values.add("value2");
  112. c.setStringList("my", null, "somename", values);
  113. final Object[] expArr = values.toArray();
  114. final String[] actArr = c.getStringList("my", null, "somename");
  115. assertArrayEquals(expArr, actArr);
  116. final String expText = "[my]\n\tsomename = value1\n\tsomename = value2\n";
  117. assertEquals(expText, c.toText());
  118. }
  119. @Test
  120. public void test006_readCaseInsensitive() throws ConfigInvalidException {
  121. final Config c = parse("[Foo]\nBar\n");
  122. assertTrue(c.getBoolean("foo", null, "bar", false));
  123. assertEquals("", c.getString("foo", null, "bar"));
  124. }
  125. @Test
  126. public void test007_readUserConfig() {
  127. final MockSystemReader mockSystemReader = new MockSystemReader();
  128. SystemReader.setInstance(mockSystemReader);
  129. final String hostname = mockSystemReader.getHostname();
  130. final Config userGitConfig = mockSystemReader.openUserConfig(null,
  131. FS.DETECTED);
  132. final Config localConfig = new Config(userGitConfig);
  133. mockSystemReader.clearProperties();
  134. String authorName;
  135. String authorEmail;
  136. // no values defined nowhere
  137. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  138. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  139. assertEquals(Constants.UNKNOWN_USER_DEFAULT, authorName);
  140. assertEquals(Constants.UNKNOWN_USER_DEFAULT + "@" + hostname, authorEmail);
  141. assertTrue(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  142. assertTrue(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  143. // the system user name is defined
  144. mockSystemReader.setProperty(Constants.OS_USER_NAME_KEY, "os user name");
  145. localConfig.uncache(UserConfig.KEY);
  146. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  147. assertEquals("os user name", authorName);
  148. assertTrue(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  149. if (hostname != null && hostname.length() != 0) {
  150. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  151. assertEquals("os user name@" + hostname, authorEmail);
  152. }
  153. assertTrue(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  154. // the git environment variables are defined
  155. mockSystemReader.setProperty(Constants.GIT_AUTHOR_NAME_KEY, "git author name");
  156. mockSystemReader.setProperty(Constants.GIT_AUTHOR_EMAIL_KEY, "author@email");
  157. localConfig.uncache(UserConfig.KEY);
  158. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  159. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  160. assertEquals("git author name", authorName);
  161. assertEquals("author@email", authorEmail);
  162. assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  163. assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  164. // the values are defined in the global configuration
  165. // first clear environment variables since they would override
  166. // configuration files
  167. mockSystemReader.clearProperties();
  168. userGitConfig.setString("user", null, "name", "global username");
  169. userGitConfig.setString("user", null, "email", "author@globalemail");
  170. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  171. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  172. assertEquals("global username", authorName);
  173. assertEquals("author@globalemail", authorEmail);
  174. assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  175. assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  176. // the values are defined in the local configuration
  177. localConfig.setString("user", null, "name", "local username");
  178. localConfig.setString("user", null, "email", "author@localemail");
  179. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  180. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  181. assertEquals("local username", authorName);
  182. assertEquals("author@localemail", authorEmail);
  183. assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  184. assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  185. authorName = localConfig.get(UserConfig.KEY).getCommitterName();
  186. authorEmail = localConfig.get(UserConfig.KEY).getCommitterEmail();
  187. assertEquals("local username", authorName);
  188. assertEquals("author@localemail", authorEmail);
  189. assertFalse(localConfig.get(UserConfig.KEY).isCommitterNameImplicit());
  190. assertFalse(localConfig.get(UserConfig.KEY).isCommitterEmailImplicit());
  191. // also git environment variables are defined
  192. mockSystemReader.setProperty(Constants.GIT_AUTHOR_NAME_KEY,
  193. "git author name");
  194. mockSystemReader.setProperty(Constants.GIT_AUTHOR_EMAIL_KEY,
  195. "author@email");
  196. localConfig.setString("user", null, "name", "local username");
  197. localConfig.setString("user", null, "email", "author@localemail");
  198. authorName = localConfig.get(UserConfig.KEY).getAuthorName();
  199. authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail();
  200. assertEquals("git author name", authorName);
  201. assertEquals("author@email", authorEmail);
  202. assertFalse(localConfig.get(UserConfig.KEY).isAuthorNameImplicit());
  203. assertFalse(localConfig.get(UserConfig.KEY).isAuthorEmailImplicit());
  204. }
  205. @Test
  206. public void testReadUserConfigWithInvalidCharactersStripped() {
  207. final MockSystemReader mockSystemReader = new MockSystemReader();
  208. final Config localConfig = new Config(mockSystemReader.openUserConfig(
  209. null, FS.DETECTED));
  210. localConfig.setString("user", null, "name", "foo<bar");
  211. localConfig.setString("user", null, "email", "baz>\nqux@example.com");
  212. UserConfig userConfig = localConfig.get(UserConfig.KEY);
  213. assertEquals("foobar", userConfig.getAuthorName());
  214. assertEquals("bazqux@example.com", userConfig.getAuthorEmail());
  215. }
  216. @Test
  217. public void testReadBoolean_TrueFalse1() throws ConfigInvalidException {
  218. final Config c = parse("[s]\na = true\nb = false\n");
  219. assertEquals("true", c.getString("s", null, "a"));
  220. assertEquals("false", c.getString("s", null, "b"));
  221. assertTrue(c.getBoolean("s", "a", false));
  222. assertFalse(c.getBoolean("s", "b", true));
  223. }
  224. @Test
  225. public void testReadBoolean_TrueFalse2() throws ConfigInvalidException {
  226. final Config c = parse("[s]\na = TrUe\nb = fAlSe\n");
  227. assertEquals("TrUe", c.getString("s", null, "a"));
  228. assertEquals("fAlSe", c.getString("s", null, "b"));
  229. assertTrue(c.getBoolean("s", "a", false));
  230. assertFalse(c.getBoolean("s", "b", true));
  231. }
  232. @Test
  233. public void testReadBoolean_YesNo1() throws ConfigInvalidException {
  234. final Config c = parse("[s]\na = yes\nb = no\n");
  235. assertEquals("yes", c.getString("s", null, "a"));
  236. assertEquals("no", c.getString("s", null, "b"));
  237. assertTrue(c.getBoolean("s", "a", false));
  238. assertFalse(c.getBoolean("s", "b", true));
  239. }
  240. @Test
  241. public void testReadBoolean_YesNo2() throws ConfigInvalidException {
  242. final Config c = parse("[s]\na = yEs\nb = NO\n");
  243. assertEquals("yEs", c.getString("s", null, "a"));
  244. assertEquals("NO", c.getString("s", null, "b"));
  245. assertTrue(c.getBoolean("s", "a", false));
  246. assertFalse(c.getBoolean("s", "b", true));
  247. }
  248. @Test
  249. public void testReadBoolean_OnOff1() throws ConfigInvalidException {
  250. final Config c = parse("[s]\na = on\nb = off\n");
  251. assertEquals("on", c.getString("s", null, "a"));
  252. assertEquals("off", c.getString("s", null, "b"));
  253. assertTrue(c.getBoolean("s", "a", false));
  254. assertFalse(c.getBoolean("s", "b", true));
  255. }
  256. @Test
  257. public void testReadBoolean_OnOff2() throws ConfigInvalidException {
  258. final Config c = parse("[s]\na = ON\nb = OFF\n");
  259. assertEquals("ON", c.getString("s", null, "a"));
  260. assertEquals("OFF", c.getString("s", null, "b"));
  261. assertTrue(c.getBoolean("s", "a", false));
  262. assertFalse(c.getBoolean("s", "b", true));
  263. }
  264. static enum TestEnum {
  265. ONE_TWO;
  266. }
  267. @Test
  268. public void testGetEnum() throws ConfigInvalidException {
  269. Config c = parse("[s]\na = ON\nb = input\nc = true\nd = off\n");
  270. assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "a",
  271. CoreConfig.AutoCRLF.FALSE));
  272. assertSame(CoreConfig.AutoCRLF.INPUT, c.getEnum("s", null, "b",
  273. CoreConfig.AutoCRLF.FALSE));
  274. assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "c",
  275. CoreConfig.AutoCRLF.FALSE));
  276. assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
  277. CoreConfig.AutoCRLF.TRUE));
  278. c = new Config();
  279. assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
  280. CoreConfig.AutoCRLF.FALSE));
  281. c = parse("[s \"b\"]\n\tc = one two\n");
  282. assertSame(TestEnum.ONE_TWO, c.getEnum("s", "b", "c", TestEnum.ONE_TWO));
  283. c = parse("[s \"b\"]\n\tc = one-two\n");
  284. assertSame(TestEnum.ONE_TWO, c.getEnum("s", "b", "c", TestEnum.ONE_TWO));
  285. }
  286. @Test
  287. public void testGetInvalidEnum() throws ConfigInvalidException {
  288. Config c = parse("[a]\n\tb = invalid\n");
  289. try {
  290. c.getEnum("a", null, "b", TestEnum.ONE_TWO);
  291. fail();
  292. } catch (IllegalArgumentException e) {
  293. assertEquals("Invalid value: a.b=invalid", e.getMessage());
  294. }
  295. c = parse("[a \"b\"]\n\tc = invalid\n");
  296. try {
  297. c.getEnum("a", "b", "c", TestEnum.ONE_TWO);
  298. fail();
  299. } catch (IllegalArgumentException e) {
  300. assertEquals("Invalid value: a.b.c=invalid", e.getMessage());
  301. }
  302. }
  303. @Test
  304. public void testSetEnum() {
  305. final Config c = new Config();
  306. c.setEnum("s", "b", "c", TestEnum.ONE_TWO);
  307. assertEquals("[s \"b\"]\n\tc = one two\n", c.toText());
  308. }
  309. @Test
  310. public void testGetFastForwardMergeoptions() throws ConfigInvalidException {
  311. Config c = new Config(null); // not set
  312. assertSame(FastForwardMode.FF, c.getEnum(
  313. ConfigConstants.CONFIG_BRANCH_SECTION, "side",
  314. ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF));
  315. MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
  316. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  317. c = parse("[branch \"side\"]\n\tmergeoptions = --ff-only\n");
  318. assertSame(FastForwardMode.FF_ONLY, c.getEnum(
  319. ConfigConstants.CONFIG_BRANCH_SECTION, "side",
  320. ConfigConstants.CONFIG_KEY_MERGEOPTIONS,
  321. FastForwardMode.FF_ONLY));
  322. mergeConfig = c.get(MergeConfig.getParser("side"));
  323. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  324. c = parse("[branch \"side\"]\n\tmergeoptions = --ff\n");
  325. assertSame(FastForwardMode.FF, c.getEnum(
  326. ConfigConstants.CONFIG_BRANCH_SECTION, "side",
  327. ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.FF));
  328. mergeConfig = c.get(MergeConfig.getParser("side"));
  329. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  330. c = parse("[branch \"side\"]\n\tmergeoptions = --no-ff\n");
  331. assertSame(FastForwardMode.NO_FF, c.getEnum(
  332. ConfigConstants.CONFIG_BRANCH_SECTION, "side",
  333. ConfigConstants.CONFIG_KEY_MERGEOPTIONS, FastForwardMode.NO_FF));
  334. mergeConfig = c.get(MergeConfig.getParser("side"));
  335. assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode());
  336. }
  337. @Test
  338. public void testSetFastForwardMergeoptions() {
  339. final Config c = new Config();
  340. c.setEnum("branch", "side", "mergeoptions", FastForwardMode.FF);
  341. assertEquals("[branch \"side\"]\n\tmergeoptions = --ff\n", c.toText());
  342. c.setEnum("branch", "side", "mergeoptions", FastForwardMode.FF_ONLY);
  343. assertEquals("[branch \"side\"]\n\tmergeoptions = --ff-only\n",
  344. c.toText());
  345. c.setEnum("branch", "side", "mergeoptions", FastForwardMode.NO_FF);
  346. assertEquals("[branch \"side\"]\n\tmergeoptions = --no-ff\n",
  347. c.toText());
  348. }
  349. @Test
  350. public void testGetFastForwardMerge() throws ConfigInvalidException {
  351. Config c = new Config(null); // not set
  352. assertSame(FastForwardMode.Merge.TRUE, c.getEnum(
  353. ConfigConstants.CONFIG_KEY_MERGE, null,
  354. ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE));
  355. MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
  356. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  357. c = parse("[merge]\n\tff = only\n");
  358. assertSame(FastForwardMode.Merge.ONLY, c.getEnum(
  359. ConfigConstants.CONFIG_KEY_MERGE, null,
  360. ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.ONLY));
  361. mergeConfig = c.get(MergeConfig.getParser("side"));
  362. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  363. c = parse("[merge]\n\tff = true\n");
  364. assertSame(FastForwardMode.Merge.TRUE, c.getEnum(
  365. ConfigConstants.CONFIG_KEY_MERGE, null,
  366. ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.TRUE));
  367. mergeConfig = c.get(MergeConfig.getParser("side"));
  368. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  369. c = parse("[merge]\n\tff = false\n");
  370. assertSame(FastForwardMode.Merge.FALSE, c.getEnum(
  371. ConfigConstants.CONFIG_KEY_MERGE, null,
  372. ConfigConstants.CONFIG_KEY_FF, FastForwardMode.Merge.FALSE));
  373. mergeConfig = c.get(MergeConfig.getParser("side"));
  374. assertSame(FastForwardMode.NO_FF, mergeConfig.getFastForwardMode());
  375. }
  376. @Test
  377. public void testCombinedMergeOptions() throws ConfigInvalidException {
  378. Config c = new Config(null); // not set
  379. MergeConfig mergeConfig = c.get(MergeConfig.getParser("side"));
  380. assertSame(FastForwardMode.FF, mergeConfig.getFastForwardMode());
  381. assertTrue(mergeConfig.isCommit());
  382. assertFalse(mergeConfig.isSquash());
  383. // branch..mergeoptions should win over merge.ff
  384. c = parse("[merge]\n\tff = false\n"
  385. + "[branch \"side\"]\n\tmergeoptions = --ff-only\n");
  386. mergeConfig = c.get(MergeConfig.getParser("side"));
  387. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  388. assertTrue(mergeConfig.isCommit());
  389. assertFalse(mergeConfig.isSquash());
  390. // merge.ff used for ff setting if not set via mergeoptions
  391. c = parse("[merge]\n\tff = only\n"
  392. + "[branch \"side\"]\n\tmergeoptions = --squash\n");
  393. mergeConfig = c.get(MergeConfig.getParser("side"));
  394. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  395. assertTrue(mergeConfig.isCommit());
  396. assertTrue(mergeConfig.isSquash());
  397. // mergeoptions wins if it has ff options amongst other options
  398. c = parse("[merge]\n\tff = false\n"
  399. + "[branch \"side\"]\n\tmergeoptions = --ff-only --no-commit\n");
  400. mergeConfig = c.get(MergeConfig.getParser("side"));
  401. assertSame(FastForwardMode.FF_ONLY, mergeConfig.getFastForwardMode());
  402. assertFalse(mergeConfig.isCommit());
  403. assertFalse(mergeConfig.isSquash());
  404. }
  405. @Test
  406. public void testSetFastForwardMerge() {
  407. final Config c = new Config();
  408. c.setEnum("merge", null, "ff",
  409. FastForwardMode.Merge.valueOf(FastForwardMode.FF));
  410. assertEquals("[merge]\n\tff = true\n", c.toText());
  411. c.setEnum("merge", null, "ff",
  412. FastForwardMode.Merge.valueOf(FastForwardMode.FF_ONLY));
  413. assertEquals("[merge]\n\tff = only\n", c.toText());
  414. c.setEnum("merge", null, "ff",
  415. FastForwardMode.Merge.valueOf(FastForwardMode.NO_FF));
  416. assertEquals("[merge]\n\tff = false\n", c.toText());
  417. }
  418. @Test
  419. public void testReadLong() throws ConfigInvalidException {
  420. assertReadLong(1L);
  421. assertReadLong(-1L);
  422. assertReadLong(Long.MIN_VALUE);
  423. assertReadLong(Long.MAX_VALUE);
  424. assertReadLong(4L * 1024 * 1024 * 1024, "4g");
  425. assertReadLong(3L * 1024 * 1024, "3 m");
  426. assertReadLong(8L * 1024, "8 k");
  427. try {
  428. assertReadLong(-1, "1.5g");
  429. fail("incorrectly accepted 1.5g");
  430. } catch (IllegalArgumentException e) {
  431. assertEquals("Invalid integer value: s.a=1.5g", e.getMessage());
  432. }
  433. }
  434. @Test
  435. public void testBooleanWithNoValue() throws ConfigInvalidException {
  436. Config c = parse("[my]\n\tempty\n");
  437. assertEquals("", c.getString("my", null, "empty"));
  438. assertEquals(1, c.getStringList("my", null, "empty").length);
  439. assertEquals("", c.getStringList("my", null, "empty")[0]);
  440. assertTrue(c.getBoolean("my", "empty", false));
  441. assertEquals("[my]\n\tempty\n", c.toText());
  442. }
  443. @Test
  444. public void testUnsetBranchSection() throws ConfigInvalidException {
  445. Config c = parse("" //
  446. + "[branch \"keep\"]\n"
  447. + " merge = master.branch.to.keep.in.the.file\n"
  448. + "\n"
  449. + "[branch \"remove\"]\n"
  450. + " merge = this.will.get.deleted\n"
  451. + " remote = origin-for-some-long-gone-place\n"
  452. + "\n"
  453. + "[core-section-not-to-remove-in-test]\n"
  454. + " packedGitLimit = 14\n");
  455. c.unsetSection("branch", "does.not.exist");
  456. c.unsetSection("branch", "remove");
  457. assertEquals("" //
  458. + "[branch \"keep\"]\n"
  459. + " merge = master.branch.to.keep.in.the.file\n"
  460. + "\n"
  461. + "[core-section-not-to-remove-in-test]\n"
  462. + " packedGitLimit = 14\n", c.toText());
  463. }
  464. @Test
  465. public void testUnsetSingleSection() throws ConfigInvalidException {
  466. Config c = parse("" //
  467. + "[branch \"keep\"]\n"
  468. + " merge = master.branch.to.keep.in.the.file\n"
  469. + "\n"
  470. + "[single]\n"
  471. + " merge = this.will.get.deleted\n"
  472. + " remote = origin-for-some-long-gone-place\n"
  473. + "\n"
  474. + "[core-section-not-to-remove-in-test]\n"
  475. + " packedGitLimit = 14\n");
  476. c.unsetSection("single", null);
  477. assertEquals("" //
  478. + "[branch \"keep\"]\n"
  479. + " merge = master.branch.to.keep.in.the.file\n"
  480. + "\n"
  481. + "[core-section-not-to-remove-in-test]\n"
  482. + " packedGitLimit = 14\n", c.toText());
  483. }
  484. @Test
  485. public void test008_readSectionNames() throws ConfigInvalidException {
  486. final Config c = parse("[a]\n [B]\n");
  487. Set<String> sections = c.getSections();
  488. assertTrue("Sections should contain \"a\"", sections.contains("a"));
  489. assertTrue("Sections should contain \"b\"", sections.contains("b"));
  490. }
  491. @Test
  492. public void test009_readNamesInSection() throws ConfigInvalidException {
  493. String configString = "[core]\n" + "repositoryFormatVersion = 0\n"
  494. + "filemode = false\n" + "logAllRefUpdates = true\n";
  495. final Config c = parse(configString);
  496. Set<String> names = c.getNames("core");
  497. assertEquals("Core section size", 3, names.size());
  498. assertTrue("Core section should contain \"filemode\"", names
  499. .contains("filemode"));
  500. assertTrue("Core section should contain \"repositoryFormatVersion\"",
  501. names.contains("repositoryFormatVersion"));
  502. assertTrue("Core section should contain \"repositoryformatversion\"",
  503. names.contains("repositoryformatversion"));
  504. Iterator<String> itr = names.iterator();
  505. assertEquals("filemode", itr.next());
  506. assertEquals("logAllRefUpdates", itr.next());
  507. assertEquals("repositoryFormatVersion", itr.next());
  508. assertFalse(itr.hasNext());
  509. }
  510. @Test
  511. public void test_ReadNamesInSectionRecursive()
  512. throws ConfigInvalidException {
  513. String baseConfigString = "[core]\n" + "logAllRefUpdates = true\n";
  514. String configString = "[core]\n" + "repositoryFormatVersion = 0\n"
  515. + "filemode = false\n";
  516. final Config c = parse(configString, parse(baseConfigString));
  517. Set<String> names = c.getNames("core", true);
  518. assertEquals("Core section size", 3, names.size());
  519. assertTrue("Core section should contain \"filemode\"",
  520. names.contains("filemode"));
  521. assertTrue("Core section should contain \"repositoryFormatVersion\"",
  522. names.contains("repositoryFormatVersion"));
  523. assertTrue("Core section should contain \"logAllRefUpdates\"",
  524. names.contains("logAllRefUpdates"));
  525. assertTrue("Core section should contain \"logallrefupdates\"",
  526. names.contains("logallrefupdates"));
  527. Iterator<String> itr = names.iterator();
  528. assertEquals("filemode", itr.next());
  529. assertEquals("repositoryFormatVersion", itr.next());
  530. assertEquals("logAllRefUpdates", itr.next());
  531. assertFalse(itr.hasNext());
  532. }
  533. @Test
  534. public void test010_readNamesInSubSection() throws ConfigInvalidException {
  535. String configString = "[a \"sub1\"]\n"//
  536. + "x = 0\n" //
  537. + "y = false\n"//
  538. + "z = true\n"//
  539. + "[a \"sub2\"]\n"//
  540. + "a=0\n"//
  541. + "b=1\n";
  542. final Config c = parse(configString);
  543. Set<String> names = c.getNames("a", "sub1");
  544. assertEquals("Subsection size", 3, names.size());
  545. assertTrue("Subsection should contain \"x\"", names.contains("x"));
  546. assertTrue("Subsection should contain \"y\"", names.contains("y"));
  547. assertTrue("Subsection should contain \"z\"", names.contains("z"));
  548. names = c.getNames("a", "sub2");
  549. assertEquals("Subsection size", 2, names.size());
  550. assertTrue("Subsection should contain \"a\"", names.contains("a"));
  551. assertTrue("Subsection should contain \"b\"", names.contains("b"));
  552. }
  553. @Test
  554. public void readNamesInSubSectionRecursive() throws ConfigInvalidException {
  555. String baseConfigString = "[a \"sub1\"]\n"//
  556. + "x = 0\n" //
  557. + "y = false\n"//
  558. + "[a \"sub2\"]\n"//
  559. + "A=0\n";//
  560. String configString = "[a \"sub1\"]\n"//
  561. + "z = true\n"//
  562. + "[a \"sub2\"]\n"//
  563. + "B=1\n";
  564. final Config c = parse(configString, parse(baseConfigString));
  565. Set<String> names = c.getNames("a", "sub1", true);
  566. assertEquals("Subsection size", 3, names.size());
  567. assertTrue("Subsection should contain \"x\"", names.contains("x"));
  568. assertTrue("Subsection should contain \"y\"", names.contains("y"));
  569. assertTrue("Subsection should contain \"z\"", names.contains("z"));
  570. names = c.getNames("a", "sub2", true);
  571. assertEquals("Subsection size", 2, names.size());
  572. assertTrue("Subsection should contain \"A\"", names.contains("A"));
  573. assertTrue("Subsection should contain \"a\"", names.contains("a"));
  574. assertTrue("Subsection should contain \"B\"", names.contains("B"));
  575. }
  576. @Test
  577. public void testNoFinalNewline() throws ConfigInvalidException {
  578. Config c = parse("[a]\n"
  579. + "x = 0\n"
  580. + "y = 1");
  581. assertEquals("0", c.getString("a", null, "x"));
  582. assertEquals("1", c.getString("a", null, "y"));
  583. }
  584. @Test
  585. public void testExplicitlySetEmptyString() throws Exception {
  586. Config c = new Config();
  587. c.setString("a", null, "x", "0");
  588. c.setString("a", null, "y", "");
  589. assertEquals("0", c.getString("a", null, "x"));
  590. assertEquals(0, c.getInt("a", null, "x", 1));
  591. assertEquals("", c.getString("a", null, "y"));
  592. assertArrayEquals(new String[]{""}, c.getStringList("a", null, "y"));
  593. assertEquals(1, c.getInt("a", null, "y", 1));
  594. assertNull(c.getString("a", null, "z"));
  595. assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
  596. }
  597. @Test
  598. public void testParsedEmptyString() throws Exception {
  599. Config c = parse("[a]\n"
  600. + "x = 0\n"
  601. + "y =\n");
  602. assertEquals("0", c.getString("a", null, "x"));
  603. assertEquals(0, c.getInt("a", null, "x", 1));
  604. assertNull(c.getString("a", null, "y"));
  605. assertArrayEquals(new String[]{null}, c.getStringList("a", null, "y"));
  606. assertEquals(1, c.getInt("a", null, "y", 1));
  607. assertNull(c.getString("a", null, "z"));
  608. assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
  609. }
  610. @Test
  611. public void testSetStringListWithEmptyValue() throws Exception {
  612. Config c = new Config();
  613. c.setStringList("a", null, "x", Arrays.asList(""));
  614. assertArrayEquals(new String[]{""}, c.getStringList("a", null, "x"));
  615. }
  616. @Test
  617. public void testEmptyValueAtEof() throws Exception {
  618. String text = "[a]\nx =";
  619. Config c = parse(text);
  620. assertNull(c.getString("a", null, "x"));
  621. assertArrayEquals(new String[]{null},
  622. c.getStringList("a", null, "x"));
  623. c = parse(text + "\n");
  624. assertNull(c.getString("a", null, "x"));
  625. assertArrayEquals(new String[]{null},
  626. c.getStringList("a", null, "x"));
  627. }
  628. @Test
  629. public void testReadMultipleValuesForName() throws ConfigInvalidException {
  630. Config c = parse("[foo]\nbar=false\nbar=true\n");
  631. assertTrue(c.getBoolean("foo", "bar", false));
  632. }
  633. @Test
  634. public void testIncludeInvalidName() {
  635. assertThrows(JGitText.get().invalidLineInConfigFile,
  636. ConfigInvalidException.class, () -> parse("[include]\nbar\n"));
  637. }
  638. @Test
  639. public void testIncludeNoValue() {
  640. assertThrows(JGitText.get().invalidLineInConfigFile,
  641. ConfigInvalidException.class, () -> parse("[include]\npath\n"));
  642. }
  643. @Test
  644. public void testIncludeEmptyValue() {
  645. assertThrows(JGitText.get().invalidLineInConfigFile,
  646. ConfigInvalidException.class,
  647. () -> parse("[include]\npath=\n"));
  648. }
  649. @Test
  650. public void testIncludeValuePathNotFound() throws ConfigInvalidException {
  651. // we do not expect an exception, included path not found are ignored
  652. String notFound = "/not/found";
  653. Config parsed = parse("[include]\npath=" + notFound + "\n");
  654. assertEquals(1, parsed.getSections().size());
  655. assertEquals(notFound, parsed.getString("include", null, "path"));
  656. }
  657. @Test
  658. public void testIncludeValuePathWithTilde() throws ConfigInvalidException {
  659. // we do not expect an exception, included path not supported are
  660. // ignored
  661. String notSupported = "~/someFile";
  662. Config parsed = parse("[include]\npath=" + notSupported + "\n");
  663. assertEquals(1, parsed.getSections().size());
  664. assertEquals(notSupported, parsed.getString("include", null, "path"));
  665. }
  666. @Test
  667. public void testIncludeValuePathRelative() throws ConfigInvalidException {
  668. // we do not expect an exception, included path not supported are
  669. // ignored
  670. String notSupported = "someRelativeFile";
  671. Config parsed = parse("[include]\npath=" + notSupported + "\n");
  672. assertEquals(1, parsed.getSections().size());
  673. assertEquals(notSupported, parsed.getString("include", null, "path"));
  674. }
  675. @Test
  676. public void testIncludeTooManyRecursions() throws IOException {
  677. File config = tmp.newFile("config");
  678. String include = "[include]\npath=" + pathToString(config) + "\n";
  679. Files.write(config.toPath(), include.getBytes(UTF_8));
  680. try {
  681. loadConfig(config);
  682. fail();
  683. } catch (ConfigInvalidException cie) {
  684. for (Throwable t = cie; t != null; t = t.getCause()) {
  685. if (t.getMessage()
  686. .equals(JGitText.get().tooManyIncludeRecursions)) {
  687. return;
  688. }
  689. }
  690. fail("Expected to find expected exception message: "
  691. + JGitText.get().tooManyIncludeRecursions);
  692. }
  693. }
  694. @Test
  695. public void testIncludeIsNoop() throws IOException, ConfigInvalidException {
  696. File config = tmp.newFile("config");
  697. String fooBar = "[foo]\nbar=true\n";
  698. Files.write(config.toPath(), fooBar.getBytes(UTF_8));
  699. Config parsed = parse("[include]\npath=" + pathToString(config) + "\n");
  700. assertFalse(parsed.getBoolean("foo", "bar", false));
  701. }
  702. @Test
  703. public void testIncludeCaseInsensitiveSection()
  704. throws IOException, ConfigInvalidException {
  705. File included = tmp.newFile("included");
  706. String content = "[foo]\nbar=true\n";
  707. Files.write(included.toPath(), content.getBytes(UTF_8));
  708. File config = tmp.newFile("config");
  709. content = "[Include]\npath=" + pathToString(included) + "\n";
  710. Files.write(config.toPath(), content.getBytes(UTF_8));
  711. FileBasedConfig fbConfig = loadConfig(config);
  712. assertTrue(fbConfig.getBoolean("foo", "bar", false));
  713. }
  714. @Test
  715. public void testIncludeCaseInsensitiveKey()
  716. throws IOException, ConfigInvalidException {
  717. File included = tmp.newFile("included");
  718. String content = "[foo]\nbar=true\n";
  719. Files.write(included.toPath(), content.getBytes(UTF_8));
  720. File config = tmp.newFile("config");
  721. content = "[include]\nPath=" + pathToString(included) + "\n";
  722. Files.write(config.toPath(), content.getBytes(UTF_8));
  723. FileBasedConfig fbConfig = loadConfig(config);
  724. assertTrue(fbConfig.getBoolean("foo", "bar", false));
  725. }
  726. @Test
  727. public void testIncludeExceptionContainsLine() {
  728. try {
  729. parse("[include]\npath=\n");
  730. fail("Expected ConfigInvalidException");
  731. } catch (ConfigInvalidException e) {
  732. assertTrue(
  733. "Expected to find the problem line in the exception message",
  734. e.getMessage().contains("include.path"));
  735. }
  736. }
  737. @Test
  738. public void testIncludeExceptionContainsFile() throws IOException {
  739. File included = tmp.newFile("included");
  740. String includedPath = pathToString(included);
  741. String content = "[include]\npath=\n";
  742. Files.write(included.toPath(), content.getBytes(UTF_8));
  743. File config = tmp.newFile("config");
  744. String include = "[include]\npath=" + includedPath + "\n";
  745. Files.write(config.toPath(), include.getBytes(UTF_8));
  746. try {
  747. loadConfig(config);
  748. fail("Expected ConfigInvalidException");
  749. } catch (ConfigInvalidException e) {
  750. // Check that there is some exception in the chain that contains
  751. // includedPath
  752. for (Throwable t = e; t != null; t = t.getCause()) {
  753. if (t.getMessage().contains(includedPath)) {
  754. return;
  755. }
  756. }
  757. fail("Expected to find the path in the exception message: "
  758. + includedPath);
  759. }
  760. }
  761. @Test
  762. public void testIncludeSetValueMustNotTouchIncludedLines1()
  763. throws IOException, ConfigInvalidException {
  764. File includedFile = createAllTypesIncludedContent();
  765. File configFile = tmp.newFile("config");
  766. String content = createAllTypesSampleContent("Alice Parker", false, 11,
  767. 21, 31, CoreConfig.AutoCRLF.FALSE,
  768. "+refs/heads/*:refs/remotes/origin/*") + "\n[include]\npath="
  769. + pathToString(includedFile);
  770. Files.write(configFile.toPath(), content.getBytes(UTF_8));
  771. FileBasedConfig fbConfig = loadConfig(configFile);
  772. assertValuesAsIncluded(fbConfig, REFS_ORIGIN, REFS_UPSTREAM);
  773. assertSections(fbConfig, "user", "core", "remote", "include");
  774. setAllValuesNew(fbConfig);
  775. assertValuesAsIsSaveLoad(fbConfig, config -> {
  776. assertValuesAsIncluded(config, REFS_BACKUP, REFS_UPSTREAM);
  777. assertSections(fbConfig, "user", "core", "remote", "include");
  778. });
  779. }
  780. @Test
  781. public void testIncludeSetValueMustNotTouchIncludedLines2()
  782. throws IOException, ConfigInvalidException {
  783. File includedFile = createAllTypesIncludedContent();
  784. File configFile = tmp.newFile("config");
  785. String content = "[include]\npath=" + pathToString(includedFile) + "\n"
  786. + createAllTypesSampleContent("Alice Parker", false, 11, 21, 31,
  787. CoreConfig.AutoCRLF.FALSE,
  788. "+refs/heads/*:refs/remotes/origin/*");
  789. Files.write(configFile.toPath(), content.getBytes(UTF_8));
  790. FileBasedConfig fbConfig = loadConfig(configFile);
  791. assertValuesAsConfig(fbConfig, REFS_UPSTREAM, REFS_ORIGIN);
  792. assertSections(fbConfig, "include", "user", "core", "remote");
  793. setAllValuesNew(fbConfig);
  794. assertValuesAsIsSaveLoad(fbConfig, config -> {
  795. assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP);
  796. assertSections(fbConfig, "include", "user", "core", "remote");
  797. });
  798. }
  799. @Test
  800. public void testIncludeSetValueOnFileWithJustContainsInclude()
  801. throws IOException, ConfigInvalidException {
  802. File includedFile = createAllTypesIncludedContent();
  803. File configFile = tmp.newFile("config");
  804. String content = "[include]\npath=" + pathToString(includedFile);
  805. Files.write(configFile.toPath(), content.getBytes(UTF_8));
  806. FileBasedConfig fbConfig = loadConfig(configFile);
  807. assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
  808. assertSections(fbConfig, "include", "user", "core", "remote");
  809. setAllValuesNew(fbConfig);
  810. assertValuesAsIsSaveLoad(fbConfig, config -> {
  811. assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP);
  812. assertSections(fbConfig, "include", "user", "core", "remote");
  813. });
  814. }
  815. @Test
  816. public void testIncludeSetValueOnFileWithJustEmptySection1()
  817. throws IOException, ConfigInvalidException {
  818. File includedFile = createAllTypesIncludedContent();
  819. File configFile = tmp.newFile("config");
  820. String content = "[user]\n[include]\npath="
  821. + pathToString(includedFile);
  822. Files.write(configFile.toPath(), content.getBytes(UTF_8));
  823. FileBasedConfig fbConfig = loadConfig(configFile);
  824. assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
  825. assertSections(fbConfig, "user", "include", "core", "remote");
  826. setAllValuesNew(fbConfig);
  827. assertValuesAsIsSaveLoad(fbConfig, config -> {
  828. assertValuesAsNewWithName(config, "Alice Muller", REFS_UPSTREAM,
  829. REFS_BACKUP);
  830. assertSections(fbConfig, "user", "include", "core", "remote");
  831. });
  832. }
  833. @Test
  834. public void testIncludeSetValueOnFileWithJustEmptySection2()
  835. throws IOException, ConfigInvalidException {
  836. File includedFile = createAllTypesIncludedContent();
  837. File configFile = tmp.newFile("config");
  838. String content = "[include]\npath=" + pathToString(includedFile)
  839. + "\n[user]";
  840. Files.write(configFile.toPath(), content.getBytes(UTF_8));
  841. FileBasedConfig fbConfig = loadConfig(configFile);
  842. assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
  843. assertSections(fbConfig, "include", "user", "core", "remote");
  844. setAllValuesNew(fbConfig);
  845. assertValuesAsIsSaveLoad(fbConfig, config -> {
  846. assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP);
  847. assertSections(fbConfig, "include", "user", "core", "remote");
  848. });
  849. }
  850. @Test
  851. public void testIncludeSetValueOnFileWithJustExistingSection1()
  852. throws IOException, ConfigInvalidException {
  853. File includedFile = createAllTypesIncludedContent();
  854. File configFile = tmp.newFile("config");
  855. String content = "[user]\nemail=alice@home\n[include]\npath="
  856. + pathToString(includedFile);
  857. Files.write(configFile.toPath(), content.getBytes(UTF_8));
  858. FileBasedConfig fbConfig = loadConfig(configFile);
  859. assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
  860. assertSections(fbConfig, "user", "include", "core", "remote");
  861. setAllValuesNew(fbConfig);
  862. assertValuesAsIsSaveLoad(fbConfig, config -> {
  863. assertValuesAsNewWithName(config, "Alice Muller", REFS_UPSTREAM,
  864. REFS_BACKUP);
  865. assertSections(fbConfig, "user", "include", "core", "remote");
  866. });
  867. }
  868. @Test
  869. public void testIncludeSetValueOnFileWithJustExistingSection2()
  870. throws IOException, ConfigInvalidException {
  871. File includedFile = createAllTypesIncludedContent();
  872. File configFile = tmp.newFile("config");
  873. String content = "[include]\npath=" + pathToString(includedFile)
  874. + "\n[user]\nemail=alice@home\n";
  875. Files.write(configFile.toPath(), content.getBytes(UTF_8));
  876. FileBasedConfig fbConfig = loadConfig(configFile);
  877. assertValuesAsIncluded(fbConfig, REFS_UPSTREAM);
  878. assertSections(fbConfig, "include", "user", "core", "remote");
  879. setAllValuesNew(fbConfig);
  880. assertValuesAsIsSaveLoad(fbConfig, config -> {
  881. assertValuesAsNew(config, REFS_UPSTREAM, REFS_BACKUP);
  882. assertSections(fbConfig, "include", "user", "core", "remote");
  883. });
  884. }
  885. @Test
  886. public void testIncludeUnsetSectionMustNotTouchIncludedLines()
  887. throws IOException, ConfigInvalidException {
  888. File includedFile = tmp.newFile("included");
  889. RefSpec includedRefSpec = new RefSpec(REFS_UPSTREAM);
  890. String includedContent = "[remote \"origin\"]\n" + "fetch="
  891. + includedRefSpec;
  892. Files.write(includedFile.toPath(), includedContent.getBytes(UTF_8));
  893. File configFile = tmp.newFile("config");
  894. RefSpec refSpec = new RefSpec(REFS_ORIGIN);
  895. String content = "[include]\npath=" + pathToString(includedFile) + "\n"
  896. + "[remote \"origin\"]\n" + "fetch=" + refSpec;
  897. Files.write(configFile.toPath(), content.getBytes(UTF_8));
  898. FileBasedConfig fbConfig = loadConfig(configFile);
  899. Consumer<FileBasedConfig> assertion = config -> {
  900. assertEquals(Arrays.asList(includedRefSpec, refSpec),
  901. config.getRefSpecs("remote", "origin", "fetch"));
  902. };
  903. assertion.accept(fbConfig);
  904. fbConfig.unsetSection("remote", "origin");
  905. assertValuesAsIsSaveLoad(fbConfig, config -> {
  906. assertEquals(Collections.singletonList(includedRefSpec),
  907. config.getRefSpecs("remote", "origin", "fetch"));
  908. });
  909. }
  910. private File createAllTypesIncludedContent() throws IOException {
  911. File includedFile = tmp.newFile("included");
  912. String includedContent = createAllTypesSampleContent("Alice Muller",
  913. true, 10, 20, 30, CoreConfig.AutoCRLF.TRUE,
  914. "+refs/heads/*:refs/remotes/upstream/*");
  915. Files.write(includedFile.toPath(), includedContent.getBytes(UTF_8));
  916. return includedFile;
  917. }
  918. private static void assertValuesAsIsSaveLoad(FileBasedConfig fbConfig,
  919. Consumer<FileBasedConfig> assertion)
  920. throws IOException, ConfigInvalidException {
  921. assertion.accept(fbConfig);
  922. fbConfig.save();
  923. assertion.accept(fbConfig);
  924. fbConfig = loadConfig(fbConfig.getFile());
  925. assertion.accept(fbConfig);
  926. }
  927. private static void setAllValuesNew(Config config) {
  928. config.setString("user", null, "name", "Alice Bauer");
  929. config.setBoolean("core", null, "fileMode", false);
  930. config.setInt("core", null, "deltaBaseCacheLimit", 12);
  931. config.setLong("core", null, "packedGitLimit", 22);
  932. config.setLong("core", null, "repositoryCacheExpireAfter", 32);
  933. config.setEnum("core", null, "autocrlf", CoreConfig.AutoCRLF.FALSE);
  934. config.setString("remote", "origin", "fetch",
  935. "+refs/heads/*:refs/remotes/backup/*");
  936. }
  937. private static void assertValuesAsIncluded(Config config, String... refs) {
  938. assertAllTypesSampleContent("Alice Muller", true, 10, 20, 30,
  939. CoreConfig.AutoCRLF.TRUE, config, refs);
  940. }
  941. private static void assertValuesAsConfig(Config config, String... refs) {
  942. assertAllTypesSampleContent("Alice Parker", false, 11, 21, 31,
  943. CoreConfig.AutoCRLF.FALSE, config, refs);
  944. }
  945. private static void assertValuesAsNew(Config config, String... refs) {
  946. assertValuesAsNewWithName(config, "Alice Bauer", refs);
  947. }
  948. private static void assertValuesAsNewWithName(Config config, String name,
  949. String... refs) {
  950. assertAllTypesSampleContent(name, false, 12, 22, 32,
  951. CoreConfig.AutoCRLF.FALSE, config, refs);
  952. }
  953. private static void assertSections(Config config, String... sections) {
  954. assertEquals(Arrays.asList(sections),
  955. new ArrayList<>(config.getSections()));
  956. }
  957. private static String createAllTypesSampleContent(String name,
  958. boolean fileMode, int deltaBaseCacheLimit, long packedGitLimit,
  959. long repositoryCacheExpireAfter, CoreConfig.AutoCRLF autoCRLF,
  960. String fetchRefSpec) {
  961. final StringBuilder builder = new StringBuilder();
  962. builder.append("[user]\n");
  963. builder.append("name=");
  964. builder.append(name);
  965. builder.append("\n");
  966. builder.append("[core]\n");
  967. builder.append("fileMode=");
  968. builder.append(fileMode);
  969. builder.append("\n");
  970. builder.append("deltaBaseCacheLimit=");
  971. builder.append(deltaBaseCacheLimit);
  972. builder.append("\n");
  973. builder.append("packedGitLimit=");
  974. builder.append(packedGitLimit);
  975. builder.append("\n");
  976. builder.append("repositoryCacheExpireAfter=");
  977. builder.append(repositoryCacheExpireAfter);
  978. builder.append("\n");
  979. builder.append("autocrlf=");
  980. builder.append(autoCRLF.name());
  981. builder.append("\n");
  982. builder.append("[remote \"origin\"]\n");
  983. builder.append("fetch=");
  984. builder.append(fetchRefSpec);
  985. builder.append("\n");
  986. return builder.toString();
  987. }
  988. private static void assertAllTypesSampleContent(String name,
  989. boolean fileMode, int deltaBaseCacheLimit, long packedGitLimit,
  990. long repositoryCacheExpireAfter, CoreConfig.AutoCRLF autoCRLF,
  991. Config config, String... fetchRefSpecs) {
  992. assertEquals(name, config.getString("user", null, "name"));
  993. assertEquals(fileMode,
  994. config.getBoolean("core", "fileMode", !fileMode));
  995. assertEquals(deltaBaseCacheLimit,
  996. config.getInt("core", "deltaBaseCacheLimit", -1));
  997. assertEquals(packedGitLimit,
  998. config.getLong("core", "packedGitLimit", -1));
  999. assertEquals(repositoryCacheExpireAfter, config.getTimeUnit("core",
  1000. null, "repositoryCacheExpireAfter", -1, MILLISECONDS));
  1001. assertEquals(autoCRLF, config.getEnum("core", null, "autocrlf",
  1002. CoreConfig.AutoCRLF.INPUT));
  1003. final List<RefSpec> refspecs = new ArrayList<>();
  1004. for (String fetchRefSpec : fetchRefSpecs) {
  1005. refspecs.add(new RefSpec(fetchRefSpec));
  1006. }
  1007. assertEquals(refspecs, config.getRefSpecs("remote", "origin", "fetch"));
  1008. }
  1009. private static void assertReadLong(long exp) throws ConfigInvalidException {
  1010. assertReadLong(exp, String.valueOf(exp));
  1011. }
  1012. private static void assertReadLong(long exp, String act)
  1013. throws ConfigInvalidException {
  1014. final Config c = parse("[s]\na = " + act + "\n");
  1015. assertEquals(exp, c.getLong("s", null, "a", 0L));
  1016. }
  1017. private static Config parse(String content)
  1018. throws ConfigInvalidException {
  1019. return parse(content, null);
  1020. }
  1021. private static Config parse(String content, Config baseConfig)
  1022. throws ConfigInvalidException {
  1023. final Config c = new Config(baseConfig);
  1024. c.fromText(content);
  1025. return c;
  1026. }
  1027. @Test
  1028. public void testTimeUnit() throws ConfigInvalidException {
  1029. assertEquals(0, parseTime("0", NANOSECONDS));
  1030. assertEquals(2, parseTime("2ns", NANOSECONDS));
  1031. assertEquals(200, parseTime("200 nanoseconds", NANOSECONDS));
  1032. assertEquals(0, parseTime("0", MICROSECONDS));
  1033. assertEquals(2, parseTime("2us", MICROSECONDS));
  1034. assertEquals(2, parseTime("2000 nanoseconds", MICROSECONDS));
  1035. assertEquals(200, parseTime("200 microseconds", MICROSECONDS));
  1036. assertEquals(0, parseTime("0", MILLISECONDS));
  1037. assertEquals(2, parseTime("2ms", MILLISECONDS));
  1038. assertEquals(2, parseTime("2000microseconds", MILLISECONDS));
  1039. assertEquals(200, parseTime("200 milliseconds", MILLISECONDS));
  1040. assertEquals(0, parseTime("0s", SECONDS));
  1041. assertEquals(2, parseTime("2s", SECONDS));
  1042. assertEquals(231, parseTime("231sec", SECONDS));
  1043. assertEquals(1, parseTime("1second", SECONDS));
  1044. assertEquals(300, parseTime("300 seconds", SECONDS));
  1045. assertEquals(2, parseTime("2m", MINUTES));
  1046. assertEquals(2, parseTime("2min", MINUTES));
  1047. assertEquals(1, parseTime("1 minute", MINUTES));
  1048. assertEquals(10, parseTime("10 minutes", MINUTES));
  1049. assertEquals(5, parseTime("5h", HOURS));
  1050. assertEquals(5, parseTime("5hr", HOURS));
  1051. assertEquals(1, parseTime("1hour", HOURS));
  1052. assertEquals(48, parseTime("48hours", HOURS));
  1053. assertEquals(5, parseTime("5 h", HOURS));
  1054. assertEquals(5, parseTime("5 hr", HOURS));
  1055. assertEquals(1, parseTime("1 hour", HOURS));
  1056. assertEquals(48, parseTime("48 hours", HOURS));
  1057. assertEquals(48, parseTime("48 \t \r hours", HOURS));
  1058. assertEquals(4, parseTime("4d", DAYS));
  1059. assertEquals(1, parseTime("1day", DAYS));
  1060. assertEquals(14, parseTime("14days", DAYS));
  1061. assertEquals(7, parseTime("1w", DAYS));
  1062. assertEquals(7, parseTime("1week", DAYS));
  1063. assertEquals(14, parseTime("2w", DAYS));
  1064. assertEquals(14, parseTime("2weeks", DAYS));
  1065. assertEquals(30, parseTime("1mon", DAYS));
  1066. assertEquals(30, parseTime("1month", DAYS));
  1067. assertEquals(60, parseTime("2mon", DAYS));
  1068. assertEquals(60, parseTime("2months", DAYS));
  1069. assertEquals(365, parseTime("1y", DAYS));
  1070. assertEquals(365, parseTime("1year", DAYS));
  1071. assertEquals(365 * 2, parseTime("2years", DAYS));
  1072. }
  1073. private long parseTime(String value, TimeUnit unit)
  1074. throws ConfigInvalidException {
  1075. Config c = parse("[a]\na=" + value + "\n");
  1076. return c.getTimeUnit("a", null, "a", 0, unit);
  1077. }
  1078. @Test
  1079. public void testTimeUnitDefaultValue() throws ConfigInvalidException {
  1080. // value not present
  1081. assertEquals(20, parse("[a]\na=0\n").getTimeUnit("a", null, "b", 20,
  1082. MILLISECONDS));
  1083. // value is empty
  1084. assertEquals(20, parse("[a]\na=\" \"\n").getTimeUnit("a", null, "a", 20,
  1085. MILLISECONDS));
  1086. // value is not numeric
  1087. assertEquals(20, parse("[a]\na=test\n").getTimeUnit("a", null, "a", 20,
  1088. MILLISECONDS));
  1089. }
  1090. @Test
  1091. public void testTimeUnitInvalid() {
  1092. assertThrows("Invalid time unit value: a.a=1 monttthhh",
  1093. IllegalArgumentException.class,
  1094. () -> parseTime("1 monttthhh", DAYS));
  1095. }
  1096. @Test
  1097. public void testTimeUnitInvalidWithSection() throws ConfigInvalidException {
  1098. Config c = parse("[a \"b\"]\na=1 monttthhh\n");
  1099. assertThrows("Invalid time unit value: a.b.a=1 monttthhh",
  1100. IllegalArgumentException.class,
  1101. () -> c.getTimeUnit("a", "b", "a", 0, DAYS));
  1102. }
  1103. @Test
  1104. public void testTimeUnitNegative() {
  1105. assertThrows(IllegalArgumentException.class,
  1106. () -> parseTime("-1", MILLISECONDS));
  1107. }
  1108. @Test
  1109. public void testEscapeSpacesOnly() throws ConfigInvalidException {
  1110. // Empty string is read back as null, so this doesn't round-trip.
  1111. assertEquals("", Config.escapeValue(""));
  1112. assertValueRoundTrip(" ", "\" \"");
  1113. assertValueRoundTrip(" ", "\" \"");
  1114. }
  1115. @Test
  1116. public void testEscapeLeadingSpace() throws ConfigInvalidException {
  1117. assertValueRoundTrip("x", "x");
  1118. assertValueRoundTrip(" x", "\" x\"");
  1119. assertValueRoundTrip(" x", "\" x\"");
  1120. }
  1121. @Test
  1122. public void testEscapeTrailingSpace() throws ConfigInvalidException {
  1123. assertValueRoundTrip("x", "x");
  1124. assertValueRoundTrip("x ","\"x \"");
  1125. assertValueRoundTrip("x ","\"x \"");
  1126. }
  1127. @Test
  1128. public void testEscapeLeadingAndTrailingSpace()
  1129. throws ConfigInvalidException {
  1130. assertValueRoundTrip(" x ", "\" x \"");
  1131. assertValueRoundTrip(" x ", "\" x \"");
  1132. assertValueRoundTrip(" x ", "\" x \"");
  1133. assertValueRoundTrip(" x ", "\" x \"");
  1134. }
  1135. @Test
  1136. public void testNoEscapeInternalSpaces() throws ConfigInvalidException {
  1137. assertValueRoundTrip("x y");
  1138. assertValueRoundTrip("x y");
  1139. assertValueRoundTrip("x y");
  1140. assertValueRoundTrip("x y z");
  1141. assertValueRoundTrip("x " + WS + " y");
  1142. }
  1143. @Test
  1144. public void testNoEscapeSpecialCharacters() throws ConfigInvalidException {
  1145. assertValueRoundTrip("x\\y", "x\\\\y");
  1146. assertValueRoundTrip("x\"y", "x\\\"y");
  1147. assertValueRoundTrip("x\ny", "x\\ny");
  1148. assertValueRoundTrip("x\ty", "x\\ty");
  1149. assertValueRoundTrip("x\by", "x\\by");
  1150. }
  1151. @Test
  1152. public void testParseLiteralBackspace() throws ConfigInvalidException {
  1153. // This is round-tripped with an escape sequence by JGit, but C git writes
  1154. // it out as a literal backslash.
  1155. assertEquals("x\by", parseEscapedValue("x\by"));
  1156. }
  1157. @Test
  1158. public void testEscapeCommentCharacters() throws ConfigInvalidException {
  1159. assertValueRoundTrip("x#y", "\"x#y\"");
  1160. assertValueRoundTrip("x;y", "\"x;y\"");
  1161. }
  1162. @Test
  1163. public void testEscapeValueInvalidCharacters() {
  1164. assertIllegalArgumentException(() -> Config.escapeSubsection("x\0y"));
  1165. }
  1166. @Test
  1167. public void testEscapeSubsectionInvalidCharacters() {
  1168. assertIllegalArgumentException(() -> Config.escapeSubsection("x\ny"));
  1169. assertIllegalArgumentException(() -> Config.escapeSubsection("x\0y"));
  1170. }
  1171. @Test
  1172. public void testParseMultipleQuotedRegions() throws ConfigInvalidException {
  1173. assertEquals("b a z; \n", parseEscapedValue("b\" a\"\" z; \\n\""));
  1174. }
  1175. @Test
  1176. public void testParseComments() throws ConfigInvalidException {
  1177. assertEquals("baz", parseEscapedValue("baz; comment"));
  1178. assertEquals("baz", parseEscapedValue("baz# comment"));
  1179. assertEquals("baz", parseEscapedValue("baz ; comment"));
  1180. assertEquals("baz", parseEscapedValue("baz # comment"));
  1181. assertEquals("baz", parseEscapedValue("baz ; comment"));
  1182. assertEquals("baz", parseEscapedValue("baz # comment"));
  1183. assertEquals("baz", parseEscapedValue("baz " + WS + " ; comment"));
  1184. assertEquals("baz", parseEscapedValue("baz " + WS + " # comment"));
  1185. assertEquals("baz ", parseEscapedValue("\"baz \"; comment"));
  1186. assertEquals("baz ", parseEscapedValue("\"baz \"# comment"));
  1187. assertEquals("baz ", parseEscapedValue("\"baz \" ; comment"));
  1188. assertEquals("baz ", parseEscapedValue("\"baz \" # comment"));
  1189. }
  1190. @Test
  1191. public void testEscapeSubsection() throws ConfigInvalidException {
  1192. assertSubsectionRoundTrip("", "\"\"");
  1193. assertSubsectionRoundTrip("x", "\"x\"");
  1194. assertSubsectionRoundTrip(" x", "\" x\"");
  1195. assertSubsectionRoundTrip("x ", "\"x \"");
  1196. assertSubsectionRoundTrip(" x ", "\" x \"");
  1197. assertSubsectionRoundTrip("x y", "\"x y\"");
  1198. assertSubsectionRoundTrip("x y", "\"x y\"");
  1199. assertSubsectionRoundTrip("x\\y", "\"x\\\\y\"");
  1200. assertSubsectionRoundTrip("x\"y", "\"x\\\"y\"");
  1201. // Unlike for values, \b and \t are not escaped.
  1202. assertSubsectionRoundTrip("x\by", "\"x\by\"");
  1203. assertSubsectionRoundTrip("x\ty", "\"x\ty\"");
  1204. }
  1205. @Test
  1206. public void testParseInvalidValues() {
  1207. assertInvalidValue(JGitText.get().newlineInQuotesNotAllowed, "x\"\n\"y");
  1208. assertInvalidValue(JGitText.get().endOfFileInEscape, "x\\");
  1209. assertInvalidValue(
  1210. MessageFormat.format(JGitText.get().badEscape, 'q'), "x\\q");
  1211. }
  1212. @Test
  1213. public void testParseInvalidSubsections() {
  1214. assertInvalidSubsection(
  1215. JGitText.get().newlineInQuotesNotAllowed, "\"x\ny\"");
  1216. }
  1217. @Test
  1218. public void testDropBackslashFromInvalidEscapeSequenceInSubsectionName()
  1219. throws ConfigInvalidException {
  1220. assertEquals("x0", parseEscapedSubsection("\"x\\0\""));
  1221. assertEquals("xq", parseEscapedSubsection("\"x\\q\""));
  1222. // Unlike for values, \b, \n, and \t are not valid escape sequences.
  1223. assertEquals("xb", parseEscapedSubsection("\"x\\b\""));
  1224. assertEquals("xn", parseEscapedSubsection("\"x\\n\""));
  1225. assertEquals("xt", parseEscapedSubsection("\"x\\t\""));
  1226. }
  1227. @Test
  1228. public void testInvalidGroupHeader() {
  1229. assertThrows(JGitText.get().badGroupHeader,
  1230. ConfigInvalidException.class,
  1231. () -> parse("[foo \"bar\" ]\nfoo=bar\n"));
  1232. }
  1233. @Test
  1234. public void testCrLf() throws ConfigInvalidException {
  1235. assertEquals("true", parseEscapedValue("true\r\n"));
  1236. }
  1237. @Test
  1238. public void testLfContinuation() throws ConfigInvalidException {
  1239. assertEquals("true", parseEscapedValue("tr\\\nue"));
  1240. }
  1241. @Test
  1242. public void testCrCharContinuation() {
  1243. assertThrows("Bad escape: \\u000d", ConfigInvalidException.class,
  1244. () -> parseEscapedValue("tr\\\rue"));
  1245. }
  1246. @Test
  1247. public void testCrEOFContinuation() {
  1248. assertThrows("Bad escape: \\u000d", ConfigInvalidException.class,
  1249. () -> parseEscapedValue("tr\\\r"));
  1250. }
  1251. @Test
  1252. public void testCrLfContinuation() throws ConfigInvalidException {
  1253. assertEquals("true", parseEscapedValue("tr\\\r\nue"));
  1254. }
  1255. @Test
  1256. public void testWhitespaceContinuation() throws ConfigInvalidException {
  1257. assertEquals("tr ue", parseEscapedValue("tr \\\n ue"));
  1258. assertEquals("tr ue", parseEscapedValue("tr \\\r\n ue"));
  1259. }
  1260. private static void assertValueRoundTrip(String value)
  1261. throws ConfigInvalidException {
  1262. assertValueRoundTrip(value, value);
  1263. }
  1264. private static void assertValueRoundTrip(String value, String expectedEscaped)
  1265. throws ConfigInvalidException {
  1266. String escaped = Config.escapeValue(value);
  1267. assertEquals("escape failed;", expectedEscaped, escaped);
  1268. assertEquals("parse failed;", value, parseEscapedValue(escaped));
  1269. }
  1270. private static String parseEscapedValue(String escapedValue)
  1271. throws ConfigInvalidException {
  1272. String text = "[foo]\nbar=" + escapedValue;
  1273. Config c = parse(text);
  1274. return c.getString("foo", null, "bar");
  1275. }
  1276. private static void assertInvalidValue(String expectedMessage,
  1277. String escapedValue) {
  1278. try {
  1279. parseEscapedValue(escapedValue);
  1280. fail("expected ConfigInvalidException");
  1281. } catch (ConfigInvalidException e) {
  1282. assertEquals(expectedMessage, e.getMessage());
  1283. }
  1284. }
  1285. private static void assertSubsectionRoundTrip(String subsection,
  1286. String expectedEscaped) throws ConfigInvalidException {
  1287. String escaped = Config.escapeSubsection(subsection);
  1288. assertEquals("escape failed;", expectedEscaped, escaped);
  1289. assertEquals("parse failed;", subsection, parseEscapedSubsection(escaped));
  1290. }
  1291. private static String parseEscapedSubsection(String escapedSubsection)
  1292. throws ConfigInvalidException {
  1293. String text = "[foo " + escapedSubsection + "]\nbar = value";
  1294. Config c = parse(text);
  1295. Set<String> subsections = c.getSubsections("foo");
  1296. assertEquals("only one section", 1, subsections.size());
  1297. return subsections.iterator().next();
  1298. }
  1299. private static void assertIllegalArgumentException(Runnable r) {
  1300. try {
  1301. r.run();
  1302. fail("expected IllegalArgumentException");
  1303. } catch (IllegalArgumentException e) {
  1304. // Expected.
  1305. }
  1306. }
  1307. private static void assertInvalidSubsection(String expectedMessage,
  1308. String escapedSubsection) {
  1309. try {
  1310. parseEscapedSubsection(escapedSubsection);
  1311. fail("expected ConfigInvalidException");
  1312. } catch (ConfigInvalidException e) {
  1313. assertEquals(expectedMessage, e.getMessage());
  1314. }
  1315. }
  1316. private static FileBasedConfig loadConfig(File file)
  1317. throws IOException, ConfigInvalidException {
  1318. final FileBasedConfig config = new FileBasedConfig(null, file,
  1319. FS.DETECTED);
  1320. config.load();
  1321. return config;
  1322. }
  1323. }