Merge pull request #27871 from yuwata/udevadm-verify-downgrade-style-issues

udevadm-verify: downgrade log level about style issues
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2023-06-05 16:07:19 +02:00
committed by GitHub
6 changed files with 125 additions and 86 deletions
+8
View File
@@ -788,6 +788,14 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--no-style</option></term>
<listitem>
<para>Ignore style issues. When specified, even if style issues are found in udev rules files,
the exit status is <constant>0</constant> if no syntactic or semantic errors are found.</para>
</listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect2>
+1 -1
View File
@@ -64,7 +64,7 @@ _udevadm() {
[MONITOR_ARG]='-s --subsystem-match -t --tag-match'
[TEST]='-a --action -N --resolve-names'
[TEST_BUILTIN]='-a --action'
[VERIFY]='-N --resolve-names --root --no-summary'
[VERIFY]='-N --resolve-names --root --no-summary --no-style'
[WAIT]='-t --timeout --initialized=no --removed --settle'
[LOCK]='-t --timeout -d --device -b --backing -p --print'
)
+1
View File
@@ -110,6 +110,7 @@ _udevadm_verify(){
{-N+,--resolve-names=}'[When to resolve names.]:resolve:(early never)' \
'--root=[Operate on catalog hierarchy under specified directory]:directories:_directories' \
{--no-summary}'[Do not show summary.]' \
{--no-style}'[Ignore style issues.]' \
{-h,--help}'[Print help text.]' \
'*::files:_files'
}
+20 -20
View File
@@ -304,7 +304,7 @@ static void log_unknown_owner(sd_device *dev, UdevRuleLine *line, int error, con
if (IN_SET(abs(error), ENOENT, ESRCH))
log_udev_rule_internal(dev, line->rule_file, line->line_number, LOG_ERR, error,
"Unknown %s '%s', ignoring", entity, name);
"Unknown %s '%s', ignoring.", entity, name);
else
log_udev_rule_internal(dev, line->rule_file, line->line_number, LOG_ERR, error,
"Failed to resolve %s '%s', ignoring: %m", entity, name);
@@ -844,7 +844,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
cmd = udev_builtin_lookup(value);
if (cmd >= 0) {
log_line_debug(rule_line, "Found builtin command '%s' for %s, replacing attribute", value, key);
log_line_debug(rule_line, "Found builtin command '%s' for %s, replacing attribute.", value, key);
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
} else
r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL);
@@ -940,7 +940,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
check_value_format_and_warn(rule_line, key, value, true);
r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL);
} else {
log_line_debug(rule_line, "User name resolution is disabled, ignoring %s=%s", key, value);
log_line_debug(rule_line, "User name resolution is disabled, ignoring %s=\"%s\".", key, value);
return 0;
}
} else if (streq(key, "GROUP")) {
@@ -968,7 +968,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
check_value_format_and_warn(rule_line, key, value, true);
r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL);
} else {
log_line_debug(rule_line, "Resolving group name is disabled, ignoring GROUP=\"%s\"", value);
log_line_debug(rule_line, "Resolving group name is disabled, ignoring GROUP=\"%s\".", value);
return 0;
}
} else if (streq(key, "MODE")) {
@@ -1013,7 +1013,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
cmd = udev_builtin_lookup(value);
if (cmd < 0)
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
"Unknown builtin command '%s', ignoring", value);
"Unknown builtin command '%s', ignoring.", value);
r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
} else
return log_line_invalid_attr(rule_line, key);
@@ -1043,7 +1043,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
SET_FLAG(rule_line->type, LINE_HAS_LABEL, true);
return 1;
} else
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL), "Invalid key '%s'", key);
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL), "Invalid key '%s'.", key);
if (r < 0)
return log_oom();
@@ -1091,26 +1091,26 @@ static void check_token_delimiters(UdevRuleLine *rule_line, const char *line) {
if (line == rule_line->line) {
/* this is the first token of the rule */
if (n_comma > 0)
log_line_warning(rule_line, "Stray leading comma.");
log_line_notice(rule_line, "style: stray leading comma.");
} else if (isempty(p)) {
/* there are no more tokens in the rule */
if (n_comma > 0)
log_line_warning(rule_line, "Stray trailing comma.");
log_line_notice(rule_line, "style: stray trailing comma.");
} else {
/* single comma is expected */
if (n_comma == 0)
log_line_warning(rule_line, "A comma between tokens is expected.");
log_line_notice(rule_line, "style: a comma between tokens is expected.");
else if (n_comma > 1)
log_line_warning(rule_line, "More than one comma between tokens.");
log_line_notice(rule_line, "style: more than one comma between tokens.");
/* whitespace after comma is expected */
if (n_comma > 0) {
if (ws_before_comma)
log_line_warning(rule_line, "Stray whitespace before comma.");
log_line_notice(rule_line, "style: stray whitespace before comma.");
if (!ws_after_comma)
log_line_warning(rule_line, "Whitespace after comma is expected.");
log_line_notice(rule_line, "style: whitespace after comma is expected.");
} else if (!ws_before_comma && !ws_after_comma)
log_line_warning(rule_line, "Whitespace between tokens is expected.");
log_line_notice(rule_line, "style: whitespace between tokens is expected.");
}
}
@@ -1274,14 +1274,14 @@ static void rule_resolve_goto(UdevRuleFile *rule_file) {
}
if (!line->goto_line) {
log_line_error(line, "GOTO=\"%s\" has no matching label, ignoring",
log_line_error(line, "GOTO=\"%s\" has no matching label, ignoring.",
line->goto_label);
SET_FLAG(line->type, LINE_HAS_GOTO, false);
line->goto_label = NULL;
if ((line->type & ~(LINE_HAS_LABEL|LINE_IS_REFERENCED)) == 0) {
log_line_notice(line, "The line has no effect any more, dropping.");
log_line_warning(line, "The line has no effect any more, dropping.");
/* LINE_IS_REFERENCED implies LINE_HAS_LABEL */
if (line->type & LINE_HAS_LABEL)
udev_rule_line_clear_tokens(line);
@@ -1400,7 +1400,7 @@ static void udev_check_unused_labels(UdevRuleLine *line) {
if (FLAGS_SET(line->type, LINE_HAS_LABEL) &&
!FLAGS_SET(line->type, LINE_IS_REFERENCED))
log_line_warning(line, "LABEL=\"%s\" is unused.", line->label);
log_line_notice(line, "style: LABEL=\"%s\" is unused.", line->label);
}
static void udev_check_conflicts_duplicates(UdevRuleLine *line) {
@@ -1424,11 +1424,11 @@ static void udev_check_conflicts_duplicates(UdevRuleLine *line) {
if (new_duplicates) {
duplicates = new_duplicates;
log_line_warning(line, "duplicate expressions");
log_line_warning(line, "duplicate expressions.");
}
if (new_conflicts) {
conflicts = new_conflicts;
log_line_error(line, "conflicting match expressions, the line has no effect");
log_line_error(line, "conflicting match expressions, the line has no effect.");
}
if (conflicts && duplicates)
return;
@@ -1544,7 +1544,7 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_che
}
if (ignore_line)
log_file_error(rule_file, line_nr, "Line is too long, ignored");
log_file_error(rule_file, line_nr, "Line is too long, ignored.");
else if (len > 0)
(void) rule_add_line(rule_file, line, line_nr, extra_checks);
@@ -1554,7 +1554,7 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_che
if (continuation)
log_file_error(rule_file, line_nr,
"Unexpected EOF after line continuation, line ignored");
"Unexpected EOF after line continuation, line ignored.");
rule_resolve_goto(rule_file);
+14 -2
View File
@@ -21,6 +21,7 @@
static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
static char *arg_root = NULL;
static bool arg_summary = true;
static bool arg_style = true;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@@ -39,6 +40,7 @@ static int help(void) {
" -N --resolve-names=early|never When to resolve names\n"
" --root=PATH Operate on an alternate filesystem root\n"
" --no-summary Do not show summary\n"
" --no-style Ignore style issues\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@@ -52,6 +54,7 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_ROOT = 0x100,
ARG_NO_SUMMARY,
ARG_NO_STYLE,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
@@ -59,6 +62,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "resolve-names", required_argument, NULL, 'N' },
{ "root", required_argument, NULL, ARG_ROOT },
{ "no-summary", no_argument, NULL, ARG_NO_SUMMARY },
{ "no-style", no_argument, NULL, ARG_NO_STYLE },
{}
};
@@ -95,6 +99,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_summary = false;
break;
case ARG_NO_STYLE:
arg_style = false;
break;
case '?':
return -EINVAL;
default:
@@ -122,7 +130,11 @@ static int verify_rules_file(UdevRules *rules, const char *fname) {
unsigned mask = (1U << LOG_ERR) | (1U << LOG_WARNING);
if (issues & mask)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: udev rules check failed", fname);
"%s: udev rules check failed.", fname);
if (arg_style && (issues & (1U << LOG_NOTICE)))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: udev rules have style issues.", fname);
return 0;
}
@@ -215,7 +227,7 @@ int verify_main(int argc, char *argv[], void *userdata) {
return log_error_errno(r, "Failed to enumerate rules files: %m");
if (arg_root && strv_isempty(files))
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"No rules files found in %s", arg_root);
"No rules files found in %s.", arg_root);
return verify_rules(rules, files);
}
+81 -63
View File
@@ -56,18 +56,21 @@ next_test_number() {
out="sample-${num_str}.out"
}
assert_0() {
assert_0_impl() {
udevadm verify "$@" >"${out}"
if [ -f "${exo}" ]; then
diff -u "${exo}" "${out}"
elif [ -f "${rules}" ]; then
diff -u "${workdir}/default_output_1_success" "${out}"
fi
}
assert_0() {
assert_0_impl "$@"
next_test_number
}
assert_1() {
assert_1_impl() {
local rc
set +e
udevadm verify "$@" >"${out}" 2>"${err}"
@@ -81,8 +84,13 @@ assert_1() {
if [ -f "${exo}" ]; then
diff -u "${exo}" "${out}"
elif [ -f "${rules}" ]; then
diff -u "${workdir}/default_output_1_fail" "${out}"
fi
}
assert_1() {
assert_1_impl "$@"
next_test_number
}
@@ -127,7 +135,6 @@ assert_0 "${rules_dir}"
# Directory with a loop.
ln -s . "${rules_dir}/loop.rules"
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules_dir}"
rm "${rules_dir}/loop.rules"
@@ -155,7 +162,6 @@ assert_0 "${rules}"
# Failed to parse rules file ${rules}: No buffer space available
printf '%16384s\n' ' ' >"${rules}"
echo "Failed to parse rules file ${rules}: No buffer space available" >"${exp}"
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
{
@@ -168,18 +174,16 @@ assert_0 "${rules}"
printf 'RUN+="/bin/true"%8176s\\\n #\n' ' ' ' ' >"${rules}"
echo >>"${rules}"
cat >"${exp}" <<EOF
${rules}:5 Line is too long, ignored
${rules}: udev rules check failed
${rules}:5 Line is too long, ignored.
${rules}: udev rules check failed.
EOF
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
printf '\\\n' >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 Unexpected EOF after line continuation, line ignored
${rules}: udev rules check failed
${rules}:1 Unexpected EOF after line continuation, line ignored.
${rules}: udev rules check failed.
EOF
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
test_syntax_error() {
@@ -191,12 +195,27 @@ test_syntax_error() {
printf '%s\n' "${rule}" >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 ${msg}
${rules}: udev rules check failed
${rules}: udev rules check failed.
EOF
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
}
test_style_error() {
local rule msg
rule="$1"; shift
msg="$1"; shift
printf '%s\n' "${rule}" >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 ${msg}
${rules}: udev rules have style issues.
EOF
assert_0_impl --no-style "${rules}"
assert_1_impl "${rules}"
next_test_number
}
test_syntax_error '=' 'Invalid key/value pair, ignoring.'
test_syntax_error 'ACTION{a}=="b"' 'Invalid attribute for ACTION.'
test_syntax_error 'ACTION:="b"' 'Invalid operator for ACTION.'
@@ -273,12 +292,12 @@ test_syntax_error 'OWNER{a}="b"' 'Invalid attribute for OWNER.'
test_syntax_error 'OWNER-="b"' 'Invalid operator for OWNER.'
test_syntax_error 'OWNER!="b"' 'Invalid operator for OWNER.'
test_syntax_error 'OWNER+="0"' "OWNER key takes '=' or ':=' operator, assuming '='."
test_syntax_error 'OWNER=":nosuchuser:"' "Unknown user ':nosuchuser:', ignoring"
test_syntax_error 'OWNER=":nosuchuser:"' "Unknown user ':nosuchuser:', ignoring."
test_syntax_error 'GROUP{a}="b"' 'Invalid attribute for GROUP.'
test_syntax_error 'GROUP-="b"' 'Invalid operator for GROUP.'
test_syntax_error 'GROUP!="b"' 'Invalid operator for GROUP.'
test_syntax_error 'GROUP+="0"' "GROUP key takes '=' or ':=' operator, assuming '='."
test_syntax_error 'GROUP=":nosuchgroup:"' "Unknown group ':nosuchgroup:', ignoring"
test_syntax_error 'GROUP=":nosuchgroup:"' "Unknown group ':nosuchgroup:', ignoring."
test_syntax_error 'MODE{a}="b"' 'Invalid attribute for MODE.'
test_syntax_error 'MODE-="b"' 'Invalid operator for MODE.'
test_syntax_error 'MODE!="b"' 'Invalid operator for MODE.'
@@ -292,42 +311,42 @@ test_syntax_error 'SECLABEL{a}:="b"' "SECLABEL key takes '=' or '+=' operator, a
test_syntax_error 'RUN=="b"' 'Invalid operator for RUN.'
test_syntax_error 'RUN-="b"' 'Invalid operator for RUN.'
test_syntax_error 'RUN="%"' 'Invalid value "%" for RUN (char 1: invalid substitution type), ignoring.'
test_syntax_error 'RUN{builtin}+="foo"' "Unknown builtin command 'foo', ignoring"
test_syntax_error 'RUN{builtin}+="foo"' "Unknown builtin command 'foo', ignoring."
test_syntax_error 'GOTO{a}="b"' 'Invalid attribute for GOTO.'
test_syntax_error 'GOTO=="b"' 'Invalid operator for GOTO.'
test_syntax_error 'NAME="a", GOTO="b"' 'GOTO="b" has no matching label, ignoring'
test_syntax_error 'NAME="a", GOTO="b"' 'GOTO="b" has no matching label, ignoring.'
test_syntax_error 'GOTO="a", GOTO="b"
LABEL="a"' 'Contains multiple GOTO keys, ignoring GOTO="b".'
test_syntax_error 'LABEL{a}="b"' 'Invalid attribute for LABEL.'
test_syntax_error 'LABEL=="b"' 'Invalid operator for LABEL.'
test_syntax_error 'LABEL="b"' 'LABEL="b" is unused.'
test_syntax_error 'a="b"' "Invalid key 'a'"
test_syntax_error 'KERNEL=="", KERNEL=="?*", NAME="a"' 'conflicting match expressions, the line has no effect'
test_syntax_error 'KERNEL=="abc", KERNEL!="abc", NAME="b"' 'conflicting match expressions, the line has no effect'
test_syntax_error 'KERNEL=="|a|b", KERNEL!="b|a|", NAME="c"' 'conflicting match expressions, the line has no effect'
test_syntax_error 'KERNEL=="a|b", KERNEL=="c|d|e", NAME="f"' 'conflicting match expressions, the line has no effect'
test_style_error 'LABEL="b"' 'style: LABEL="b" is unused.'
test_syntax_error 'a="b"' "Invalid key 'a'."
test_syntax_error 'KERNEL=="", KERNEL=="?*", NAME="a"' 'conflicting match expressions, the line has no effect.'
test_syntax_error 'KERNEL=="abc", KERNEL!="abc", NAME="b"' 'conflicting match expressions, the line has no effect.'
test_syntax_error 'KERNEL=="|a|b", KERNEL!="b|a|", NAME="c"' 'conflicting match expressions, the line has no effect.'
test_syntax_error 'KERNEL=="a|b", KERNEL=="c|d|e", NAME="f"' 'conflicting match expressions, the line has no effect.'
# shellcheck disable=SC2016
test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}!="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
'conflicting match expressions, the line has no effect'
test_syntax_error 'ACTION=="a*", ACTION=="bc*", NAME="d"' 'conflicting match expressions, the line has no effect'
test_syntax_error 'ACTION=="a*|bc*", ACTION=="d*|ef*", NAME="g"' 'conflicting match expressions, the line has no effect'
test_syntax_error 'KERNEL!="", KERNEL=="?*", NAME="a"' 'duplicate expressions'
test_syntax_error 'KERNEL=="|a|b", KERNEL=="b|a|", NAME="c"' 'duplicate expressions'
'conflicting match expressions, the line has no effect.'
test_syntax_error 'ACTION=="a*", ACTION=="bc*", NAME="d"' 'conflicting match expressions, the line has no effect.'
test_syntax_error 'ACTION=="a*|bc*", ACTION=="d*|ef*", NAME="g"' 'conflicting match expressions, the line has no effect.'
test_syntax_error 'KERNEL!="", KERNEL=="?*", NAME="a"' 'duplicate expressions.'
test_syntax_error 'KERNEL=="|a|b", KERNEL=="b|a|", NAME="c"' 'duplicate expressions.'
# shellcheck disable=SC2016
test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}=="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
'duplicate expressions'
test_syntax_error ',ACTION=="a", NAME="b"' 'Stray leading comma.'
test_syntax_error ' ,ACTION=="a", NAME="b"' 'Stray leading comma.'
test_syntax_error ', ACTION=="a", NAME="b"' 'Stray leading comma.'
test_syntax_error 'ACTION=="a", NAME="b",' 'Stray trailing comma.'
test_syntax_error 'ACTION=="a", NAME="b", ' 'Stray trailing comma.'
test_syntax_error 'ACTION=="a" NAME="b"' 'A comma between tokens is expected.'
test_syntax_error 'ACTION=="a",, NAME="b"' 'More than one comma between tokens.'
test_syntax_error 'ACTION=="a" , NAME="b"' 'Stray whitespace before comma.'
test_syntax_error 'ACTION=="a",NAME="b"' 'Whitespace after comma is expected.'
'duplicate expressions.'
test_style_error ',ACTION=="a", NAME="b"' 'style: stray leading comma.'
test_style_error ' ,ACTION=="a", NAME="b"' 'style: stray leading comma.'
test_style_error ', ACTION=="a", NAME="b"' 'style: stray leading comma.'
test_style_error 'ACTION=="a", NAME="b",' 'style: stray trailing comma.'
test_style_error 'ACTION=="a", NAME="b", ' 'style: stray trailing comma.'
test_style_error 'ACTION=="a" NAME="b"' 'style: a comma between tokens is expected.'
test_style_error 'ACTION=="a",, NAME="b"' 'style: more than one comma between tokens.'
test_style_error 'ACTION=="a" , NAME="b"' 'style: stray whitespace before comma.'
test_style_error 'ACTION=="a",NAME="b"' 'style: whitespace after comma is expected.'
test_syntax_error 'RESULT=="a", PROGRAM="b"' 'Reordering RESULT check after PROGRAM assignment.'
test_syntax_error 'RESULT=="a*", PROGRAM="b", RESULT=="*c", PROGRAM="d"' \
'Reordering RESULT check after PROGRAM assignment.'
'Reordering RESULT check after PROGRAM assignment.'
cat >"${rules}" <<'EOF'
KERNEL=="a|b", KERNEL=="a|c", NAME="d"
@@ -342,11 +361,10 @@ assert_0 "${rules}"
echo 'GOTO="a"' >"${rules}"
cat >"${exp}" <<EOF
${rules}:1 GOTO="a" has no matching label, ignoring
${rules}:1 GOTO="a" has no matching label, ignoring.
${rules}:1 The line has no effect any more, dropping.
${rules}: udev rules check failed
${rules}: udev rules check failed.
EOF
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
cat >"${rules}" <<'EOF'
@@ -361,11 +379,11 @@ LABEL="b"
LABEL="b"
EOF
cat >"${exp}" <<EOF
${rules}:3 LABEL="b" is unused.
${rules}: udev rules check failed
${rules}:3 style: LABEL="b" is unused.
${rules}: udev rules have style issues.
EOF
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
assert_0_impl --no-style "${rules}"
assert_1_impl "${rules}"
cat >"${rules}" <<'EOF'
GOTO="a"
@@ -373,46 +391,46 @@ LABEL="a", LABEL="b"
EOF
cat >"${exp}" <<EOF
${rules}:2 Contains multiple LABEL keys, ignoring LABEL="a".
${rules}:1 GOTO="a" has no matching label, ignoring
${rules}:1 GOTO="a" has no matching label, ignoring.
${rules}:1 The line has no effect any more, dropping.
${rules}:2 LABEL="b" is unused.
${rules}: udev rules check failed
${rules}:2 style: LABEL="b" is unused.
${rules}: udev rules check failed.
EOF
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
cat >"${rules}" <<'EOF'
KERNEL!="", KERNEL=="?*", KERNEL=="", NAME="a"
EOF
cat >"${exp}" <<EOF
${rules}:1 duplicate expressions
${rules}:1 conflicting match expressions, the line has no effect
${rules}: udev rules check failed
${rules}:1 duplicate expressions.
${rules}:1 conflicting match expressions, the line has no effect.
${rules}: udev rules check failed.
EOF
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
cat >"${rules}" <<'EOF'
ACTION=="a"NAME="b"
EOF
cat >"${exp}" <<EOF
${rules}:1 A comma between tokens is expected.
${rules}:1 Whitespace between tokens is expected.
${rules}: udev rules check failed
${rules}:1 style: a comma between tokens is expected.
${rules}:1 style: whitespace between tokens is expected.
${rules}: udev rules have style issues.
EOF
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
assert_0_impl --no-style "${rules}"
assert_1_impl "${rules}"
next_test_number
cat >"${rules}" <<'EOF'
ACTION=="a" ,NAME="b"
EOF
cat >"${exp}" <<EOF
${rules}:1 Stray whitespace before comma.
${rules}:1 Whitespace after comma is expected.
${rules}: udev rules check failed
${rules}:1 style: stray whitespace before comma.
${rules}:1 style: whitespace after comma is expected.
${rules}: udev rules have style issues.
EOF
cp "${workdir}/default_output_1_fail" "${exo}"
assert_1 "${rules}"
assert_0_impl --no-style "${rules}"
assert_1_impl "${rules}"
next_test_number
# udevadm verify --root
sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"