summaryrefslogtreecommitdiff
path: root/installed-tests/scripts/testCommandLine.sh
blob: 62be8243593d62774c6b3257189b4d9bef09a7be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#!/bin/sh
# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
# SPDX-FileCopyrightText: 2016 Endless Mobile, Inc.
# SPDX-FileCopyrightText: 2016 Philip Chimento <philip.chimento@gmail.com>

if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then
    gjs="$TOP_BUILDDIR/gjs-console"
else
    gjs="gjs-console"
fi

# Avoid interference in the profiler tests from stray environment variable
unset GJS_ENABLE_PROFILER

# Avoid interference in the warning tests from G_DEBUG=fatal-warnings/criticals
OLD_G_DEBUG="$G_DEBUG"

# This JS script should exit immediately with code 42. If that is not working,
# then it will exit after 3 seconds as a fallback, with code 0.
cat <<EOF >exit.js
const GLib = imports.gi.GLib;
let loop = GLib.MainLoop.new(null, false);
GLib.idle_add(GLib.PRIORITY_LOW, () => imports.system.exit(42));
GLib.timeout_add_seconds(GLib.PRIORITY_HIGH, 3, () => loop.quit());
loop.run();
EOF

# this JS script fails if either 1) --help is not passed to it, or 2) the string
# "sentinel" is not in its search path
cat <<EOF >help.js
const System = imports.system;
if (imports.searchPath.indexOf('sentinel') == -1)
    System.exit(1);
if (ARGV.indexOf('--help') == -1)
    System.exit(1);
System.exit(0);
EOF

# this JS script should print one string (jobs are run before the interpreter
# finishes) and should not print the other (jobs should not be run after the
# interpreter is instructed to quit)
cat <<EOF >promise.js
const System = imports.system;
Promise.resolve().then(() => {
    print('Should be printed');
    System.exit(42);
});
Promise.resolve().then(() => print('Should not be printed'));
EOF

# this JS script should not cause an unhandled promise rejection
cat <<EOF >awaitcatch.js
async function foo() { throw new Error('foo'); }
async function bar() {
    try {
        await foo();
    } catch (e) {}
}
bar();
EOF

# this JS script should fail to import a second version of the same namespace
cat <<EOF >doublegi.js
import 'gi://Gio?version=2.0';
import 'gi://Gio?version=75.94';
EOF

# this JS script is used to test ARGV handling
cat <<EOF >argv.js
const System = imports.system;

if (System.programPath.endsWith('/argv.js'))
    System.exit(0);
else
    System.exit(1);
EOF

# this JS script is used to test correct exiting from signal callbacks
cat <<EOF >signalexit.js
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import { exit } from 'system';

const Button = GObject.registerClass({
    Signals: {
        'clicked': {},
    },
}, class Button extends GObject.Object {
    go() {
        this.emit('clicked');
    }
});

const button = new Button();
button.connect('clicked', () => exit(15));
let n = 1;
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 2, () => {
    print(\`click \${n++}\`);
    button.go();
    return GLib.SOURCE_CONTINUE;
});
const loop = new GLib.MainLoop(null, false);
loop.run();
EOF

# this is similar to exit.js but should exit with an unhandled promise rejection
cat <<EOF >promiseexit.js
const {GLib} = imports.gi;
const System = imports.system;
const loop = GLib.MainLoop.new(null, false);
Promise.reject();
GLib.idle_add(GLib.PRIORITY_LOW, () => System.exit(42));
GLib.timeout_add_seconds(GLib.PRIORITY_HIGH, 3, () => loop.quit());
loop.run();
EOF

total=0

report () {
    exit_code=$?
    total=$((total + 1))
    if test $exit_code -eq 0; then
        echo "ok $total - $1"
    else
        echo "not ok $total - $1"
    fi
}

report_xfail () {
    exit_code=$?
    total=$((total + 1))
    if test $exit_code -eq 23; then
        echo "not ok $total - $1 (leaked memory)"
    elif test $exit_code -ne 0; then
        echo "ok $total - $1 (exit code $exit_code)"
    else
        echo "not ok $total - $1"
    fi
}

skip () {
    total=$((total + 1))
    echo "ok $total - $1 # SKIP $2"
}

$gjs --invalid-option >/dev/null 2>/dev/null
report_xfail "Invalid option should exit with failure"
$gjs --invalid-option 2>&1 | grep -q invalid-option
report "Invalid option should print a relevant message"

# Test that System.exit() works in gjs-console
$gjs -c 'imports.system.exit(0)'
report "System.exit(0) should exit successfully"
$gjs -c 'imports.system.exit(42)'
test $? -eq 42
report "System.exit(42) should exit with the correct exit code"

# Test the System.programPath works in gjs-console
$gjs argv.js
report "System.programPath should end in '/argv.js' when gjs argv.js is run"

# FIXME: should check -eq 42 specifically, but in debug mode we will be
# hitting an assertion. For this reason, skip when running under valgrind
# since nothing will be freed. Also suppress LSan for the same reason.
echo "# VALGRIND = $VALGRIND"
if test -z $VALGRIND; then
    ASAN_OPTIONS=detect_leaks=0 $gjs exit.js
    test $? -ne 0
    report "System.exit() should still exit across an FFI boundary"

    # https://gitlab.gnome.org/GNOME/gjs/-/issues/417
    output="$(ASAN_OPTIONS=detect_leaks=0 $gjs promiseexit.js 2>&1)"
    test $? -ne 0 && printf '%s' "$output" | grep -q "Unhandled promise rejection"
    report "Unhandled promise rejections should still be printed when exiting"
else
    skip "System.exit() should still exit across an FFI boundary" "running under valgrind"
    skip "Unhandled promise rejections should still be printed when exiting" "running under valgrind"
fi

# ensure the encoding of argv is being properly handled
$gjs -c 'imports.system.exit((ARGV[0] !== "Valentín") ? 1 : 0)' "Valentín"
report "Basic unicode encoding (accents, etc) should be functioning properly for ARGV and imports."
$gjs -c 'imports.system.exit((ARGV[0] !== "☭") ? 1 : 0)' "☭"
report "Unicode encoding for symbols should be functioning properly for ARGV and imports."

# gjs --help prints GJS help
$gjs --help >/dev/null
report "--help should succeed"
test -n "$($gjs --help)"
report "--help should print something"

# print GJS help even if it's not the first argument
$gjs -I . --help >/dev/null
report "should succeed when --help is not first arg"
test -n "$($gjs -I . --help)"
report "should print something when --help is not first arg"

# --help before a script file name prints GJS help
$gjs --help help.js >/dev/null
report "--help should succeed before a script file"
test -n "$($gjs --help help.js)"
report "--help should print something before a script file"

# --help before a -c argument prints GJS help
script='imports.system.exit(1)'
$gjs --help -c "$script" >/dev/null
report "--help should succeed before -c"
test -n "$($gjs --help -c "$script")"
report "--help should print something before -c"

# --help after a script file name is passed to the script
$gjs -I sentinel help.js --help
report "--help after script file should be passed to script"
test -z "$($gjs -I sentinel help.js --help)"
report "--help after script file should not print anything"

# --help after a -c argument is passed to the script
script='if(ARGV[0] !== "--help") imports.system.exit(1)'
$gjs -c "$script" --help
report "--help after -c should be passed to script"
test -z "$($gjs -c "$script" --help)"
report "--help after -c should not print anything"

# -I after a program is not consumed by GJS
# Temporary behaviour: still consume the argument, but give a warning
# "$gjs" help.js --help -I sentinel
# report_xfail "-I after script file should not be added to search path"
# fi
G_DEBUG=$(echo "$G_DEBUG" | sed -e 's/fatal-warnings,\{0,1\}//')
$gjs help.js --help -I sentinel 2>&1 | grep -q 'Gjs-WARNING.*--include-path'
report "-I after script should succeed but give a warning"
$gjs -c 'imports.system.exit(0)' --coverage-prefix=foo --coverage-output=foo 2>&1 | grep -q 'Gjs-WARNING.*--coverage-prefix'
report "--coverage-prefix after script should succeed but give a warning"
$gjs -c 'imports.system.exit(0)' --coverage-prefix=foo --coverage-output=foo 2>&1 | grep -q 'Gjs-WARNING.*--coverage-output'
report "--coverage-output after script should succeed but give a warning"
rm -f foo/coverage.lcov
G_DEBUG="$OLD_G_DEBUG"

for version_arg in --version --jsversion; do
    # --version and --jsversion work
    $gjs $version_arg >/dev/null
    report "$version_arg should work"
    test -n "$($gjs $version_arg)"
    report "$version_arg should print something"

    # --version and --jsversion after a script go to the script
    script="if(ARGV[0] !== '$version_arg') imports.system.exit(1)"
    $gjs -c "$script" $version_arg
    report "$version_arg after -c should be passed to script"
    test -z "$($gjs -c "$script" $version_arg)"
    report "$version_arg after -c should not print anything"
done

# --profile
rm -f gjs-*.syscap foo.syscap
$gjs -c 'imports.system.exit(0)' && ! stat gjs-*.syscap > /dev/null 2>&1
report "no profiling data should be dumped without --profile"

# Skip some tests if built without profiler support
if $gjs --profile -c 1 2>&1 | grep -q 'Gjs-Message.*Profiler is disabled'; then
    reason="profiler is disabled"
    skip "--profile should dump profiling data to the default file name" "$reason"
    skip "--profile with argument should dump profiling data to the named file" "$reason"
    skip "GJS_ENABLE_PROFILER=1 should enable the profiler" "$reason"
else
    rm -f gjs-*.syscap
    $gjs --profile -c 'imports.system.exit(0)' && stat gjs-*.syscap > /dev/null 2>&1
    report "--profile should dump profiling data to the default file name"
    rm -f gjs-*.syscap
    $gjs --profile=foo.syscap -c 'imports.system.exit(0)' && test -f foo.syscap
    report "--profile with argument should dump profiling data to the named file"
    rm -f foo.syscap && rm -f gjs-*.syscap
    GJS_ENABLE_PROFILER=1 $gjs -c 'imports.system.exit(0)' && stat gjs-*.syscap > /dev/null 2>&1
    report "GJS_ENABLE_PROFILER=1 should enable the profiler"
    rm -f gjs-*.syscap
fi

# interpreter handles queued promise jobs correctly
output=$($gjs promise.js)
test $? -eq 42
report "interpreter should exit with the correct exit code from a queued promise job"
test -n "$output" -a -z "${output##*Should be printed*}"
report "interpreter should run queued promise jobs before finishing"
test -n "${output##*Should not be printed*}"
report "interpreter should stop running jobs when one calls System.exit()"

$gjs -c "Promise.resolve().then(() => { throw new Error(); });" 2>&1 | grep -q 'Gjs-WARNING.*Unhandled promise rejection.*[sS]tack trace'
report "unhandled promise rejection should be reported"
test -z "$($gjs awaitcatch.js)"
report "catching an await expression should not cause unhandled rejection"
# https://gitlab.gnome.org/GNOME/gjs/issues/18
G_DEBUG=$(echo "$G_DEBUG" | sed -e 's/fatal-warnings,\{0,1\}//')
$gjs -c "(async () => await true)(); void foobar;" 2>&1 | grep -q 'ReferenceError: foobar is not defined'
report "main program exceptions are not swallowed by queued promise jobs"
G_DEBUG="$OLD_G_DEBUG"

# https://gitlab.gnome.org/GNOME/gjs/issues/26
$gjs -c 'new imports.gi.Gio.Subprocess({argv: ["true"]}).init(null);'
report "object unref from other thread after shutdown should not race"

# https://gitlab.gnome.org/GNOME/gjs/issues/212
if test -n "$ENABLE_GTK"; then
    G_DEBUG=$(echo "$G_DEBUG" | sed -e 's/fatal-warnings,\{0,1\}//' -e 's/fatal-criticals,\{0,1\}//')
    $gjs -c 'imports.gi.versions.Gtk = "3.0";
             const Gtk = imports.gi.Gtk;
             const GObject = imports.gi.GObject;
             Gtk.init(null);
             let BadWidget = GObject.registerClass(class BadWidget extends Gtk.Widget {
                vfunc_destroy() {};
             });
             let w = new BadWidget ();'
    report "avoid crashing when GTK vfuncs are called on context destroy"
    G_DEBUG="$OLD_G_DEBUG"
else
    skip "avoid crashing when GTK vfuncs are called on context destroy" "GTK disabled"
fi

# https://gitlab.gnome.org/GNOME/gjs/-/issues/322
$gjs --coverage-prefix=$(pwd) --coverage-output=$(pwd) awaitcatch.js
grep -q TN: coverage.lcov
report "coverage prefix is treated as an absolute path"
rm -f coverage.lcov

$gjs -m doublegi.js 2>&1 | grep -q 'already loaded'
report "avoid statically importing two versions of the same module"

# https://gitlab.gnome.org/GNOME/gjs/-/issues/19
echo "# VALGRIND = $VALGRIND"
if test -z $VALGRIND; then
    ASAN_OPTIONS=detect_leaks=0 output=$($gjs -m signalexit.js)
    test $? -eq 15
    report "exit with correct code from a signal callback"
    test -n "$output" -a -z "${output##*click 1*}"
    report "avoid asserting when System.exit is called from a signal callback"
    test -n "${output##*click 2*}"
    report "exit after first System.exit call in a signal callback"
else
    skip "exit with correct code from a signal callback" "running under valgrind"
    skip "avoid asserting when System.exit is called from a signal callback" "running under valgrind"
    skip "exit after first System.exit call in a signal callback" "running under valgrind"
fi

rm -f exit.js help.js promise.js awaitcatch.js doublegi.js argv.js \
    signalexit.js promiseexit.js

echo "1..$total"