Home | History | Annotate | Line # | Download | only in unit-tests
      1  1.37  rillig # $NetBSD: varmod-edge.mk,v 1.37 2025/06/28 22:39:29 rillig Exp $
      2   1.1  rillig #
      3   1.1  rillig # Tests for edge cases in variable modifiers.
      4   1.1  rillig #
      5   1.1  rillig # These tests demonstrate the current implementation in small examples.
      6   1.1  rillig # They may contain surprising behavior.
      7   1.1  rillig #
      8   1.1  rillig # Each test consists of:
      9   1.1  rillig # - INP, the input to the test
     10   1.1  rillig # - MOD, the expression for testing the modifier
     11   1.1  rillig # - EXP, the expected output
     12   1.1  rillig 
     13  1.26  rillig INP=	(parentheses) {braces} (opening closing) ()
     14  1.26  rillig MOD=	${INP:M(*)}
     15  1.26  rillig EXP=	(parentheses) ()
     16  1.26  rillig .if ${MOD} != ${EXP}
     17  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
     18  1.26  rillig .endif
     19   1.1  rillig 
     20   1.1  rillig # The first closing brace matches the opening parenthesis.
     21  1.18  rillig # The second closing brace actually ends the expression.
     22   1.1  rillig #
     23   1.1  rillig # XXX: This is unexpected but rarely occurs in practice.
     24  1.26  rillig INP=	(paren-brace} (
     25  1.26  rillig MOD=	${INP:M(*}}
     26  1.26  rillig EXP=	(paren-brace}
     27  1.26  rillig .if ${MOD} != ${EXP}
     28  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
     29  1.26  rillig .endif
     30   1.1  rillig 
     31   1.4  rillig # After the :M modifier has parsed the pattern, only the closing brace
     32   1.4  rillig # and the colon are unescaped. The other characters are left as-is.
     33   1.4  rillig # To actually see this effect, the backslashes in the :M modifier need
     34   1.4  rillig # to be doubled since single backslashes would simply be unescaped by
     35   1.4  rillig # Str_Match.
     36   1.4  rillig #
     37   1.4  rillig # XXX: This is unexpected. The opening brace should also be unescaped.
     38  1.26  rillig INP=	({}): \(\{\}\)\: \(\{}\):
     39  1.26  rillig MOD=	${INP:M\\(\\{\\}\\)\\:}
     40  1.26  rillig EXP=	\(\{}\):
     41  1.26  rillig .if ${MOD} != ${EXP}
     42  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
     43  1.26  rillig .endif
     44   1.4  rillig 
     45   1.1  rillig # When the :M and :N modifiers are parsed, the pattern finishes as soon
     46   1.1  rillig # as open_parens + open_braces == closing_parens + closing_braces. This
     47   1.1  rillig # means that ( and } form a matching pair.
     48   1.1  rillig #
     49  1.18  rillig # Nested expressions are not parsed as such. Instead, only the
     50   1.1  rillig # parentheses and braces are counted. This leads to a parse error since
     51   1.1  rillig # the nested expression is not "${:U*)}" but only "${:U*)", which is
     52   1.1  rillig # missing the closing brace. The expression is evaluated anyway.
     53   1.1  rillig # The final brace in the output comes from the end of M.nest-mix.
     54   1.1  rillig #
     55   1.1  rillig # XXX: This is unexpected but rarely occurs in practice.
     56  1.26  rillig INP=	(parentheses)
     57  1.26  rillig MOD=	${INP:M${:U*)}}
     58  1.26  rillig EXP=	(parentheses)}
     59  1.37  rillig # expect+1: Unclosed expression, expecting "}" for modifier "U*)"
     60  1.26  rillig .if ${MOD} != ${EXP}
     61  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
     62  1.26  rillig .endif
     63  1.24  rillig 
     64   1.1  rillig 
     65   1.1  rillig # In contrast to parentheses and braces, the brackets are not counted
     66  1.26  rillig # when the :M modifier is parsed since Makefile expressions only take the
     67   1.1  rillig # ${VAR} or $(VAR) forms, but not $[VAR].
     68   1.1  rillig #
     69   1.1  rillig # The final ] in the pattern is needed to close the character class.
     70  1.26  rillig INP=	[ [[ [[[
     71  1.26  rillig MOD=	${INP:M${:U[[[[[]}}
     72  1.26  rillig EXP=	[
     73  1.26  rillig .if ${MOD} != ${EXP}
     74  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
     75  1.26  rillig .endif
     76   1.1  rillig 
     77  1.28  rillig 
     78   1.1  rillig # The pattern in the nested variable has an unclosed character class.
     79   1.1  rillig #
     80  1.27  rillig # Before str.c 1.104 from 2024-07-06, no error was reported.
     81   1.5  rillig #
     82   1.5  rillig # Before 2019-12-02, this test case triggered an out-of-bounds read
     83   1.5  rillig # in Str_Match.
     84  1.26  rillig INP=	[ [[ [[[
     85  1.26  rillig MOD=	${INP:M${:U[[}}
     86  1.26  rillig EXP=	[
     87  1.37  rillig # expect+1: Unfinished character list in pattern "[[" of modifier ":M"
     88  1.26  rillig .if ${MOD} != ${EXP}
     89  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
     90  1.26  rillig .endif
     91   1.1  rillig 
     92   1.2  rillig # The first backslash does not escape the second backslash.
     93   1.2  rillig # Therefore, the second backslash escapes the parenthesis.
     94   1.2  rillig # This means that the pattern ends there.
     95  1.26  rillig # The final } in the output comes from the end of MOD.
     96   1.2  rillig #
     97   1.2  rillig # If the first backslash were to escape the second backslash, the first
     98  1.26  rillig # closing brace would match the opening parenthesis (see paren-brace), and
     99   1.2  rillig # the second closing brace would be needed to close the variable.
    100   1.3  rillig # After that, the remaining backslash would escape the parenthesis in
    101   1.3  rillig # the pattern, therefore (} would match.
    102  1.26  rillig INP=	(} \( \(}
    103  1.26  rillig MOD=	${INP:M\\(}}
    104  1.26  rillig EXP=	\(}
    105  1.26  rillig #EXP=	(}	# If the first backslash were to escape ...
    106  1.26  rillig .if ${MOD} != ${EXP}
    107  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    108  1.26  rillig .endif
    109   1.2  rillig 
    110   1.4  rillig # The backslash in \( does not escape the parenthesis, therefore it
    111   1.4  rillig # counts for the nesting level and matches with the first closing brace.
    112   1.4  rillig # The second closing brace closes the variable, and the third is copied
    113   1.4  rillig # literally.
    114   1.4  rillig #
    115   1.4  rillig # The second :M in the pattern is nested between ( and }, therefore it
    116   1.4  rillig # does not start a new modifier.
    117  1.26  rillig INP=	( (:M (:M} \( \(:M \(:M}
    118  1.26  rillig MOD=	${INP:M\(:M*}}}
    119  1.26  rillig EXP=	(:M}}
    120  1.26  rillig .if ${MOD} != ${EXP}
    121  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    122  1.26  rillig .endif
    123   1.4  rillig 
    124   1.4  rillig # The double backslash is passed verbatim to the pattern matcher.
    125   1.4  rillig # The Str_Match pattern is \\(:M*}, and there the backslash is unescaped.
    126   1.4  rillig # Again, the ( takes place in the nesting level, and there is no way to
    127   1.4  rillig # prevent this, no matter how many backslashes are used.
    128  1.26  rillig INP=	( (:M (:M} \( \(:M \(:M}
    129  1.26  rillig MOD=	${INP:M\\(:M*}}}
    130  1.26  rillig EXP=	\(:M}}
    131  1.26  rillig .if ${MOD} != ${EXP}
    132  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    133  1.26  rillig .endif
    134  1.26  rillig 
    135  1.26  rillig # Before str.c 1.48 from 2020-06-15, Str_Match used a recursive algorithm for
    136  1.26  rillig # matching the '*' patterns and did not optimize for multiple '*' in a row.
    137  1.26  rillig # Test a pattern with 65536 asterisks.
    138  1.26  rillig INP=	${:U\\:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:S,\\,x,g}
    139  1.26  rillig PAT=	${:U\\:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:Q:S,\\,*,g}
    140  1.26  rillig MOD=	${INP:M${PAT}}
    141  1.26  rillig EXP=	${INP}
    142  1.26  rillig .if ${MOD} != ${EXP}
    143  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    144  1.26  rillig .endif
    145   1.6  rillig 
    146   1.6  rillig # This is the normal SysV substitution. Nothing surprising here.
    147  1.26  rillig INP=	file.c file.cc
    148  1.26  rillig MOD=	${INP:%.c=%.o}
    149  1.26  rillig EXP=	file.o file.cc
    150  1.26  rillig .if ${MOD} != ${EXP}
    151  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    152  1.26  rillig .endif
    153   1.6  rillig 
    154   1.6  rillig # The SysV := modifier is greedy and consumes all the modifier text
    155   1.6  rillig # up until the closing brace or parenthesis. The :Q may look like a
    156   1.6  rillig # modifier, but it really isn't, that's why it appears in the output.
    157  1.26  rillig INP=	file.c file.cc
    158  1.26  rillig MOD=	${INP:%.c=%.o:Q}
    159  1.26  rillig EXP=	file.o:Q file.cc
    160  1.26  rillig .if ${MOD} != ${EXP}
    161  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    162  1.26  rillig .endif
    163   1.6  rillig 
    164   1.6  rillig # The = in the := modifier can be escaped.
    165  1.26  rillig INP=	file.c file.c=%.o
    166  1.26  rillig MOD=	${INP:%.c\=%.o=%.ext}
    167  1.26  rillig EXP=	file.c file.ext
    168  1.26  rillig .if ${MOD} != ${EXP}
    169  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    170  1.26  rillig .endif
    171   1.6  rillig 
    172   1.8  rillig # Having only an escaped '=' results in a parse error.
    173   1.8  rillig # The call to "pattern.lhs = ParseModifierPart" fails.
    174  1.26  rillig INP=	file.c file...
    175  1.26  rillig MOD=	${INP:a\=b}
    176  1.26  rillig EXP=	# empty
    177  1.34  rillig # expect+1: Unfinished modifier after "a\=b}", expecting "="
    178  1.26  rillig .if ${MOD} != ${EXP}
    179  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    180  1.26  rillig .endif
    181  1.26  rillig 
    182  1.26  rillig INP=	value
    183  1.26  rillig MOD=	${INP:}
    184  1.26  rillig EXP=	value
    185  1.26  rillig .if ${MOD} != ${EXP}
    186  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    187  1.26  rillig .endif
    188  1.26  rillig 
    189  1.26  rillig INP=	value
    190  1.26  rillig MOD=	${INP::::}
    191  1.35  rillig EXP=	:}
    192  1.36  rillig # expect+1: Unknown modifier "::"
    193  1.26  rillig .if ${MOD} != ${EXP}
    194  1.26  rillig .  warning expected "${EXP}", got "${MOD}"
    195  1.26  rillig .endif
    196  1.11  rillig 
    197  1.14  rillig # Even in expressions based on an unnamed variable, there may be errors.
    198  1.36  rillig # expect+1: Unknown modifier ":Z"
    199  1.14  rillig .if ${:Z}
    200  1.14  rillig .  error
    201  1.14  rillig .else
    202  1.14  rillig .  error
    203  1.14  rillig .endif
    204  1.14  rillig 
    205  1.14  rillig # Even in expressions based on an unnamed variable, there may be errors.
    206  1.15  rillig #
    207  1.15  rillig # Before var.c 1.842 from 2021-02-23, the error message did not surround the
    208  1.15  rillig # variable name with quotes, leading to the rather confusing "Unfinished
    209  1.15  rillig # modifier for  (',' missing)", having two spaces in a row.
    210  1.15  rillig #
    211  1.34  rillig # expect+1: Unfinished modifier after "}", expecting ","
    212  1.14  rillig .if ${:S,}
    213  1.14  rillig .  error
    214  1.14  rillig .else
    215  1.14  rillig .  error
    216  1.14  rillig .endif
    217