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 347
|
#!/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/cjs-console"
else
gjs="cjs-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 cjs-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 cjs-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 'Cjs-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 'Cjs-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 'Cjs-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 'Cjs-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 'Cjs-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
output=$(env LSAN_OPTIONS=detect_leaks=0 ASAN_OPTIONS=detect_leaks=0 \
$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"
|