Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shellspec can not collect data with the profile option enabled #274

Closed
lutzmad opened this issue Aug 4, 2022 · 34 comments
Closed

Shellspec can not collect data with the profile option enabled #274

lutzmad opened this issue Aug 4, 2022 · 34 comments

Comments

@lutzmad
Copy link

lutzmad commented Aug 4, 2022

Hello,
I try to use the profile option, but the formatter get no proper data.

# shellspec --profile spec/em_spec.sh
Running: /bin/bash [bash 5.1.4(1)-release]
............/lib/shellspec/lib/general.sh: line 538: * 10: syntax error: operand expected (error token is "* 10")
Aborted with status code [executor: 0] [reporter: 0] [error handler: 102]
Fatal error occurred, terminated with exit status 102.

This is a problem in shellspec_shift10() because "$2" is empty.
Therefore, I try find the way the function will be called.

But this ended because I can not find the code where "$time_real" filled with proper values.
See /lib/shellspec/lib/libexec/reporter/profiler_formatter.sh in profiler_end() in the line

  read_profiler callback "$profiler_tick_total" "$time_real" \
    < "$SHELLSPEC_PROFILER_LOG" \
    > "$SHELLSPEC_PROFILER_REPORT"

The time_real is empty, is ''. If I assign "0" to the empty "$time_real" I get no useful report but Shellspec finished without an error.

My question, where the variable "$time_real" came from?

Sorry, I can not find, what is going wrong,
Lutz

p.s.
I use shellspec 0.28.1 dist version.

@LukeSavefrogs
Copy link
Contributor

LukeSavefrogs commented Nov 11, 2022

I currently have the same issue

  • Are you running shellspec using Cygwin on Windows?
  • Do you see this line (near the end) when running it?
    [...]
    Finished in ? seconds (user ? seconds, sys ? seconds)
    [...]
    

I think it's something related to the timing (see following output from --help) since i always see the lines mentioned before.

$ shellspec -h | grep profile
    -p, --{no-}profile              Enable profiling and list the slowest examples [default: disabled]

@LukeSavefrogs
Copy link
Contributor

LukeSavefrogs commented Nov 11, 2022

My question, where the variable "$time_real" came from?

Those variables seems to be generated here:

eval "$1_real='' $1_user='' $1_sys=''"

TL;DR;

Run export LC_ALL=C before running the shellspec command and the profiler will work as expected. 😃

🐞 Bug?

As you can see in the screenshot below, in my current situation if i run shellspec:

  • Without --profile: No error, but at the same time no timing is shown (Finished in ? seconds (user ? seconds, sys ? seconds))
  • With --profile: Fatal error, shellspec breaks as the OP already said before, also no timing is shown.

image

However... Look what happens if i first change my locale to the POSIX locale:
image

Now everything works as expected!

When does it happen?

It seems that this problem happens when i run shellcheck with locales different than the English (which the C/POSIX defaults to)... If i set the locale to one that is not installed on my system, the fallback seems to always be the C locale (POSIX) so it works too.

image

LocaleInstalledWorking?
it_ITYesNo
es_ESYesNo
fr_FRYesNo
en_USYesYes
ja_JPNoYes
ko_KRNoYes
CAlwaysYes

As you can see the only language that works all the time is the C locale (this link provides interesting info). We need to check WHAT causes this behaviour to change based on the selected locale.

I'll try to look into this problem and see what i can find, but i think only @ko1nksm can give us a real answer...

🖥 System Info

I'm using Cygwin on Windows 10 19044:

$ uname -a
CYGWIN_NT-10.0-19044 Desktop-Luca 3.3.6-341.x86_64 2022-09-05 11:15 UTC x86_64 Cygwin

🚧 Workaround

Run export LC_ALL=C before running the shellspec command.

@LukeSavefrogs
Copy link
Contributor

LukeSavefrogs commented Nov 11, 2022

UPDATE

I did a little dive after a ☕ and I think i have found what is the problem...

As I suspected before, the bug is indeed in the read_time_log function:

# $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\" ]"
}

In the line 25 it is checked if the second space-separated value is a number, and if so, it is skipped:

case ${line##*[ $TAB]} in (*[!0-9.]*) continue; esac

By adding set -x at the start of the function we can see what's happening:

+ set +x
Line: real 0,99
+ eval 'time_real='\'''\'' time_user='\'''\'' time_sys='\'''\'''
Line: user 0,24
++ time_real=
Line: sys 0,48
++ time_user=
++ time_sys=
+ '[' -r /tmp/shellspec.1668189685.7137/.shellspec-time.log ']'
+ IFS=
+ read -r line
+ echo 'Line: real 0,99'
+ case $line in
+ case ${line##*[ $TAB]} in
+ continue
+ IFS=
+ read -r line
+ echo 'Line: user 0,24'
+ case $line in
+ case ${line##*[ $TAB]} in
+ continue
+ IFS=
+ read -r line
+ echo 'Line: sys 0,48'
+ case $line in
+ case ${line##*[ $TAB]} in
+ continue
+ IFS=
+ read -r line
+ :
+ eval '[ "$time_real" ] && [ "$time_user" ] && [ "$time_sys" ]'
++ '[' '' ']'
+ set +x

Basically every line is skipped because in these locales (tested with italian and spanish) the decimal separator is the comma (,) and not the period (.), so the glob pattern (*[!0-9.]*) will never match!

This is confirmed by looking at the same output when using the en_US locale:

+ eval 'time_real='\'''\'' time_user='\'''\'' time_sys='\'''\'''
Line: user 0.19
++ time_real=
Line: sys 0.44
++ time_user=
++ time_sys=
+ '[' -r /tmp/shellspec.1668189953.7388/.shellspec-time.log ']'
+ IFS=
+ read -r line
+ echo 'Line: real 0.96'
+ case $line in
+ case ${line##*[ $TAB]} in
+ eval 'time_real="${line##*[ $TAB]}"'
++ time_real=0.96
+ IFS=
+ read -r line
+ echo 'Line: user 0.19'
+ case $line in
+ case ${line##*[ $TAB]} in
+ eval 'time_user="${line##*[ $TAB]}"'
++ time_user=0.19
+ IFS=
+ read -r line
+ echo 'Line: sys 0.44'
+ case $line in
+ case ${line##*[ $TAB]} in
+ eval 'time_sys="${line##*[ $TAB]}"'
++ time_sys=0.44
+ IFS=
+ read -r line
+ :
+ eval '[ "$time_real" ] && [ "$time_user" ] && [ "$time_sys" ]'
++ '[' 0.96 ']'
++ '[' 0.19 ']'
++ '[' 0.44 ']'
+ set +x

You can see that this time the eval 'time_user="${line##*[ $TAB]}"' line is correctly executed.

We can easily reproduce the bug by writing this on the console:
image

✅ Solutions

Solution n°1

A quick solution that can solve this "bug" is adding the other decimal separator to the glob pattern:

  • Before: (*[!0-9.]*)
  • After: (*[!0-9.,]*)

case ${line##*[ $TAB]} in (*[!0-9.]*) continue; esac

Tests

Screenshot

Solution n°2

The most correct solution however would be to start the $SHELLSPEC_TIME command with the LC_ALL environment variable already set to C.

$SHELLSPEC_TIME $SHELLSPEC_SHELL "$executor" "$@" 3>&2 2>"$SHELLSPEC_TIME_LOG"

This line would be:

LC_ALL=C $SHELLSPEC_TIME $SHELLSPEC_SHELL "$executor" "$@" 3>&2 2>"$SHELLSPEC_TIME_LOG"
Help needed

This would be the best solution, since it would remove the problem from the root. However, it also could introduce new unexpected bugs, because:

  • I don't really know the implications of setting the LC_ALL variable in other shells;
  • This line is responsible of the initiation of the $SHELLSPEC_SHELL "$executor" script, which (from what i could understand) will actually execute your code. This introduces us to the main problems:
    1. Will the environment variable LC_ALL be propagated to all subprocesses created by the $executor script?
    2. If so, could it be a risk of unexpected values from the end-user point of view (i.e. error messages or numbers not formatted in the expected language)?
    3. Either way, could the fact that ONLY the $executor script has the LC_ALL varable set to C collide with other subshells that could have the user locale (i'm talking about internal shellspec functions)?

To implement this solution we would absolutely need @ko1nksm, since it could break something...

Final result

Now the profiler works with all (hopefully) the locales and the time is shown as it should:
image

@ko1nksm I'll make a PR for the 1st solution as soon as I can 😄

@lutzmad
Copy link
Author

lutzmad commented Jan 30, 2024

Thanks for all the suggestion/help Luca.
Maybe, Koichi Nakashima will spend some time to Shellspec again.

I use Shellspec in a MacOS, Linux and AIX environment.
In my old MacOS system, with Bash 3.2.57, Shellspec create profile data (LANG is de_DE.UTF-8).
In Linux (and AIX) it does not create profile data.

Lutz

Appendage:
The profile data in a MacOS environment.

lutz% shellspec --profile spec/em_spec.sh
Running: /bin/sh [bash 3.2.57(1)-release]
............

Finished in 44.99 seconds (user 1.74 seconds, sys 1.44 seconds)
12 examples, 0 failures

# Top 10 slowest examples of the 12 examples
#   1 30.7176 spec/em_spec.sh:66-83
#   2 10.2671 spec/em_spec.sh:87-92
#   3 1.8422 spec/em_spec.sh:8-12
#   4 0.2166 spec/em_spec.sh:36-42
#   5 0.2000 spec/em_spec.sh:22-27
#   6 0.1997 spec/em_spec.sh:101-105
#   7 0.1790 spec/em_spec.sh:52-58
#   8 0.1763 spec/em_spec.sh:44-50
#   9 0.1692 spec/em_spec.sh:14-18
#  10 0.1593 spec/em_spec.sh:60-64

@LukeSavefrogs
Copy link
Contributor

Maybe, Koichi Nakashima will spend some time to Shellspec again.

I hope so too.

@lutzmad Does #281 fix your issue? How did you resolve the problem otherwise?

@LukeSavefrogs
Copy link
Contributor

In Linux (and AIX) it does not create profile data.

What do you mean? What shell/locale are you using?

@lutzmad
Copy link
Author

lutzmad commented Jan 30, 2024

Hello Luca,
I try to add your suggested modification tomorrow.
I used my MacOS, but Shellspec collect profile data as expected in the MacOS environment.
Lutz

p.s.
I use the Bash shell in the Linux (and AIX) environment.
The LANG is not C, we use de_DE with/without UTF-8 in general.

In a MacOS environment, the time output does not depend to the used LANG.

$ export LANG=de_DE (or en_US)
$ time -p id
uid= ...
real 0.00
user 0.00
sys 0.00

In the AIX ksh environment (my default shell, but I like the ksh, sorry), the time output honour the used LANG. This is the time command used by Shellspec, I think.

# export LANG=en_US
# /usr/bin/time -p id
uid= ...

Real   0.00
User   0.00
System 0.00
# export LANG=de_DE
# /usr/bin/time -p id
uid= ...

real   0,01
user   0,00
sys    0,00

And in the Linux and AIX bash environment, also.

# export LANG=de_DE
# time -p id
uid= ...
real 0,00
user 0,00
sys 0,00
# export LANG=en_US
# time -p id
uid= ...
real 0.00
user 0.00
sys 0.00

@lutzmad
Copy link
Author

lutzmad commented Feb 3, 2024

Hello Luke,
you are right, as long as the US number format is used everything works well.
But sometimes (in some environments, I switched to LANG=en_US), the output from the used "time" command looks different to the expected output.

The output (in a AIX environment) evaluated by read_time_log() echoed to the terminal (see lines prefixed by "#### ").

# shellspec --profile spec/em_spec.sh   Running: /bin/bash [bash 5.1.16(1)-release]
............#### /home/lutz/lib/shellspec/libexec/shellspec-executor.sh: line 9: [: : integer expression expected
####
#### Real   6.86
#### User   0.82
#### System 0.46
#### /home/lutz/lib/shellspec/libexec/shellspec-executor.sh: line 9: [: : integer expression expected
####
#### Real   6.86
#### User   0.82
#### System 0.46


Finished in 6.86 seconds (user 0.82 seconds, sys 0.46 seconds)
12 examples, 0 failures

# Top 10 slowest examples of the 12 examples
#   1 0.7171 spec/em_spec.sh:66-83
#   2 0.6268 spec/em_spec.sh:36-42
#   3 0.6197 spec/em_spec.sh:101-105
#   4 0.5499 spec/em_spec.sh:52-58
#   5 0.5480 spec/em_spec.sh:44-50
#   6 0.5409 spec/em_spec.sh:60-64
#   7 0.5192 spec/em_spec.sh:87-92
#   8 0.4966 spec/em_spec.sh:8-12
#   9 0.4908 spec/em_spec.sh:14-18
#  10 0.4787 spec/em_spec.sh:29-34

And sometimes (in a Linux environment), the output evaluated looks like this (see lines prefixed by "#### ").

> shellspec --profile spec/em_spec.sh
Running: /bin/sh [bash 4.3.48(1)-release]
............#### /usr/local/lib/shellspec/libexec/shellspec-executor.sh: line 9: [: : integer expression expected
#### real 42.09
#### user 1.63
#### sys 0.67
#### /usr/local/lib/shellspec/libexec/shellspec-executor.sh: line 9: [: : integer expression expected
#### real 42.09
#### user 1.63
#### sys 0.67


Finished in 42.09 seconds (user 1.63 seconds, sys 0.67 seconds)
12 examples, 0 failures

# Top 10 slowest examples of the 12 examples
#   1 30.2448 spec/em_spec.sh:66-83
#   2 10.1672 spec/em_spec.sh:87-92
#   3 0.1492 spec/em_spec.sh:36-42
#   4 0.1449 spec/em_spec.sh:44-50
#   5 0.1439 spec/em_spec.sh:52-58
#   6 0.1416 spec/em_spec.sh:101-105
#   7 0.1375 spec/em_spec.sh:14-18
#   8 0.1361 spec/em_spec.sh:8-12
#   9 0.1346 spec/em_spec.sh:60-64
#  10 0.1306 spec/em_spec.sh:22-27

Therefore, my suggested patch to handle the output use some additional lines of code, to handle Linux and AIX environments and your change.

--- shellspec/lib/libexec/reporter.sh.orig      2021-01-11 06:58:16.000000000 +0100
+++ shellspec/lib/libexec/reporter.sh   2024-02-02 10:58:14.827350190 +0100
@@ -17,15 +17,25 @@
# $1: prefix, $2: filename
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]}\""
+    case $line in ([Rr]eal[\ $TAB]*|[Uu]ser[\ $TAB]*|[Ss]ys[\ $TAB]*|[Ss]ystem[\ $TAB]*)
+      case ${line##*[ $TAB]} in (*[!0-9.,]*) continue; esac
+      case $line in
+        [Rr]eal[\ $TAB]*)
+          eval "$1_real=\"\${line##*[ \$TAB]}\"";;
+        [Uu]ser[\ $TAB]*)
+          eval "$1_user=\"\${line##*[ \$TAB]}\"";;
+        [Ss]ys[\ $TAB]*)
+          eval "$1_sys=\"\${line##*[ \$TAB]}\"";;
+        [Ss]ystem[\ $TAB]*)
+          eval "$1_sys=\"\${line##*[ \$TAB]}\"";;
+        *) eval "$1_${line%%[ $TAB]*}=\"\${line##*[ \$TAB]}\"";;
+      esac
     esac
   done < "$2" &&:
   eval "[ \"\$$1_real\" ] && [ \"\$$1_user\" ] && [ \"\$$1_sys\" ]"
}

field_description() {

Based on your suggested patch (handle '.' and ',') and some additional lines Shellspec is able to profile the scripts in Linux, AIX (and MacOS) enviroments, in some more/bad weather conditions.

Nice, thanks for your investigations,
Lutz

@LukeSavefrogs
Copy link
Contributor

LukeSavefrogs commented May 7, 2024

Therefore, my suggested patch to handle the output use some additional lines of code, to handle Linux and AIX environments and your change.

Sorry for the late reply, did you test your solution using the integrated tests?

Can you provide a PR to my fork updating both the code and tests? This way it will be already in the PR of this repo

@LukeSavefrogs
Copy link
Contributor

LukeSavefrogs commented May 7, 2024

you are right, as long as the US number format is used everything works well. But sometimes (in some environments, I switched to LANG=en_US), the output from the used "time" command looks different to the expected output.

However something that i do not understand is how is this possible... The documentation for AIX time command clearly shows that the output is the same as linux:

real      10.5
user      0.3
sys       3.6

@lutzmad Could you provide more context?

@ko1nksm
Copy link
Member

ko1nksm commented May 7, 2024

@LukeSavefrogs Thanks for your help. And I'm sorry for neglecting you guys for so long.

Perhaps #281 can be merged without problems. ShellSpec must work in all locales. I did not pay enough attention to the locale when I wrote that code. Shells and commands have historically respected locales. However, not all.

The time command is built into the shell in some shells and not in others. In ksh, the time command invokes the built-in shell when no options are specified, but invokes an external command when options are specified. That is very confusing.

bash is usually invoked by the shell built-in time command. Its behavior differs from that of the external (GNU) time command.

$ bash -c 'export LC_ALL=de_DE.UTF-8; time -p true'
real 0,00
user 0,00
sys 0,00

$ bash -c 'export LC_ALL=de_DE.UTF-8; env time -p true'
real 0.00
user 0.00
sys 0.00

@lutzmad
Copy link
Author

lutzmad commented May 7, 2024

Hello, no problem.

However something that i do not understand is how is this possible... The documentation for AIX time command clearly shows that the output is the same as linux:

I will add additional "time" command output to the issue and I try to find out which "time" command is used.
Lutz

@lutzmad
Copy link
Author

lutzmad commented May 15, 2024

Hello,
a check to the TIME behavior in AIX.

7.2.5.201 TL05 - AIX Kernel Version

In the man page I find your information also.

Description

       The time command prints the elapsed time during the execution of a command, time in the system, and execution time of the time command in seconds to standard
       error.

            Note: Sleep time is not charged to either system or user time.

       The time command is also built into the C shell (csh) and Korn shell (ksh) with a different format. To run the time command while in the csh and ksh shells,
       enter:

       /usr/bin/time

:
Examples
       1    To measure the time required to run a program, enter:

            /usr/bin/time -p a.out
            This command runs the program a.out and writes the amount of real, user, and system time to standard error, in the format specified by the -p flag; for
            example:

            real      10.5
            user      0.3
            sys       3.6
       2    To save a record of the time command information in a file, enter:

            /usr/bin/time a.out 2> a.time

Files

       Item
            Description
       /usr/bin/time
            Specifies the path of the time command.

A POSIX Sample (https://pubs.opengroup.org/onlinepubs/9699919799/functions/times.html) use other terms for "real", "user", "sys" also.

Therefore I checked it again based on the suggested "/bin/usr/time" command.

# ls -la /bin/time*
-r-xr-xr-x    1 bin      bin            4150 01 Jun 2015  /bin/time
-rwxr-xr-x    1 root     system          303 12 Jun 2020  /bin/timed-read
-rwxr-xr-x    1 root     system          277 12 Jun 2020  /bin/timed-run
-r-xr-sr-x    1 bin      adm            8709 26 Jul 2021  /bin/timex
#
# ls -la /usr/bin/time*
-r-xr-xr-x    1 bin      bin            4150 Jun 01 2015  /usr/bin/time
-rwxr-xr-x    1 root     system          303 Jun 12 2020  /usr/bin/timed-read
-rwxr-xr-x    1 root     system          277 Jun 12 2020  /usr/bin/timed-run
-r-xr-sr-x    1 bin      adm            8709 Jul 26 2021  /usr/bin/timex

And I find a "time.cat" file, but for "en_US" only.

# ls -la /lib/nls/msg/*/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /lib/nls/msg/en.8859-15/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /lib/nls/msg/en.ISO8859-1/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /lib/nls/msg/en.ISO8859-15/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /lib/nls/msg/en_US.8859-15/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /lib/nls/msg/en_US.ISO8859-1/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /lib/nls/msg/en_US/time.cat
#
# ls -la /usr/lib/nls/msg/*/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en.8859-15/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en.ISO8859-1/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en.ISO8859-15/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en_US.8859-15/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en_US.ISO8859-1/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en_US/time.cat

And the globalization/localization files are used.

# env | grep NLS
NLSPATH=/usr/lib/nls/msg/%L/%N:/usr/lib/nls/msg/%L/%N.cat

Based on the default ("en_US") lang environment.

# env | grep LANG
LANG=en_US
# /usr/bin/time -p sleep 1

Real   1.00
User   0.00
System 0.00
#

And after a switch to an other ("en_DE") lang environment.

# export LANG=de_DE
# env | grep LANG
LANG=de_DE
# /usr/bin/time -p sleep 1

real   1,00
user   0,00
sys    0,00
#

But TIME is a KSH shell internal command also, see man snippet.

# ksh
# time -p sleep 1
ksh: -p:  not found.

real    0m0.00s
user    0m0.00s
sys     0m0.00s

#

And a BASH shell internal command.

# bash
# time -p sleep 1
real 1.00
user 0.00
sys 0.00
#
# export LANG=de_DE
# env | grep LANG
LANG=de_DE
# time -p sleep 1
real 1,00
user 0,00
sys 0,00
#

My conclusion, the man page is right, but globalization/localization is used for the "en_US" lang environment and the used messages/terms are translated to "en_US" therefore.

Shellspec should handle both formats used for "en_US", with and without globalization/localization.

# env | grep LANG
LANG=en_US
# /usr/bin/time -p sleep 1

Real   1.00
User   0.00
System 0.00
#
# unset NLSPATH
# /usr/bin/time -p sleep 1

real   1.00
user   0.00
sys    0.00
#

Lutz

@lutzmad
Copy link
Author

lutzmad commented May 16, 2024

Hello,
the result of the shellspec internal tests, based on shellspec 0.28.1 and my modification in "shellspec/lib/libexec/reporter.sh", for AIX, Linux and MacOS (see below). This fit to the result of the shellspec internal tests without my modification.
Lutz

Appendage:
AIX

# shellspec -s /bin/sh spec (this is a ksh88, the default shell)
Unsupported shell (builtin commands can not redefine).

# shellspec -s /bin/bash spec
Running: /bin/bash [bash 5.2.15(1)-release]
  11) core/subjects/line.sh line subject gets undefined when stdout is undefined
      When run shellspec_subject_line 1 _modifier_

      11.1) WARNING: There was output to stderr but not found expectation

              stderr: /home/j949350/lib/shellspec/lib/core/modifiers/line.sh: line 56: pop_var_context: head of shell_variables not a function context
              /home/j949350/lib/shellspec/lib/core/syntax.sh: line 20: pop_var_context: head of shell_variables not a function context

            # spec/core/subjects/line_spec.sh:26-30

  12) core/subjects/word.sh word subject gets undefined when stdout is undefined
      When run shellspec_subject_word 1 _modifier_

      12.1) WARNING: There was output to stderr but not found expectation

              stderr: /home/j949350/lib/shellspec/lib/core/modifiers/word.sh: line 56: pop_var_context: head of shell_variables not a function context
              /home/j949350/lib/shellspec/lib/core/syntax.sh: line 20: pop_var_context: head of shell_variables not a function context

            # spec/core/subjects/word_spec.sh:24-28

Finished in 109.51 seconds (user 30.65 seconds, sys 4.37 seconds)
1619 examples, 0 failures, 2 warnings, 40 skips (muted 30 skips)

shellspec spec/core/subjects/line_spec.sh:26 # 11) core/subjects/line.sh line subject gets undefined when stdout is undefined WARNED
shellspec spec/core/subjects/word_spec.sh:24 # 12) core/subjects/word.sh word subject gets undefined when stdout is undefined WARNED

# shellspec -s /bin/ksh spec (this is a ksh88, the default shell)
Unsupported shell (builtin commands can not redefine).

# shellspec -s /bin/ksh93 spec
Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]
Finished in 84.36 seconds (user 21.83 seconds, sys 3.99 seconds)
1619 examples, 33 failures, 47 skips (muted 33 skips)

Running: /home/j123450/bin/mksh [mksh @(#)MIRBSD KSH R59 2020/10/31]
Finished in 96.09 seconds (user 27.02 seconds, sys 3.18 seconds)
1619 examples, 0 failures, 47 skips (muted 33 skips)

Linux

# shellspec -s /bin/sh spec
Running: /bin/sh [bash 4.3.48(1)-release]
Finished in 60.87 seconds (user 63.58 seconds, sys 10.65 seconds)
1619 examples, 0 failures, 40 skips (muted 30 skips)

# shellspec -s /bin/bash spec
Running: /bin/bash [bash 4.3.48(1)-release]
Finished in 60.76 seconds (user 63.93 seconds, sys 9.96 seconds)
1619 examples, 0 failures, 40 skips (muted 30 skips)

# shellspec -s /bin/ksh spec
Running: /bin/ksh [ksh Version AJM 93u+ 2012-08-01]
Finished in 27.28 seconds (user 0.01 seconds, sys 0.00 seconds)
1619 examples, 0 failures, 45 skips (muted 33 skips)

# shellspec -s /bin/mksh spec
Running: /bin/mksh [mksh @(#)MIRBSD KSH R50 2015/04/19 SLES]
Finished in 43.89 seconds (user 38.07 seconds, sys 11.15 seconds)
1619 examples, 0 failures, 45 skips (muted 33 skips)

MacOS

# shellspec -s /bin/bash spec
Running: /bin/bash [bash 3.2.57(1)-release]
Finished in 61.00 seconds (user 59.14 seconds, sys 14.88 seconds)
1619 examples, 0 failures, 40 skips (muted 30 skips)

# shellspec -s /usr/local/bin/bash spec
Running: /usr/local/bin/bash [bash 5.1.16(1)-release]
Finished in 53.89 seconds (user 47.26 seconds, sys 13.57 seconds)
1619 examples, 0 failures, 40 skips (muted 30 skips)

# shellspec -s /bin/zsh spec
Running: /bin/zsh [zsh 5.0.5]
Finished in 31.98 seconds (user 26.85 seconds, sys 13.44 seconds)
1619 examples, 0 failures, 45 skips (muted 33 skips)

# shellspec -s /bin/ksh spec
Running: /bin/ksh [ksh Version AJM 93u+ 2012-08-01]
Finished in 31.98 seconds (user 30.80 seconds, sys 9.03 seconds)
1619 examples, 0 failures, 45 skips (muted 33 skips)

# shellspec -s /usr/local/bin/mksh spec
Running: /usr/local/bin/mksh [mksh @(#)MIRBSD KSH R59 2020/10/31]
Finished in 66.55 seconds (user 51.66 seconds, sys 20.67 seconds)
1619 examples, 0 failures, 45 skips (muted 33 skips)

@ko1nksm
Copy link
Member

ko1nksm commented May 16, 2024

Thanks for the report. It is very interesting.

When I wrote these codes, I apparently did not know about the time command. ShellSpec will use time -p in preference to the external command time if it is present.

shellspec/shellspec

Lines 414 to 423 in 166e4f0

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

In ksh, the time command is an internal command, but in some cases an external command is invoked. (sigh)

(Invoke internal command)
$ time sleep 1

real    0m1.00s
user    0m0.00s
sys     0m0.00s

(Attempts to invoke internal commands but no -p option)
$ time -p sleep 1
ksh: -p:  not found

real    0m0.00s
user    0m0.00s
sys     0m0.00s

(Invoke external command)
$ t="time -p"
$ $t sleep 1

real 1.00
user 0.00
sys 0.00

The report shows that /usr/bin/time on AIX uses locale-dependent decimal point symbol. The same applies to bash. My understanding is that NLSPATH is the path to the message catalog and I assume it has nothing to do with the decimal point symbol. This issue will be fixed in merge #281. It will be merged soon.

However, there is another issue. Implementations of the various time commands can change their format via environment variables. The current implementation does not work well when a non-default format is specified. Eventually I am considering addressing this issue.

@ko1nksm
Copy link
Member

ko1nksm commented May 16, 2024

Thanks for the report. The bash warnings are fixed here.

# shellspec -s /bin/sh spec (this is a ksh88, the default shell)
Unsupported shell (builtin commands can not redefine).

This means that the AIX version of the ksh88 is not working at all?
It is working on the Solaris 10 version of ksh88. Apparently there is something wrong.

# shellspec -s /bin/ksh93 spec
Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]
Finished in 84.36 seconds (user 21.83 seconds, sys 3.99 seconds)
1619 examples, 33 failures, 47 skips (muted 33 skips)

Apparently there is also a problem with ksh93t+.
Could you please create a new Issue as these are different issues?

@LukeSavefrogs
Copy link
Contributor

LukeSavefrogs commented May 16, 2024

However, there is another issue. Implementations of the various time commands can change their format via environment variables. The current implementation does not work well when a non-default format is specified. Eventually I am considering addressing this issue.

What about always forcing the time command to report using a standardized locale (eg. LC_ALL=C time sleep 1)?

@lutzmad
Copy link
Author

lutzmad commented May 16, 2024

Hello,
your are right, Koichi Nakashima.

The KSH (93) use an internal time command, this is the reason, IBM suggest to use "/usr/bin/time", similar to MacOS.

The "." and "," depends to the LANG setting.
But LANG and NLSPATH handle the used messages, this is the reason the time command output looks different on AIX for "en_US". Therefore profiling does not work on AIX for "en_US".

To add the "," (like ".") to handle the time output will fix the problem. And shellspec will support profiling in non "en_US" environments, on Linux. An unset to NLSPATH will fix the time command output problem on AIX for "en_US" also.

On AIX shellspec works well with "-s /bin/bash" only, this is the only way to use shellspec.
On MacOS the KSH (93) works well, I think. But I spend no time to find out the problem/reason the KSH (93) does not work on AIX, sorry.

Lutz

@lutzmad
Copy link
Author

lutzmad commented May 17, 2024

Hello,
after unset to NLSLANG and set LANG to C I got some failures on AIX for the ksh93 shellspec test again.

Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]
Finished in 69.73 seconds (user 19.33 seconds, sys 2.91 seconds)
1619 examples, 33 failures, 47 skips (muted 33 skips)

But a test based on mksh works well.

Running: /home/j123450/bin/mksh [mksh @(#)MIRBSD KSH R59 2020/10/31]
Finished in 96.09 seconds (user 27.02 seconds, sys 3.18 seconds)
1619 examples, 0 failures, 47 skips (muted 33 skips)

Nice.

Have a nivce weekend,
Lutz

@lutzmad
Copy link
Author

lutzmad commented May 17, 2024

Hello,
after unset to NLSLANG and set LANG to C I got some failures on AIX for the ksh93 shellspec test again.

Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]
Finished in 69.73 seconds (user 19.33 seconds, sys 2.91 seconds)
1619 examples, 33 failures, 47 skips (muted 33 skips)

But a test based on mksh works well.

Running: /home/j123450/bin/mksh [mksh @(#)MIRBSD KSH R59 2020/10/31]
Finished in 96.09 seconds (user 27.02 seconds, sys 3.18 seconds)
1619 examples, 0 failures, 47 skips (muted 33 skips)

Nice.
Next week I try to do some more tests,
based on a KSH 93t+ on MacOS and KSH 93u+ on AIX. If I can build the proper KSH myself.

Have a nice weekend,
Lutz

ko1nksm added a commit that referenced this issue May 19, 2024
fix: Added support for comma (`,`) as decimal separator - Fix #274
@ko1nksm
Copy link
Member

ko1nksm commented May 25, 2024

@lutzmad

I have registered with IBM cloud and modified it to work with ksh93t+ on AIX 7.2 and ksh93u+ on AIX 7.3. The fixes are included in the latest master. Please note that the problems that did not work with ksh93 are not related to locale issues. This is a bug related to the virtual subshell of ksh93.

There are no plans to support sh (Version M-11/16/88f). This ksh88 is very old (released around 1995?), and although AIX seems to claim that it is POSIX compliant, it probably has bugs in its errexit-related behaviour. It is quite hard to support this.

@LukeSavefrogs
Copy link
Contributor

@lutzmad

I have registered with IBM cloud and modified it to work with ksh93t+ on AIX 7.2 and ksh93u+ on AIX 7.3. The fixes are included in the latest master. Please note that the problems that did not work with ksh93 are not related to locale issues. This is a bug related to the virtual subshell of ksh93.

There are no plans to support sh (Version M-11/16/88f). This ksh88 is very old (released around 1995?), and although AIX seems to claim that it is POSIX compliant, it probably has bugs in its errexit-related behaviour. It is quite hard to support this.

Thank you for taking the time to look into this issue.

It's good to see you back in action 😄

@lutzmad
Copy link
Author

lutzmad commented May 26, 2024

Hello Koichi Nakashima,
nice to see IBM make AIX available via a cloud service for developers.
I was not aware of this option to get access to a IBM AIX system.

You are right, the KSH88 is very old and I do not know the reason IBM still use KSH88 as a default shell. I will start testing in a real AIX system next week, based on the github master.

Last week I make MKSH ([mksh @(#)MIRBSD KSH R59 2020/10/31]) available on AIX and shellspec works well, I can not find errors based on the shellspec tests, the tests are passed only 47 are skipped.

AIX 7.2

shellspec -s /home/j123450/bin/mksh spec

Running: /home/j123450/bin/mksh [mksh @(#)MIRBSD KSH R59 2020/10/31]
Finished in 96.09 seconds (user 27.02 seconds, sys 3.18 seconds)
1619 examples, 0 failures, 47 skips (muted 33 skips)

Linux SLES 12/15 and RHEL 9 (I think)

shellspec -s /bin/mksh spec

Running: /bin/mksh [mksh @(#)MIRBSD KSH R50 2015/04/19 SLES]
Finished in 43.89 seconds (user 38.07 seconds, sys 11.15 seconds)
1619 examples, 0 failures, 45 skips (muted 33 skips)

MacOS

shellspec -s /bin/mksh spec

Running: /bin/mksh [mksh @(#)MIRBSD KSH R59 2020/10/31]
Finished in 69.46 seconds (user 51.00 seconds, sys 23.35 seconds)
1619 examples, 0 failures, 45 skips (muted 33 skips)

I compare the skipped tests to see the differences, for the MKSH.
And I compare the KSH93 based tests on AIX and Linux.

Thank you for taking the time to look into this issue.

It's good to see you back in action

Thanks for all your help/support.
I'll be back next week,
Lutz

@lutzmad
Copy link
Author

lutzmad commented May 27, 2024

Hello (back to MKSH),
tests with MKSH on AIX (compared to Linux and MacOS) also skips the following tests, because "/dev/stdin" does not exist.

  13) ./install.sh prompt() when not pre answered returns the answer
      13.1) SKIPPED: /dev/stdin not exists

            # spec/install_spec.sh:46

  14) ./install.sh confirm() when not pre answered returns the answer
      14.1) SKIPPED: /dev/stdin not exists

            # spec/install_spec.sh:77

Finished in 124.48 seconds (user 27.90 seconds, sys 3.05 seconds)
1619 examples, 0 failures, 47 skips (muted 33 skips)

shellspec spec/install_spec.sh:45 # 13) ./install.sh prompt() when not pre answered returns the answer SKIPPED
shellspec spec/install_spec.sh:76 # 14) ./install.sh confirm() when not pre answered returns the answer SKIPPED

The BASH will simulate it (on AIX), I think.

# test -e /dev/stdin; echo $?
0

But MKSH and KSH test the dev filesystem.

# test -e /dev/stdin; echo $?
1

But the file does not exist.

Nice to see,
Lutz

@LukeSavefrogs
Copy link
Contributor

The BASH will simulate it (on AIX), I think.

# test -e /dev/stdin; echo $?
0

But MKSH and KSH test the dev filesystem.

# test -e /dev/stdin; echo $?
1

But the file does not exist.

Nice to see

Very nice indeed 😳

@lutzmad
Copy link
Author

lutzmad commented May 29, 2024

Sorry, I am late,
based on the master (45b02e0) all old errors are gone in the AIX environment,
but I got a new one (see the following output snippet).

# shellspec -s /bin/ksh93  spec
Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]

libexec/prechecker.sh
  error()
    outputs to stderr
  warn()
    outputs to fd3 (FAILED - 18)
  info()
    outputs to fd4
  abort()
    exits with an error messagels -
    exits with an exit status
    exits with an exit status and error message
  minimum_version()
    checks minimum version
    raises error when the minimum version is not specified
    raises error when an invalid version is specified
    raises error when the minimum version is not met

  18) libexec/prechecker.sh warn() outputs to fd3
      When call warn foo bar

      18.1) The fd 3 should eq [warn] foo bar

              expected: "[warn] foo bar"
                   got: ""

            # spec/libexec/prechecker_spec.sh:17

Finished in ? seconds (user ? seconds, sys ? seconds)
1696 examples, 1 failure, 56 skips (muted 39 skips)


Notable examples: (Listed here do not affect your suite's status)

shellspec spec/core/matchers/be/empty_spec.sh:123 # 1) core/matchers/be/empty.sh be empty directory matcher when enabled failglob in bash matches SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:114 # 2) core/matchers/be/stat.sh be symlink matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:142 # 3) core/matchers/be/stat.sh be pipe matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:169 # 4) core/matchers/be/stat.sh be socket matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:196 # 5) core/matchers/be/stat.sh be readable matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:225 # 6) core/matchers/be/stat.sh be writable matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:253 # 7) core/matchers/be/stat.sh be executable matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:281 # 8) core/matchers/be/stat.sh be block device matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:309 # 9) core/matchers/be/stat.sh be character device matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:14 # 10) core/matchers/have/stat.sh has setgid matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:43 # 11) core/matchers/have/stat.sh has setuid matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:72 # 12) core/matchers/have/stat.sh should have setgid matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:101 # 13) core/matchers/have/stat.sh should have setuid matcher example SKIPPED
shellspec spec/core/subjects/fd_spec.sh:18 # 14) core/subjects/fd.sh fd subject example example SKIPPED
shellspec spec/core/utils_spec.sh:115 # 15) core/utils.sh shellspec_shopt() shopt option sets option SKIPPED
shellspec spec/install_spec.sh:45 # 16) ./install.sh prompt() when not pre answered returns the answer SKIPPED
shellspec spec/install_spec.sh:76 # 17) ./install.sh confirm() when not pre answered returns the answer SKIPPED


Failure examples / Errors: (Listed here affect your suite's status)

shellspec spec/libexec/prechecker_spec.sh:15 # 18) libexec/prechecker.sh warn() outputs to fd3 FAILED

/home/j123450/lib/shellspec/libexec/shellspec-runner.sh[209]: echo: write to 1 failed [A file descriptor does not refer to an open file.]
Aborted with status code [executor: 102] [reporter: ] [error handler: ]
Fatal error occurred, terminated with exit status 102.

Unfortunately I can not find detail information about the new "UseFD" statement used by the test.
Snippet from ./spec/libexec/prechecker_spec.sh

  Describe "warn()"
    UseFD 3
    It 'outputs to fd3'
      When call warn "foo" "bar"
      The fd 3 should eq "[warn] foo bar"
    End
  End

The old test (based on 0.28.1) looks different (and is passed)

  Describe "warn()"
    _warn() { warn "$@" 3>&1 >/dev/null; }

    It 'outputs to fd3'
      When call _warn "foo" "bar"
      The output should eq "[warn] foo bar"
    End
  End

The function "warn" was not changed. See snippet from ./lib/libexec/prechecker.sh

warn() {
  [ $# -gt 0 ] || return 0
  IFS=" $IFS"; "$SHELLSPEC_PRINTF" '[warn] %s\n' "$*" >&3; IFS=" ${IFS#?}"
}

A new file descriptor is created by shellspec_open_file_descriptor() (in lib/shellspec/lib/core/file_descriptor.sh)
but I do not know how to find out what is going wrong (ls -la /proc/$$/fd does not show a descriptor 3).

Any suggestion,
Lutz

@ko1nksm
Copy link
Member

ko1nksm commented May 29, 2024

Thanks for the report. Unfortunately, I cannot reproduce this in my environment.

# oslevel
7.2.0.0
# oslevel -s
7200-05-07-2346

the UseFD is a new feature for testing output to file descriptors. Instead of opening file descriptors with command redirection, they are opened using exec (in eval). Executing commands in eval often reveals bugs of the shell.

lib/core/file_descriptor.sh:

shellspec_open_file_descriptor() {
  eval "exec $1>\"\$2\""
}

The old test (based on 0.28.1) looks different (and is passed)

The test is the same, but rewritten using the new UseFD.

(ls -la /proc/$$/fd does not show a descriptor 3).

Since file descriptors are opened only when needed, it does not shown in a simple way. If it works well, you can find fd 3 by

  Describe "warn()"
    UseFD 3
    _warn() { /bin/sh -c '/bin/ls -la /proc/$$/fd'> /dev/tty; warn "$@"; }

    It 'outputs to fd3'
      When call _warn "foo" "bar"
      The fd 3 should eq "[warn] foo bar"
    End
  End
# /bin/ksh93 ./shellspec -s /bin/ksh93 ./spec/libexec/prechecker_spec.sh
Running: /usr/bin/ksh93 [ksh Version M 93t+ 2009-05-01]
..total 0
dr-x------    1 root     system            0 May 29 09:21 .
dr-xr-xr-x    1 root     system            0 May 29 09:21 ..
c---------    1 root     system        1,  0 May 29 09:21 0
c---------    1 root     system        1,  0 May 29 09:21 1
--w-------    1 root     system            0 May 29 09:21 2
lr-xr-xr-x    1 root     system            0 May 29 09:21 3 -> /proc/9699828/fd/
...............

Finished in 0.43 seconds (user 0.00 seconds, sys 0.00 seconds)
17 examples, 0 failures

The version of ksh93 is the same, but may have changed. This is the information for ksh93 in my environment.

# ls -al /bin/ksh93
-r-xr-xr-x    2 bin      bin         2387068 Aug 03 2023  /bin/ksh93

# shasum /bin/ksh93
6c377b14651f502a9999000eaed7c22b443eb2f1  /bin/ksh93

If you have set environment variables that affect the execution environment, please let us know.


This is a different issue, your time does not seem to be output.

Finished in ? seconds (user ? seconds, sys ? seconds)
1696 examples, 1 failure, 56 skips (muted 39 skips)

It does not appear to be showing here either. I thought I fixed this issue. In this state, there should be an error when enabling the profile option. Any idea what it is?

[edit] See next comment.

I have tried to use smit lang to install additional locales but I get the following error and cannot install. Do you know what it is? I will look into it some more, but I don't know AIX and don't know what to do.

[edit] It appears that the reason is that the installation source directory was not specified.

``` COMMAND STATUS

Command: failed stdout: yes stderr: no

Before command completion, additional instructions may appear below.

installp: The specified device /.cd_98e7ec
is not a valid device or file.
Error: The selected CULTURAL convention and LANGUAGE translation
require the installation of filesets which could not be found
in the current input device or directory.

</details>

@ko1nksm
Copy link
Member

ko1nksm commented May 29, 2024

I may have been able to reproduce it after setting the locale. But I have no more time today. The reason why the time is not shown is probably due to an error.

bash-5.2# LC_ALL=DE_DE /bin/ksh93 ./shellspec -s /bin/ksh93 spec/libexec/prechecker_spec.sh
Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]
.F............../shellspec-master/libexec/shellspec-executor.sh[111]: echo: Schreiben in 1 fehlgeschlagen. [Ein Dateideskriptor verweist nicht auf eine offene Datei.]
/shellspec-master/libexec/shellspec-runner.sh[208]: echo: Schreiben in 1 fehlgeschlagen. [Ein Dateideskriptor verweist nicht auf eine offene Datei.]


Examples:
  1) libexec/prechecker.sh warn() outputs to fd3
     When call warn foo bar

     1.1) The fd 3 should eq [warn] foo bar

            expected: "[warn] foo bar"
                 got: ""

          # spec/libexec/prechecker_spec.sh:17

Finished in ? seconds (user ? seconds, sys ? seconds)
16 examples, 1 failure


Failure examples / Errors: (Listed here affect your suite's status)

shellspec spec/libexec/prechecker_spec.sh:15 # 1) libexec/prechecker.sh warn() outputs to fd3 FAILED

/shellspec-master/libexec/shellspec-runner.sh[209]: echo: Schreiben in 1 fehlgeschlagen. [Ein Dateideskriptor verweist nicht auf eine offene Datei.]
Aborted with status code [executor: 102] [reporter: ] [error handler: ]
Fatal error occurred, terminated with exit status 102.

I don't see why it would affect by the locale. 🤔

@lutzmad
Copy link
Author

lutzmad commented May 29, 2024

Hello,
thanks for your answer. The used AIX environment is similar.

# oslevel
7.2.0.0
# oslevel -s
7200-05-05-2246

Based on your sample to gather some more information, my output looks similar, but the test failed.

# /bin/ksh93 ./shellspec -s /bin/ksh93 spec/libexec/prechecker_spec.sh
Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]
.total 0
dr-x------    1 j123450  user            0 May 29 19:38 .
dr-xr-xr-x    1 j123450  user            0 May 29 19:38 ..
c---------    1 j123450  user        1,  0 May 29 19:37 0
c---------    1 j123450  user        1,  0 May 29 19:37 1
--w-------    1 j123450  user            0 May 29 19:38 2
lr-xr-xr-x    1 j123450  user            0 May 29 19:38 3 -> /proc/17301990/fd/
FF............../home/j123450/shellspec-master/libexec/shellspec-executor.sh[111]: echo: write to 1 failed [Bad file number]
/home/j123450/shellspec-master/libexec/shellspec-runner.sh[208]: echo: write to 1 failed [Bad file number]

The KSH93 version number is the same, but the files size is not the same.

# ls -la /bin/ksh93
-r-xr-xr-x    2 bin      bin         2387302 Aug 26 2022  /bin/ksh93

# shasum /bin/ksh93
a7072e229502350104cce7c136e5c7c063a21a01  /bin/ksh93

The only ugly environment variables are set/used:

TZ=MEZ-1MESZ,M3.5.0/02:00:00,M10.5.0/03:00:00
LANG=en_US
NLSPATH=/usr/lib/nls/msg/%L/%N:/usr/lib/nls/msg/%L/%N.cat
LC_ALL is not set

And "/opt/freeware/bin" is in the path, to make some Linux/GNU tools available in the AIX environment.

The used LANG and NLSPATH variables are the reason for the problem with the "time -p" command output, but this is an other storry. But you can check your NLSPATH to find the translated messages, in your AIX environment.

Finished in ? seconds (user ? seconds, sys ? seconds)
1696 examples, 1 failure, 56 skips (muted 39 skips)

If I check the NLSPATH, I find messages for en_US only.

# ls -la /usr/lib/nls/msg/*/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en.8859-15/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en.ISO8859-1/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en.ISO8859-15/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en_US.8859-15/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en_US.ISO8859-1/time.cat
-rw-r--r--    1 bin      bin             252 Jun 01 2015  /usr/lib/nls/msg/en_US/time.cat

A funny test result, if I do this (based on your last sample).

# LC_ALL=DE_DE /bin/ksh93 ./shellspec -s /bin/ksh93 spec/libexec/prechecker_spec.sh
Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]
.total 0
dr-x------    1 j949350  siuser            0 May 29 19:51 .
dr-xr-xr-x    1 j949350  siuser            0 May 29 19:51 ..
c---------    1 j949350  siuser        1,  0 May 29 19:46 0
c---------    1 j949350  siuser        1,  0 May 29 19:46 1
--w-------    1 j949350  siuser            0 May 29 19:51 2
lr-xr-xr-x    1 j949350  siuser            0 May 29 19:51 3 -> /proc/18612700/fd/
................

Finished in 0.59 seconds (user 0.00 seconds, sys 0.00 seconds)
17 examples, 0 failures

Every think works well. And "LANG=EN_US" work well too, but "LANG=en_US" does not.
With the suggest LANG "DE_DE" or "de_DE" the "time -p" looks like expected, too.

# LANG=DE_DE /bin/time -p sleep 1
real   1.05
user   0.00
sys    0.00

# LANG=en_US /bin/time -p sleep 1
Real   1.00
User   0.00
System 0.00

It is a AIX LANG environment setting/handling problem?
Lutz

@ko1nksm
Copy link
Member

ko1nksm commented May 30, 2024

1. Reasons for not getting the time

Sorry. I had overlooked this information.

# env | grep LANG
LANG=en_US
# /usr/bin/time -p sleep 1

Real   1.00
User   0.00
System 0.00

The AIX time (/usr/bin/time) command outputs locale-dependent names (not just the decimal point symbol) even when the -p option is specified.

bash-5.2# LC_ALL=DE_DE env time -p sleep 0

Real   0,00
Benutzer 0,00
System 0,00

To fix this issue, we need to execute LC_ALL=C (as @LukeSavefrogs said), but it would affect all child processes, so it need to be bring back to previous locale to affect just the time command. I know what needs to be done, but it takes time to fix.

2. echo: write to 1 failed

I mistakenly thought that we could not write to file descriptor 3 specified in UseFD.

/home/j123450/lib/shellspec/libexec/shellspec-runner.sh[209]: echo: write to 1 failed [A file descriptor does not refer to an open file.]

This is probably a bug in ksh93. In my environment, I was able to reproduce the problem with the following code.

$ LC_ALL=DE_DE /bin/ksh93 -c '( ( echo test >&5 | cat ) 5>&1 | cat ) 3>&1 4>&2'
/bin/ksh93[1]: echo: Schreiben in 1 fehlgeschlagen. [Ein Dateideskriptor verweist nicht auf eine offene Datei.]

$ /bin/ksh93 -c '( ( echo test >&5 | cat ) 5>&1 | cat ) 3>&1 4>&2'
test

Adding 5>/dev/null would have avoided this issue.

$ LC_ALL=DE_DE /bin/ksh93 -c '( ( echo test >&5 | cat ) 5>&1 | cat ) 3>&1 4>&2 5>/dev/null'
test

We were able to work around this by fixing the following However, it might be better to rewrite the code to use a temporary file so that &5 is not used.

libexec/shellspec-runner.sh:

set +e
( ( ( ( ( set -e; exec 3>&- 4>&- 5>&-; executor "$@" ); echo $? >&5; ) \
  | ( set -e; reporter "$@" ) >&3; echo $? >&5 ) 2>&1 \
  | ( set -e; error_handler ) >&4; echo $? >&5 ) 5>&1 \
  | (
      read -r xs1; read -r xs2; read -r xs3
      xs='' error='' msg="Aborted with status code"
      for i in "$xs1" "$xs2" "$xs3"; do
        case $i in
          0) continue ;;
          "$SHELLSPEC_FAILURE_EXIT_CODE") [ "$xs" ] || xs=$i ;;
          "$SHELLSPEC_ERROR_EXIT_CODE") xs=$i error=1 && break ;;
          *) [ "${xs#"$SHELLSPEC_FAILURE_EXIT_CODE"}" ] || xs=$i; error=1
        esac
      done
      if [ "$error" ]; then
        error "$msg [executor: $xs1] [reporter: $xs2] [error handler: $xs3]"
      fi
      set_exit_status "${xs:-0}"
    )
) 3>&1 4>&2 5>/dev/null
exit_status=$?

libexec/shellspec-executor.sh:

set +e
( ( ( ( (
  ( set -e; exec 3>&- 4>&- 5>&-; executor "$@" ) ) 2>&1 >&4; echo $? >&5 ) \
  | ( set -e; error_handler ) >&3; echo $? >&5 ) 5>&1) \
  | (
      read -r xs1 && [ "$xs1" -ne 0 ] && exit "$xs1"
      read -r xs2 && [ "$xs2" -ne 0 ] && exit "$xs2"
      exit 0
    )
) 4>&1 5>/dev/null

3. Failure of UseFD

This is the most enigmatic issue. This is probably a bug in ksh93, but the file size being written is very strange.

lib/core/dsl.sh:

  Describe "warn()"
    UseFD 3
    (
      exec 3>/tmp/log.txt
      echo >&3
      /bin/od -tx1 /tmp/log.txt >/dev/tty
      /bin/ls -al "/tmp/log.txt" > /dev/tty
    )
    It 'outputs to fd3'
      When call warn "foo" "bar"
      The fd 3 should eq "[warn] foo bar"
    End
  End

I would have written only newlines (echo >&3), but for some reason a file of 142410 bytes was created instead of 1 byte.

Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]
.0000000  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0426100  00 00 00 00 00 00 00 00 00 0a
0426112
-rw-r--r--    1 root     system       142410 30 Mai 08:53  /tmp/log.txt

For some reason the first part of the file seems to have 00 filled in. I am not sure if there is a workaround for this.

What needs to be fixed

  1. Fix 1. Reasons for not getting the time where the problem is clear.
    • Rewrite shellspec-time.sh
  2. Fix 2. echo: write to 1 failed problem so that it does not occur.
  3. Look for workarounds to 3. Failure of UseFD.

@ko1nksm ko1nksm reopened this May 30, 2024
@lutzmad
Copy link
Author

lutzmad commented May 30, 2024

Hello,
nice to see a german language pack create german messages, the NLS packages are not installated in our environments, the "en_US" language is installed only, by default.

bash-5.2# LC_ALL=DE_DE env time -p sleep 0

Real   0,00
Benutzer 0,00
System 0,00

In general I use "en_US" or "en_US.UTF-8". This fit well (I thought) in the past, but I try do some tests based on "C", tomorrow.

From my point of view the "/bin/time -p" problem can not fixed, as long as the NLS messages are used, the messages can not handled properly. The fix from LukeSavefrogs fix the "."/"," problem, but if a NLS package is available, the messages are not properly handled, because values are not properly assigned.

But "C" is a good idea.

Lutz

@lutzmad
Copy link
Author

lutzmad commented May 31, 2024

Hello,
based on the shellepc master (45b02e0) and "LANG=C" the tests are passed successfully with the KSH93 also.

LANG=C shellspec -s /bin/ksh93 spec
Running: /bin/ksh93 [ksh Version M 93t+ 2009-05-01]
Finished in 50.40 seconds (user 0.05 seconds, sys 0.01 seconds)
1696 examples, 0 failures, 56 skips (muted 39 skips)


Notable examples: (Listed here do not affect your suite's status)

shellspec spec/core/matchers/be/empty_spec.sh:123 # 1) core/matchers/be/empty.sh be empty directory matcher when enabled failglob in bash matches SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:114 # 2) core/matchers/be/stat.sh be symlink matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:142 # 3) core/matchers/be/stat.sh be pipe matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:169 # 4) core/matchers/be/stat.sh be socket matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:196 # 5) core/matchers/be/stat.sh be readable matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:225 # 6) core/matchers/be/stat.sh be writable matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:253 # 7) core/matchers/be/stat.sh be executable matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:281 # 8) core/matchers/be/stat.sh be block device matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:309 # 9) core/matchers/be/stat.sh be character device matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:14 # 10) core/matchers/have/stat.sh has setgid matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:43 # 11) core/matchers/have/stat.sh has setuid matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:72 # 12) core/matchers/have/stat.sh should have setgid matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:101 # 13) core/matchers/have/stat.sh should have setuid matcher example SKIPPED
shellspec spec/core/subjects/fd_spec.sh:18 # 14) core/subjects/fd.sh fd subject example example SKIPPED
shellspec spec/core/utils_spec.sh:115 # 15) core/utils.sh shellspec_shopt() shopt option sets option SKIPPED
shellspec spec/install_spec.sh:45 # 16) ./install.sh prompt() when not pre answered returns the answer SKIPPED
shellspec spec/install_spec.sh:76 # 17) ./install.sh confirm() when not pre answered returns the answer SKIPPED

The tests with the KSH93 looks similar to the BASH (version 5.2!) tests now.

LANG=C shellspec -s /bin/bash spec
Running: /bin/bash [bash 5.2.15(1)-release]
Finished in 114.48 seconds (user 32.27 seconds, sys 4.44 seconds)
1696 examples, 0 failures, 49 skips (muted 36 skips)


Notable examples: (Listed here do not affect your suite's status)

shellspec spec/core/matchers/be/stat_spec.sh:114 # 1) core/matchers/be/stat.sh be symlink matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:142 # 2) core/matchers/be/stat.sh be pipe matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:169 # 3) core/matchers/be/stat.sh be socket matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:196 # 4) core/matchers/be/stat.sh be readable matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:225 # 5) core/matchers/be/stat.sh be writable matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:253 # 6) core/matchers/be/stat.sh be executable matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:281 # 7) core/matchers/be/stat.sh be block device matcher example SKIPPED
shellspec spec/core/matchers/be/stat_spec.sh:309 # 8) core/matchers/be/stat.sh be character device matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:14 # 9) core/matchers/have/stat.sh has setgid matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:43 # 10) core/matchers/have/stat.sh has setuid matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:72 # 11) core/matchers/have/stat.sh should have setgid matcher example SKIPPED
shellspec spec/core/matchers/have/stat_spec.sh:101 # 12) core/matchers/have/stat.sh should have setuid matcher example SKIPPED
shellspec spec/core/subjects/fd_spec.sh:18 # 13) core/subjects/fd.sh fd subject example example SKIPPED

Based on "LANG=C" no NLSPATH is found in my AIX environment, therefore no messages are translated and the expected message text is found. The runtime time values are available also.

Have a nice weekend,
Lutz

p.s.
Nice to see, how easy a (inappropriate) LANG setting confuse lots of scripts.
On the other hand, I know this problem, some of the used Java environments/application switch to "LANG=de_DE" or "LANG=de_DE.UTF-8", therefore some messages are in german and somtimes partial messages are in german only.
However, I was not aware that "LANG=en_US" does not necessarily use the default message text.

ko1nksm added a commit that referenced this issue Jun 5, 2024
This is the following workaround
#274 (comment)

But the bug where user time and sys time were not being
measured correctly has also been fixed.
@ko1nksm
Copy link
Member

ko1nksm commented Jun 5, 2024

Fixes were added to master.

  1. Reasons for not getting the time
  2. echo: write to 1 failed (ksh93)
  3. Failure of UseFD (ksh93)
    • pending (This issue has not been fixed)

The time should be measured correctly and the profiler should work correctly.

# LC_ALL=DE_DE bash ./shellspec --profile --no-banner spec/semver_spec.sh
Running: /usr/bin/bash [bash 5.2.15(1)-release]
...............................................

Finished in 2.113 seconds (user: 0.457s, sys: 0.031s) [time: bash-builtin]
47 examples, 0 failures

# Top 10 slowest examples of the 47 examples
#   1 0.0441 spec/semver_spec.sh:56-60
#   2 0.0430 spec/semver_spec.sh:88-91
#   3 0.0393 spec/semver_spec.sh:88-91
#   4 0.0382 spec/semver_spec.sh:49-52
#   5 0.0378 spec/semver_spec.sh:49-52
#   6 0.0361 spec/semver_spec.sh:88-91
#   7 0.0321 spec/semver_spec.sh:25-28
#   8 0.0315 spec/semver_spec.sh:88-91
#   9 0.0313 spec/semver_spec.sh:88-91
#  10 0.0313 spec/semver_spec.sh:88-91

Bonus: The reimplemented locale-independent shellspec-time.sh can be run alone.

# LC_ALL=DE_DE libexec/shellspec-time.sh sleep 1
real:1.10 user:0.00 sys:0.00 type:ksh88-builtin

# LC_ALL=DE_DE ksh93 libexec/shellspec-time.sh sleep 1
real:1.000 user:0.000 sys:0.000 type:ksh93-builtin

# LC_ALL=DE_DE bash libexec/shellspec-time.sh locale
LANG=en_US
LC_COLLATE="DE_DE"
LC_CTYPE="DE_DE"
LC_MONETARY="DE_DE"
LC_NUMERIC="DE_DE"
LC_TIME="DE_DE"
LC_MESSAGES="DE_DE"
LC_ALL=DE_DE
real:0.004 user:0.001 sys:0.001 type:bash-builtin

ko1nksm added a commit that referenced this issue Jun 6, 2024
Workaround for file descriptor malfunction when specifying locale

See below
#274
@ko1nksm
Copy link
Member

ko1nksm commented Jun 6, 2024

  • Failure of UseFD (ksh93)

However, it is not clear whether this fix has completely fixed the issue.

@ko1nksm ko1nksm closed this as completed Jun 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants