# $NetBSD: t_swsensor.sh,v 1.13 2021/12/05 08:16:10 msaitoh Exp $

get_sensor_info() {
       rump.envstat -x | \
       sed -e "\;swsensor;,\;/array;p" -e "d"
}

get_sensor_key() {
       local v
       v=$(get_sensor_info | grep -A1 $1 | grep integer | \
           sed -e 's;<[/a-z]*>;;g')
       if [ -z "$v" ] ; then
               v="key_$1_not_found"
       fi
       echo $v
}

get_powerd_event_count() {
       grep "not running" powerd.log | wc -l
}

get_rnd_bits_count() {
       env RUMPHIJACK=blanket=/dev/random:/dev/urandom \
           RUMP_SERVER=unix://t_swsensor_socket        \
           LD_PRELOAD=/usr/lib/librumphijack.so          rndctl -l | \
       grep "swsensor-sensor" | \
       awk '{print $3}'
}

check_powerd_event() {
       event=$(grep "not running" powerd.log | \
               sed -e "$1p" -e "d" )
       event=${event##*//}
       script=${event%% *}
       event=${event#* }
       device=${event%% *}
       event=${event#* }
       state=${event%% *}
       sensor=${event#* }
       sensor=${sensor% *}

       if [ "${script}" != "sensor_indicator" ] ; then
               echo "Event uses wrong script: ${script}"
       elif [ "${device}" != "swsensor" ] ; then
               echo "Event uses wrong device: ${device}"
       elif [ "${sensor}" != "sensor" ] ; then
               echo "Event uses wrong sensor: ${sensor}"
       elif [ "${state}" != "$2" ] ; then
               echo "Event uses wrong state: ${state}"
       fi
}

# Start the rump server, then load the swsensor module with the
# requested properties

start_rump() {
       rump_allserver -l rumpvfs -l rumpdev -l rumpdev_sysmon ${RUMP_SERVER}
       if [ $( get_sensor_info | wc -l ) -ne 0 ] ; then
               rump.modunload swsensor
               rump.modload -f $1 swsensor
       else
               rump.modload $1 swsensor
       fi
       return $?
}

common_head() {
       atf_set descr           "$1"
       atf_set timeout         120
       atf_set require.progs   rump.powerd rump.envstat rump.modload   \
                               rump.halt   rump.sysctl  rump_server    \
                               sed         grep         awk            \
                               rndctl      expr
}

common_cleanup() {
       rump.modunload swsensor
       rump.halt
}

create_envsys_conf_files() {
       cat << ENV0 > env0.conf
       swsensor {
               refresh-timeout = 2s;
       }
ENV0
       cat << ENV1 > env1.conf
       swsensor {
               sensor0 { critical-min = $(( $1 - $2 )); }
       }
ENV1
       cat << ENV2 > env2.conf
       swsensor {
               sensor0 { critical-min = $1; }
       }
ENV2
}

# Test body common to all sensors
#       $1      sensor mode
#       $2      initial sensor value
#       $3      initial limit
#       $4      amount to lower limit
#       $5      difference from limit to trigger event
#       $6      sensor flags, for FHAS_ENTROPY and FMONNOTSUPP

common_body() {
       # Start the rump-server process and load the module
       modload_args="-i mode=$1 -i value=$2 -i limit=$3 ${6:+-i flags=$6}"
       start_rump "$modload_args"

       # create configuration files for updates
       create_envsys_conf_files $3 $4

       if [ $? -ne 0 ] ; then
               atf_skip "Cannot set-up rump environment"
       fi

       # start powerd so we can detect sensor events
       rump.powerd -n -d > powerd.log 2>&1 &
       if [ -z "$(jobs)" ] ; then
               skip_events=1
               echo "Skipping event sub-tests - powerd did not start"
       else
               skip_events=0
               expected_event=1
       fi

       # Step 0 - verify that sensor is registered
       get_sensor_info | grep -q swsensor ||
               atf_fail "0: Device swsensor not registered"

       # Step 1 - update the refresh-timeout and verify
       # (use $(( ... )) since the timeout is displayed in hex!)
       rump.envstat -c env0.conf
       if [ $(( $( get_sensor_key refresh-timeout ) )) -ne 2 ] ; then
               atf_fail "1: Could not set refresh-timeout to 2s"
       fi

       # Step 2 - verify that we can read sensor's value
       if [ $1 -ne 0 -a $( get_sensor_key cur-value ) -ne $2 ] ; then
               atf_fail "2: Value not available"
       fi

       # Step 3 - verify that changes in sensor value are seen
       rump.sysctl -w hw.swsensor.cur_value=$(( $2 + 1 ))
       if [ $( get_sensor_key cur-value ) -ne $(( $2 + 1 )) ] ; then
               atf_fail "3: Value not updated"
       fi

       # Step 4 - if sensor provides hw limit, make sure we can read it
       if [ $1 -ne 0 ] ; then
               if [ $( get_sensor_key critical-min ) -ne $3 ] ; then
                       atf_fail "4: Limit not set by device"
               fi
       fi

       # Step 5 - if sensor provides hw limit, make sure it works
       if [ $1 -ne 0 -a ${skip_events} -eq 0 ] ; then
               rump.sysctl -w hw.swsensor.cur_value=$(( $3 - $5 ))
               sleep 5
               cnt=$(get_powerd_event_count)
               if [ ${cnt} -lt ${expected_event} ] ; then
                       atf_fail "5: No event triggered"
               elif [ ${cnt} -gt ${expected_event} ] ; then
                       atf_fail "5: Multiple events triggered"
               fi
               evt=$( check_powerd_event ${cnt} "critical-under")
               if [ -n "${evt}" ] ; then
                       atf_fail "5: ${evt}"
               fi
               expected_event=$(( 1 + ${expected_event} ))
       fi

       # Step 6 - verify that we return to normal state
       if [ $1 -ne 0 -a ${skip_events} -eq 0 ] ; then
               rump.sysctl -w hw.swsensor.cur_value=$(( $3 + $5 ))
               sleep 5
               cnt=$(get_powerd_event_count)
               if [ ${cnt} -lt ${expected_event} ] ; then
                       atf_fail "6: No event triggered"
               elif [ ${cnt} -gt ${expected_event} ] ; then
                       atf_fail "6: Multiple events triggered"
               fi
               evt=$( check_powerd_event ${cnt} "normal")
               if [ -n "${evt}" ] ; then
                       atf_fail "6: ${evt}"
               fi
               expected_event=$(( 1 + ${expected_event} ))
       fi

       # Step 7 - verify that we can set our own limit

       # Steps 7 thru 12 are skipped if the sensor cannot be monitored
       if [ $( expr \( 0$6 / 2048 \) % 2 ) -ne 1 ] ; then
               rump.envstat -c env1.conf
               if [ $( get_sensor_key critical-min ) -ne $(( $3 - $4 )) ] ; then
                       atf_fail "7: Limit not set by envstat -c"
               fi

       # Step 8 - make sure user-set limit works
               if [ ${skip_events} -eq 0 ] ; then
                       rump.sysctl -w hw.swsensor.cur_value=$(( $3 - $4 - $5 ))
                       sleep 5
                       cnt=$(get_powerd_event_count)
                       if [ ${cnt} -lt ${expected_event} ] ; then
                               atf_fail "8: No event triggered"
                       elif [ ${cnt} -gt ${expected_event} ] ; then
                               atf_fail "8: Multiple events triggered"
                       fi
                       evt=$( check_powerd_event ${cnt} "critical-under")
                       if [ -n "${evt}" ] ; then
                               atf_fail "8: ${evt}"
                       fi
                       expected_event=$(( 1 + ${expected_event} ))
               fi

       # Step 9 - verify that we return to normal state
               if [ ${skip_events} -eq 0 ] ; then
                       rump.sysctl -w hw.swsensor.cur_value=$(( $3 - $4 + $5 ))
                       sleep 5
                       cnt=$(get_powerd_event_count)
                       if [ ${cnt} -lt ${expected_event} ] ; then
                               atf_fail "9: No event triggered"
                       elif [ ${cnt} -gt ${expected_event} ] ; then
                               atf_fail "9: Multiple events triggered"
                       fi
                       evt=$( check_powerd_event ${cnt} "normal")
                       if [ -n "${evt}" ] ; then
                               atf_fail "9: ${evt}"
                       fi
                       expected_event=$(( 1 + ${expected_event} ))
               fi

       # Step 10 - reset to defaults
               rump.envstat -S
               if [ $1 -eq 0 ] ; then
                       get_sensor_info | grep -q critical-min &&
                               atf_fail "10: Failed to clear a limit with envstat -S"
               else
                       if [ $( get_sensor_key critical-min ) -ne $3 ] ; then
                               atf_fail "10: Limit not reset to initial value"
                       fi
               fi

       # Step 11 - see if more events occur
               if [ ${skip_events} -eq 0 ] ; then
                       rump.envstat -c env0.conf
                       rump.sysctl -w hw.swsensor.cur_value=$(( $3 - $4 - $5 ))
                       sleep 5
                       cnt=$(get_powerd_event_count)
                       if [ ${cnt} -ge ${expected_event} ] ; then
                               if [ $1 -ne 2 ] ; then
                                       atf_fail "11b Event triggered after reset"
                               fi
                               evt=$( check_powerd_event ${cnt} "critical-under")
                               if [ -n "${evt}" ] ; then
                                       atf_fail "11a: ${evt}"
                               fi
                       fi
               fi

       # Step 12 - make sure we can set new limits once more
               rump.envstat -c env2.conf
               if [ $( get_sensor_key critical-min ) -ne $3 ] ; then
                       atf_fail "12a: Limit not reset to same value"
               fi
               rump.envstat -c env1.conf
               if [ $( get_sensor_key critical-min ) -ne $(( $3 - $4 )) ] ; then
                       atf_fail "12b: Limit not reset to new value"
               fi
       fi

       # Step 13 - confirm registration (or lack thereof) with rndctl
       rnd_bits=$( get_rnd_bits_count )
       if [ $( expr \( 0$6 / 8192 \) % 2 ) -eq 1 ] ; then
               if [ -z "$rnd_bits" ] ; then
                       atf_fail "13a: Not registered with rndctl"
               fi
       else
               if [ -n "$rnd_bits" ] ; then
                       atf_fail "13b: Wrongly registered with rndctl"
               fi
       fi

       # Steps 14 and 15 are only if sensor is providing entropy
       if [ $( expr \( 0$6 / 8192 \) % 2 ) -ne 1 ] ; then
               return
       fi

       # Step 14 - make sure entropy collected when device is being polled
       rump.envstat -c env0.conf
       rump.sysctl -w hw.swsensor.cur_value=$3
       sleep 5
       rump.sysctl -w hw.swsensor.cur_value=$(( $3 + $4 ))
       sleep 5
       new_rnd_bits=$( get_rnd_bits_count )
       if [ $new_rnd_bits -le $rnd_bits ] ; then
               atf_fail "14a: entropy bits did not increase after polling"
       fi
       rnd_bits=$new_rnd_bits
       sleep 5
       new_rnd_bits=$( get_rnd_bits_count )
       if [ $new_rnd_bits -gt $rnd_bits ] ; then
               atf_fail "14b: entropy bits increased after poll with no value change"
       fi

       # Step 15 - make sure entropy collected when device is interrogated
       rump.envstat -c env0.conf
       rump.sysctl -w hw.swsensor.cur_value=$3
       get_sensor_key cur-value
       rnd_bits=$( get_rnd_bits_count )
       rump.sysctl -w hw.swsensor.cur_value=$(( $3 + $4 ))
       get_sensor_key cur-value
       new_rnd_bits=$( get_rnd_bits_count )
       if [ $new_rnd_bits -le $rnd_bits ] ; then
               atf_fail "15a: entropy bits did not increase after interrogation"
       fi
       rnd_bits=$new_rnd_bits
       get_sensor_key cur-value
       new_rnd_bits=$( get_rnd_bits_count )
       if [ $new_rnd_bits -gt $rnd_bits ] ; then
               atf_fail "15b: entropy bits increased after interrogation with no value change"
       fi
}

atf_test_case simple_sensor cleanup
simple_sensor_head() {
       common_head "Test a simple sensor"
}

simple_sensor_body() {
       common_body 0 50 30 10 1
}

simple_sensor_cleanup() {
       common_cleanup
}

atf_test_case limit_sensor cleanup
limit_sensor_head() {
       common_head "Test a sensor with internal limit"
}

limit_sensor_body() {
       common_body 1 45 25 8 2
}

limit_sensor_cleanup() {
       common_cleanup
}

atf_test_case alarm_sensor cleanup
alarm_sensor_head() {
       common_head "Test a sensor with internal checking"
}

alarm_sensor_body() {
       common_body 2 40 20 6 3
}

alarm_sensor_cleanup() {
       common_cleanup
}

atf_test_case entropy_polled_sensor cleanup
entropy_polled_sensor_head() {
       common_head "Test a simple sensor that provides entropy"
}

entropy_polled_sensor_body() {
       common_body 0 50 30 10 1 8192
}

entropy_polled_sensor_cleanup() {
       common_cleanup
}

atf_test_case entropy_interrupt_sensor cleanup
entropy_interrupt_sensor_head() {
       common_head "Test a sensor that provides entropy without polling"
}

entropy_interrupt_sensor_body() {
       common_body 0 50 30 10 1 10240
}

entropy_interrupt_sensor_cleanup() {
       common_cleanup
}

atf_init_test_cases() {
       RUMP_SERVER="unix://t_swsensor_socket" ; export RUMP_SERVER
       atf_add_test_case simple_sensor
       atf_add_test_case limit_sensor
       atf_add_test_case alarm_sensor
       atf_add_test_case entropy_polled_sensor
       atf_add_test_case entropy_interrupt_sensor
}