# $NetBSD: t_lint2.sh,v 1.13 2021/12/16 09:38:54 rillig Exp $
#
# Copyright (c) 2021 The NetBSD Foundation, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

lint2=/usr/libexec/lint2

std_head()
{
       atf_set 'require.progs' "$lint2"
}

std_body()
{
       # shellcheck disable=SC2155
       local srcdir="$(atf_get_srcdir)"

       # remove comments and whitespace from the .ln file
       sed -e '/^#/d' -e '/^$/d' -e 's,#.*,,' -e 's,[[:space:]],,g' \
           < "$srcdir/$1.ln" \
           > "$1.ln"

       atf_check -o "file:$srcdir/$1.exp" \
           "$lint2" -h -x "$1.ln"
}

std_emit_body()
{
       # shellcheck disable=SC2155
       local srcdir="$(atf_get_srcdir)"

       # remove comments and whitespace from the .ln files
       sed -e '/^#/d' -e '/^$/d' -e 's,#.*,,' -e 's,[[:space:]],,g' \
           < "$srcdir/$1.ln" \
           > "$1.ln"
       sed -e '/^#/d' -e '/^$/d' -e 's,#.*,,' -e 's,[[:space:]],,g' \
           < "$srcdir/$1.exp-ln" \
           > "$1.exp-ln"

       atf_check \
           "$lint2" -h -x -C "$1" "$1.ln"

       atf_check -o "file:$1.exp-ln" \
           cat "llib-l$1.ln"
}

emit_body()
{
       std_emit_body 'emit'
}

emit_lp64_body()
{
       std_emit_body 'emit_lp64'
}

# usage: test_error input message-regex [input-regex]
test_error()
{
       printf '%s\n' \
           "$1"
       printf '%s\n' \
           '0sinput.ln' \
           'Sinput.ln' \
           "$1" \
           > 'input.ln'

       atf_check -s 'exit:1' \
           -e "match:error: input\\.ln:3: $2 \\(for '${3-$1}'\\)\$" \
           "$lint2" 'input.ln'
}

test_error_ignored()
{
       printf '%s\n' \
           "$1"
       printf '%s\n' \
           '0sinput.ln' \
           'Sinput.ln' \
           "$1" \
           > 'input.ln'

       atf_check -o 'ignore' \
           "$lint2" 'input.ln'
}

error_cases_head()
{
       std_head
}
error_cases_body()
{
       test_error ''                   'missing record type'
       test_error '123'                'missing record type'
       test_error '0X'                 'not a number: '
       test_error '0d'                 'not a number: '
       test_error '0dXYZ'              'not a number: XYZ'
       test_error '0d123'              'bad line number'
       test_error '0d123.XYZ'          'not a number: XYZ'
       test_error '0X0.0'              'bad record type X'

       # function calls
       test_error '0c0.0'              'not a number: '
       test_error '0c0.0uu'            'used or discovered: u'
       test_error '0c0.0du'            'used or discovered: u'
       test_error '0c0.0ui'            'used or discovered: i'
       test_error '0c0.0di'            'used or discovered: i'
       test_error '0c0.0ud'            'used or discovered: d'
       test_error '0c0.0dd'            'used or discovered: d'
       # Unlike 'd' and 'u', the 'i' may be repeated.
       test_error '0c0.0iiiiiii1n_'    'bad type: _ '
       # Negative argument numbers like in 'z-1' are accepted but ignored.
       test_error '0c0.0z-1d_'         'not a number: _'
       # Argument 1 is both positive '1p' and negative '1n', which is
       # impossible in practice.  It is not worth handling this though since
       # only lint1 generates these .ln files.
       test_error '0c0.0p1n1d_'        'not a number: _'
       test_error '0c0.0s'             'not a number: '
       test_error '0c0.0s2'            'not quote: '
       test_error '0c0.0s2|'           'not quote: |'
       test_error '0c0.0s2"'           'trailing data: '
       test_error '0c0.0s2"%'          'missing closing quote'
       # shellcheck disable=SC1003
       test_error '0c0.0s2"\'          'missing after \\'      '0c0\.0s2"\\'
       # shellcheck disable=SC1003
       test_error '0c0.0s2"%\'         'missing after \\'      '0c0\.0s2"%\\'

       # declarations and definitions
       test_error '0d0'                'bad line number'
       test_error '0d0.0'              'not a number: '
       test_error '0d0.0dd'            'def'
       test_error '0d0.0de'            'decl'
       test_error '0d0.0ee'            'decl'
       test_error '0d0.0ii'            'inline'
       test_error '0d0.0oo'            'osdef'
       test_error '0d0.0rr'            'r'
       test_error '0d0.0ss'            'static'
       test_error '0d0.0tt'            'tdef'
       test_error '0d0.0uu'            'used'
       test_error '0d0.0v1v1'          'v'
       test_error '0d0.0P1P1'          'P'
       test_error '0d0.0S1S1'          'S'
       test_error '0d0.0v1P1S_'        'not a number: _'
       test_error '0d0.0d3var_'        'bad type: _ '
       test_error '0d0.0d3varPV_'      'trailing line: _'

       # usage of a variable or a function
       test_error '0u0.0'              'bad delim '
       test_error '0u0.0_'             'bad delim _'
       test_error '0u0.0x'             'not a number: '

       # trailing garbage is not detected
       test_error_ignored '0u0.0x3var_'
}

missing_newline_head()
{
       std_head
}

missing_newline_body()
{
       # Before read.c 1.72 from 2021-12-16, the error message was just 'c'
       # without any textual description or context, and the line number was
       # off by one, it was reported as line 0.

       printf '1d1.1e5func' > 'input.ln'

       atf_check -s 'exit:1' \
           -e 'match:^.*: error: input\.ln:1: missing newline after .c. \(for .1d1\.1e5func.\)$' \
           "$lint2" 'input.ln'
}

atf_init_test_cases()
{
       local i

       # shellcheck disable=SC2013
       # shellcheck disable=SC2035
       for i in $(cd "$(atf_get_srcdir)" && echo *.ln); do
               i=${i%.ln}

               case "$i" in
               *lp64*)
                       case "$(uname -p)" in
                       *64) ;;
                       *) continue
                       esac
               esac

               type "${i}_head" 1>/dev/null 2>&1 \
               || eval "${i}_head() { std_head; }"
               type "${i}_body" 1>/dev/null 2>&1 \
               || eval "${i}_body() { std_body '$i'; }"
               atf_add_test_case "$i"
       done

       atf_add_test_case 'error_cases'
       atf_add_test_case 'missing_newline'
}