From 81c611e365cea5edf8bc80df626ffec7fcca9de0 Mon Sep 17 00:00:00 2001 From: Koichi Nakashima Date: Mon, 3 Jun 2024 23:00:29 +0900 Subject: [PATCH] Re-implement shellspec-time for locale support --- helper/fixture/time_log.txt | 5 +- lib/libexec/reporter.sh | 28 ++- lib/libexec/reporter/finished_formatter.sh | 4 +- libexec/shellspec-executor.sh | 5 +- libexec/shellspec-runner.sh | 2 +- libexec/shellspec-time.sh | 203 +++++++++++++++++---- shellspec | 14 +- spec/libexec/reporter_spec.sh | 4 +- 8 files changed, 193 insertions(+), 72 deletions(-) diff --git a/helper/fixture/time_log.txt b/helper/fixture/time_log.txt index 5297e4d9..dbfb38c2 100644 --- a/helper/fixture/time_log.txt +++ b/helper/fixture/time_log.txt @@ -1,4 +1 @@ - -real 1.23 -user 0.11 -sys 12.45 +real:1.23 user:0.11 sys:12.45 type:external diff --git a/lib/libexec/reporter.sh b/lib/libexec/reporter.sh index 3623ccf7..b93aea5a 100644 --- a/lib/libexec/reporter.sh +++ b/lib/libexec/reporter.sh @@ -17,16 +17,16 @@ count_examples() { # $1: prefix, $2: filename # shellcheck disable=SC2295 read_time_log() { - eval "$1_real='' $1_user='' $1_sys=''" - [ -r "$2" ] || return 1 - # shellcheck disable=SC2034 - while IFS= read -r line; do - case $line in (real[\ $TAB]*|user[\ $TAB]*|sys[\ $TAB]*) - case ${line##*[ $TAB]} in (*[!0-9.,]*) continue; esac - eval "$1_${line%%[ $TAB]*}=\"\${line##*[ \$TAB]}\"" - esac - done < "$2" &&: - eval "[ \"\$$1_real\" ] && [ \"\$$1_user\" ] && [ \"\$$1_sys\" ]" + eval "$1_real='' $1_user='' $1_sys='' $1_type=''" + [ -s "$2" ] || return 1 + { + set -- "$1_real" "$1_user" "$1_sys" "$1_type" + IFS=" " read -r "$@" || return 1 + while [ $# -gt 0 ]; do + eval "$1=\${$1#${1#*_}:}" + shift + done + } < "$2" } field_description() { @@ -112,18 +112,12 @@ inc() { read_profiler() { time_real_nano=0 - case $3 in - *,*) separator=',' ;; - *) separator='.' ;; - esac - shellspec_replace_all time_real_nano "$3" "$separator" '.' - shellspec_shift10 time_real_nano "$time_real_nano" 4 + shellspec_shift10 time_real_nano "$3" 4 profiler_count=0 while IFS=" " read -r tick; do duration=$(($time_real_nano * $tick / $2)) shellspec_shift10 duration "$duration" -4 - shellspec_replace_all duration "$duration" '.' "$separator" set -- "$1" "$2" "$3" "$profiler_count" "$tick" "$duration" eval "profiler_tick$4=\$5 profiler_time$4=\$6" "$@" diff --git a/lib/libexec/reporter/finished_formatter.sh b/lib/libexec/reporter/finished_formatter.sh index 4e2e7b42..9b048fb2 100644 --- a/lib/libexec/reporter/finished_formatter.sh +++ b/lib/libexec/reporter/finished_formatter.sh @@ -4,7 +4,9 @@ create_buffers finished finished_end() { finished '=' "Finished in ${time_real:-?} seconds" \ - "(user ${time_user:-?} seconds, sys ${time_sys:-?} seconds)${LF}" + "(user: ${time_user:-n/a}${time_user:+s}," \ + "sys: ${time_sys:-n/a}${time_sys:+s})" \ + "[time: $time_type]${LF}" } finished_output() { diff --git a/libexec/shellspec-executor.sh b/libexec/shellspec-executor.sh index d5fec300..d4d084f0 100755 --- a/libexec/shellspec-executor.sh +++ b/libexec/shellspec-executor.sh @@ -3,9 +3,6 @@ set -eu -# Close the file descriptor for time command -exec 2>&3 - # shellcheck source=lib/libexec/executor.sh . "${SHELLSPEC_LIB:-./lib}/libexec/executor.sh" @@ -115,4 +112,4 @@ set +e read -r xs2 && [ "$xs2" -ne 0 ] && exit "$xs2" exit 0 ) -) 4>&1 +) 4>&1 3>&2 diff --git a/libexec/shellspec-runner.sh b/libexec/shellspec-runner.sh index a3e93fa3..bbde9983 100755 --- a/libexec/shellspec-runner.sh +++ b/libexec/shellspec-runner.sh @@ -66,7 +66,7 @@ executor() { start_profiler executor="$SHELLSPEC_LIBEXEC/shellspec-executor.sh" # shellcheck disable=SC2086 - $SHELLSPEC_TIME $SHELLSPEC_SHELL "$executor" "$@" 3>&2 2>"$SHELLSPEC_TIME_LOG" + $SHELLSPEC_SHELL "$SHELLSPEC_TIME" $SHELLSPEC_SHELL "$executor" "$@" eval "stop_profiler; return $?" } diff --git a/libexec/shellspec-time.sh b/libexec/shellspec-time.sh index ca64af35..27e4d5f2 100755 --- a/libexec/shellspec-time.sh +++ b/libexec/shellspec-time.sh @@ -1,46 +1,185 @@ #!/bin/sh -#shellcheck disable=SC2004 +# shellcheck disable=SC2004 +# Write it to work in a Bourne shell if possible. + +set -f + +: "${SHELLSPEC_TRAP:=trap}" +: "${SHELLSPEC_TIME_TYPE:=auto}" +LF=' +' "$SHELLSPEC_TRAP" : INT -if [ "$BASH_VERSION" ] || [ "$KSH_VERSION" ]; then - time -p "$@" - exit $? -fi +datetime2unixtime() { + set -- "$1" "${2%%-*}" "${2%%T*}" "${2##*T}" + set -- "$1" "${2#"${2%%[!0]*}"}" "${3#*-}" "${4%%:*}" "${4#*:}" + set -- "$1" "$2" "${3%%-*}" "${3#*-}" "$4" "${5%%:*}" "${5#*:}" + set -- "$1" "${2:-0}" "${3#0}" "${4#0}" "${5#0}" "${6#0}" "${7#0}" + [ "$3" -lt 3 ] && set -- "$1" $(($2-1)) $(($3+12)) "$4" "$5" "$6" "$7" + set -- "$1" $((365*$2+$2/4-$2/100+$2/400)) "$3" "$4" "$5" "$6" "$7" + set -- "$1" "$2" $(((306*($3+1)/10)-428)) "$4" "$5" "$6" "${7%.*}" "${7#*.}" + eval "$1=$((($2+$3+$4-719163)*86400+$5*3600+$6*60+$7))${8:+.}${8}" +} + +time_using_date() { + date +'start %Y-%m-%dT%H:%M:%S.%N' + "$@" + set -- $? + date +'end %Y-%m-%dT%H:%M:%S.%N' + return "$1" +} + +detect_time_type() { + [ "$SHELLSPEC_TIME_TYPE" = auto ] || return 0 + + if ! { time true; } >/dev/null 2>&1; then + SHELLSPEC_TIME_TYPE=external-date && return 0 + fi + + if [ ! "${KSH_VERSION:-}" ]; then + # shellcheck disable=SC2006 + KSH_VERSION=`eval 'echo ${.sh.version}'` + fi 2>/dev/null + + if [ "${BASH_VERSION:-}" ]; then + SHELLSPEC_TIME_TYPE=bash-builtin && return 0 + fi + + if [ "${KSH_VERSION:-}" ]; then + case $KSH_VERSION in + *PD\ KSH*) SHELLSPEC_TIME_TYPE=pdksh-builtin ;; + *MIRBSD\ KSH*) SHELLSPEC_TIME_TYPE=mksh-builtin ;; + *LEGACY\ KSH*) SHELLSPEC_TIME_TYPE=lksh-builtin ;; + *) SHELLSPEC_TIME_TYPE=ksh93-builtin ;; + esac + return 0 + fi + + if [ "${ZSH_VERSION:-}" ]; then + SHELLSPEC_TIME_TYPE=zsh-builtin && return 0 + fi + + if { time -p true; } >/dev/null 2>&1; then + SHELLSPEC_TIME_TYPE=external-time + else + SHELLSPEC_TIME_TYPE=legacy-time + fi +} + +detect_time_type + +{ + ( + "$SHELLSPEC_TRAP" : INT + + set -- -- "$@" -set -eu + case ${LC_ALL+x} in + ?) set -- -s LC_ALL "$LC_ALL" "$@" ;; + *) set -- -u LC_ALL "$@" ;; + esac + LC_ALL=C && export LC_ALL -read -r sec_start millisec_start <&3 3>&- >&4 + ' "$0" "$@" -read -r sec_end millisec_end <&2 + ) 2>&1 | ( + "$SHELLSPEC_TRAP" '' INT -case $millisec_end in (*[!0-9]*) - millisec_end=0 -esac + real='' user='' sys='' type=$SHELLSPEC_TIME_TYPE ex='' + # shellcheck disable=SC2162 -sec=$(($sec_end - $sec_start)) -millisec=$((1$millisec_end - 1$millisec_start)) -if [ $millisec -lt 0 ]; then - millisec=$(($millisec + 100)) - sec=$(($sec - 1)) -fi -[ $millisec -lt 10 ] && millisec="0$millisec" + while read name time; do + # ksh88: 1m2.34s + case $time in *m*s) + type=ksh88 + min=${time%m*} && time=${time#*m} + sec=${time%.*} && time=${time#*.} && time=${time%s} + time="$(($min * 60 + $sec)).$time" + esac -echo "real $sec.$millisec" >&2 + case $name in + real) real=$time ;; + user) user=$time ;; + sys) sys=$time ;; + start) start=$time ;; + end) end=$time ;; + status) ex=$time ;; + esac + done -exit $status + if [ ! "$real" ]; then + datetime2unixtime start "$start" + datetime2unixtime end "$end" + real=$((${end%.*} - ${start%.*})) + case $start in + *[!0-9.]*) ;; + *) + start="${start#*.}00" end="${end#*.}00" + start="1${start%"${start#??}"}" end="3${end%"${end#??}"}" + diff=$(($end - $start)) + real="$(( $real - ($diff < 200) )).${diff#[12]}" + esac + fi + if [ "${SHELLSPEC_TIME_LOG:+x}" ]; then + exec 2>"$SHELLSPEC_TIME_LOG" + fi + echo "real:${real:-0} user:$user sys:$sys type:$type" >&2 + exit "${ex:-1}" + ) +} 3>&2 4>&1 diff --git a/shellspec b/shellspec index 58667f24..2925c510 100755 --- a/shellspec +++ b/shellspec @@ -38,7 +38,6 @@ export SHELLSPEC_BUILTIN_READARRAY='' export SHELLSPEC_SEEKABLE='' export SHELLSPEC_READ_DELIM='' export SHELLSPEC_STRING_CONCAT='' -export SHELLSPEC_TIME='' export SHELLSPEC_LIST='' export SHELLSPEC_COUNT_FILE='' export SHELLSPEC_DEBUG_TRAP='' @@ -130,6 +129,8 @@ export SHELLSPEC_REPORTERLIB="$SHELLSPEC_LIB/libexec/reporter" export SHELLSPEC_LIBEXEC="$SHELLSPEC_ROOT/libexec" export SHELLSPEC_INSPECTION="$SHELLSPEC_LIBEXEC/shellspec-inspection.sh" export SHELLSPEC_UNREADONLY_PATH="$SHELLSPEC_LIBEXEC/shellspec-unreadonly-path.sh" +export SHELLSPEC_TIME_TYPE=auto +export SHELLSPEC_TIME="$SHELLSPEC_LIBEXEC/shellspec-time.sh" # shellcheck source=lib/libexec/shellspec.sh . "$SHELLSPEC_LIB/libexec/shellspec.sh" @@ -410,17 +411,6 @@ fi command_path SHELLSPEC_FIND "find" ||: command_path SHELLSPEC_OD "od" ||: command_path SHELLSPEC_HEXDUMP "hexdump" ||: - - if command_path "time" || [ "$SHELLSPEC_BUSYBOX_W32" ]; then - SHELLSPEC_TIME="time -p" - else - SHELLSPEC_TIME="$SHELLSPEC_LIBEXEC/shellspec-time.sh" - if command_path bash; then - SHELLSPEC_TIME="bash $SHELLSPEC_TIME" - elif command_path ksh; then - SHELLSPEC_TIME="ksh $SHELLSPEC_TIME" - fi - fi } if ! signal 0 $$ 2>/dev/null; then diff --git a/spec/libexec/reporter_spec.sh b/spec/libexec/reporter_spec.sh index 36d00a25..f2c83f78 100644 --- a/spec/libexec/reporter_spec.sh +++ b/spec/libexec/reporter_spec.sh @@ -28,10 +28,11 @@ Describe "libexec/reporter.sh" Describe "read_time_log()" Before "prefix_real=0 prefix_user=0 prefix_sys=0" It "does not read anything if file missing" - When call read_time_log prefix "$FILE.not-exits" + When call read_time_log prefix "$FILE.not-exists" The variable prefix_real should eq '' The variable prefix_user should eq '' The variable prefix_sys should eq '' + The variable prefix_type should eq '' The status should be failure End @@ -40,6 +41,7 @@ Describe "libexec/reporter.sh" The variable prefix_real should equal 1.23 The variable prefix_user should equal 0.11 The variable prefix_sys should equal 12.45 + The variable prefix_type should equal external The status should be success End End