2015-08-05 06:23:59 +00:00
|
|
|
use base "anacondatest";
|
2015-01-26 14:58:07 +00:00
|
|
|
use strict;
|
2019-08-16 00:51:57 +00:00
|
|
|
use lockapi;
|
2015-01-26 14:58:07 +00:00
|
|
|
use testapi;
|
2017-01-18 07:15:44 +00:00
|
|
|
use utils;
|
2019-11-05 20:13:33 +00:00
|
|
|
use tapnet;
|
2017-01-18 07:15:44 +00:00
|
|
|
use anaconda;
|
2015-01-26 14:58:07 +00:00
|
|
|
|
2024-03-06 22:15:39 +00:00
|
|
|
sub _handle_incomplete_hub {
|
|
|
|
if (match_has_tag "anaconda_main_hub_keyboard_layout_incomplete") {
|
|
|
|
# workaround IoT/osbuild issue
|
|
|
|
# https://github.com/osbuild/images/issues/309
|
|
|
|
# by visiting the incomplete spokes
|
2024-03-08 16:49:52 +00:00
|
|
|
click_lastmatch;
|
2024-03-06 22:15:39 +00:00
|
|
|
wait_still_screen 3;
|
|
|
|
assert_and_click "anaconda_spoke_done";
|
2024-03-08 16:49:52 +00:00
|
|
|
# for animation
|
|
|
|
wait_still_screen 3;
|
2024-03-06 22:15:39 +00:00
|
|
|
assert_and_click "anaconda_main_hub_time_date_incomplete";
|
|
|
|
wait_still_screen 3;
|
|
|
|
assert_and_click "anaconda_spoke_done";
|
|
|
|
wait_still_screen 3;
|
|
|
|
send_key "shift-tab";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-26 14:58:07 +00:00
|
|
|
sub run {
|
2016-05-04 18:53:11 +00:00
|
|
|
my $self = shift;
|
2024-05-13 16:19:45 +00:00
|
|
|
my $arch = get_var("ARCH");
|
2023-11-22 00:23:25 +00:00
|
|
|
if (get_var("IS_PXE")) {
|
2019-08-16 00:51:57 +00:00
|
|
|
# PXE tests have DELAYED_START set, so VM is not running yet,
|
|
|
|
# because if we boot immediately PXE will time out waiting for
|
|
|
|
# DHCP before the support server is ready. So we wait here for
|
|
|
|
# support server to be ready, then go ahead and start the VM
|
|
|
|
mutex_lock "support_ready";
|
|
|
|
mutex_unlock "support_ready";
|
|
|
|
resume_vm;
|
|
|
|
}
|
|
|
|
|
add a cockpit realmd FreeIPA join test
Summary:
This requires a few other changes:
* turn clone_host_resolv into clone_host_file, letting you clone
any given host file (cloning /etc/hosts seems to make both
server deployment and client enrolment faster/more reliable)
* allow loading of multiple POSTINSTALL tests (so we can share
the freeipa_client_postinstall test). Note this is compatible,
existing uses will work fine
* move initial password change for the IPA test users into the
server deployment test (so the client tests don't conflict over
doing that)
* add GRUB_POSTINSTALL, for specifying boot parameters for boot of
the installed system, and make it work by tweaking _console_wait
_login (doesn't work for _graphical_wait_login yet, as I didn't
need that)
* make the static networking config for tap tests into a library
function so the tests can share it
* handle ABRT problem dirs showing up in /var/spool/abrt as well
as /var/tmp/abrt (because the enrol attempt hits #1330766 and
the crash report shows up in /var/spool/abrt, don't ask me why
the difference, I just work here)
* specify the DNS servers from the worker host's resolv.conf as
the forwarders for the FreeIPA server when deploying it; if we
don't do this, rolekit defaults to using the root servers as
forwarders(!) and thus we get the public, not phx2-appropriate,
results for e.g. mirrors.fedoraproject.org, some of which the
workers can't reach, so PackageKit package install always fails
(boy, was it fun figuring THAT mess out)
Even after all that, the test still doesn't actually pass, but
I'm reasonably confident this is because it's hitting actual bugs,
not because it's broken. It runs into #1330766 nearly every time
(I think I saw *one* time the enrolment actually succeeded), and
seems to run into a subsequent bug I hadn't seen before when
trying to work around that by trying the join again (see
https://bugzilla.redhat.com/show_bug.cgi?id=1330766#c37 ).
Test Plan:
Run the test, see what happens. If you're really lucky,
it'll actually pass. But you'll probably run into #1330766#c37,
I'm mostly posting for comment. You'll need a tap-capable openQA
instance to test this.
Reviewers: jskladan, garretraziel
Reviewed By: garretraziel
Subscribers: tflink
Differential Revision: https://phab.qadevel.cloud.fedoraproject.org/D880
2016-06-07 20:00:39 +00:00
|
|
|
# construct the kernel params. the trick here is to wind up with
|
|
|
|
# spaced params if GRUB or GRUBADD is set, and just spaces if not,
|
2016-05-04 18:53:11 +00:00
|
|
|
# then check if we got all spaces. We wind up with a harmless
|
|
|
|
# extra space if GRUBADD is set but GRUB is not.
|
add a cockpit realmd FreeIPA join test
Summary:
This requires a few other changes:
* turn clone_host_resolv into clone_host_file, letting you clone
any given host file (cloning /etc/hosts seems to make both
server deployment and client enrolment faster/more reliable)
* allow loading of multiple POSTINSTALL tests (so we can share
the freeipa_client_postinstall test). Note this is compatible,
existing uses will work fine
* move initial password change for the IPA test users into the
server deployment test (so the client tests don't conflict over
doing that)
* add GRUB_POSTINSTALL, for specifying boot parameters for boot of
the installed system, and make it work by tweaking _console_wait
_login (doesn't work for _graphical_wait_login yet, as I didn't
need that)
* make the static networking config for tap tests into a library
function so the tests can share it
* handle ABRT problem dirs showing up in /var/spool/abrt as well
as /var/tmp/abrt (because the enrol attempt hits #1330766 and
the crash report shows up in /var/spool/abrt, don't ask me why
the difference, I just work here)
* specify the DNS servers from the worker host's resolv.conf as
the forwarders for the FreeIPA server when deploying it; if we
don't do this, rolekit defaults to using the root servers as
forwarders(!) and thus we get the public, not phx2-appropriate,
results for e.g. mirrors.fedoraproject.org, some of which the
workers can't reach, so PackageKit package install always fails
(boy, was it fun figuring THAT mess out)
Even after all that, the test still doesn't actually pass, but
I'm reasonably confident this is because it's hitting actual bugs,
not because it's broken. It runs into #1330766 nearly every time
(I think I saw *one* time the enrolment actually succeeded), and
seems to run into a subsequent bug I hadn't seen before when
trying to work around that by trying the join again (see
https://bugzilla.redhat.com/show_bug.cgi?id=1330766#c37 ).
Test Plan:
Run the test, see what happens. If you're really lucky,
it'll actually pass. But you'll probably run into #1330766#c37,
I'm mostly posting for comment. You'll need a tap-capable openQA
instance to test this.
Reviewers: jskladan, garretraziel
Reviewed By: garretraziel
Subscribers: tflink
Differential Revision: https://phab.qadevel.cloud.fedoraproject.org/D880
2016-06-07 20:00:39 +00:00
|
|
|
my $params = "";
|
|
|
|
$params .= get_var("GRUB", "") . " ";
|
|
|
|
$params .= get_var("GRUBADD", "") . " ";
|
2016-05-04 18:53:11 +00:00
|
|
|
# Construct inst.repo arg for REPOSITORY_VARIATION
|
|
|
|
my $repourl = get_var("REPOSITORY_VARIATION");
|
|
|
|
if ($repourl) {
|
2017-01-18 07:15:44 +00:00
|
|
|
$params .= "inst.repo=" . get_full_repo($repourl) . " ";
|
2015-02-04 13:45:37 +00:00
|
|
|
}
|
2019-01-25 13:41:32 +00:00
|
|
|
# Construct inst.addrepo arg for ADD_REPOSITORY_VARIATION
|
|
|
|
my $repourl = get_var("ADD_REPOSITORY_VARIATION");
|
|
|
|
if ($repourl) {
|
2021-08-11 19:15:46 +00:00
|
|
|
$params .= "inst.addrepo=addrepo,$repourl ";
|
2019-01-25 13:41:32 +00:00
|
|
|
}
|
2024-05-13 16:19:45 +00:00
|
|
|
# for update tests
|
|
|
|
if (get_var("ADVISORY_OR_TASK")) {
|
|
|
|
# add workaround repo if there is one
|
|
|
|
$params .= "inst.addrepo=workarounds,nfs://172.16.2.110:/mnt/workarounds_repo " if (get_workarounds);
|
|
|
|
# add tag repo if we're on rawhide
|
|
|
|
if (get_var("VERSION") eq get_var("RAWREL")) {
|
|
|
|
$params .= "inst.addrepo=koji-rawhide,https://kojipkgs.fedoraproject.org/repos/rawhide/latest/${arch} ";
|
|
|
|
}
|
|
|
|
}
|
2018-06-25 00:09:19 +00:00
|
|
|
if (get_var("ANACONDA_TEXT")) {
|
|
|
|
$params .= "inst.text ";
|
2019-11-06 12:55:27 +00:00
|
|
|
# we need this on aarch64 till #1594402 is resolved,
|
|
|
|
# and we also can utilize this if we want to run this
|
|
|
|
# over the serial console.
|
2024-05-13 16:19:45 +00:00
|
|
|
$params .= "console=tty0 " if ($arch eq "aarch64");
|
2019-11-06 12:55:27 +00:00
|
|
|
# when the text installation should run over the serial console,
|
|
|
|
# we have to add some more parametres to grub. Although, the written
|
|
|
|
# test case recommends using ttyS0, OpenQA only uses that console for
|
|
|
|
# displaying information but does not accept key strokes. Therefore,
|
|
|
|
# let us use a real virtio console here.
|
|
|
|
if (get_var("SERIAL_CONSOLE")) {
|
2019-11-20 16:39:28 +00:00
|
|
|
# this is icky. on ppc64 (OFW), virtio-console is hvc1 and
|
|
|
|
# virtio-console1 is hvc2, because the 'standard' serial
|
2019-11-06 12:55:27 +00:00
|
|
|
# terminal is hvc0 (the firmware does this or something).
|
|
|
|
# On other arches, the 'standard' serial terminal is ttyS0,
|
2019-11-20 16:39:28 +00:00
|
|
|
# so virtio-console becomes hvc0 and virtio-console1 is
|
2019-11-06 12:55:27 +00:00
|
|
|
# hvc1. We want anaconda to wind up on the console that is
|
2019-11-20 16:39:28 +00:00
|
|
|
# virtio-console1 in both cases
|
2019-11-06 12:55:27 +00:00
|
|
|
if (get_var("OFW")) {
|
|
|
|
$params .= "console=hvc2 ";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$params .= "console=hvc1 ";
|
|
|
|
}
|
|
|
|
}
|
2018-06-25 00:09:19 +00:00
|
|
|
}
|
2017-01-16 17:30:14 +00:00
|
|
|
# inst.debug enables memory use tracking
|
|
|
|
$params .= "debug" if get_var("MEMCHECK");
|
add a cockpit realmd FreeIPA join test
Summary:
This requires a few other changes:
* turn clone_host_resolv into clone_host_file, letting you clone
any given host file (cloning /etc/hosts seems to make both
server deployment and client enrolment faster/more reliable)
* allow loading of multiple POSTINSTALL tests (so we can share
the freeipa_client_postinstall test). Note this is compatible,
existing uses will work fine
* move initial password change for the IPA test users into the
server deployment test (so the client tests don't conflict over
doing that)
* add GRUB_POSTINSTALL, for specifying boot parameters for boot of
the installed system, and make it work by tweaking _console_wait
_login (doesn't work for _graphical_wait_login yet, as I didn't
need that)
* make the static networking config for tap tests into a library
function so the tests can share it
* handle ABRT problem dirs showing up in /var/spool/abrt as well
as /var/tmp/abrt (because the enrol attempt hits #1330766 and
the crash report shows up in /var/spool/abrt, don't ask me why
the difference, I just work here)
* specify the DNS servers from the worker host's resolv.conf as
the forwarders for the FreeIPA server when deploying it; if we
don't do this, rolekit defaults to using the root servers as
forwarders(!) and thus we get the public, not phx2-appropriate,
results for e.g. mirrors.fedoraproject.org, some of which the
workers can't reach, so PackageKit package install always fails
(boy, was it fun figuring THAT mess out)
Even after all that, the test still doesn't actually pass, but
I'm reasonably confident this is because it's hitting actual bugs,
not because it's broken. It runs into #1330766 nearly every time
(I think I saw *one* time the enrolment actually succeeded), and
seems to run into a subsequent bug I hadn't seen before when
trying to work around that by trying the join again (see
https://bugzilla.redhat.com/show_bug.cgi?id=1330766#c37 ).
Test Plan:
Run the test, see what happens. If you're really lucky,
it'll actually pass. But you'll probably run into #1330766#c37,
I'm mostly posting for comment. You'll need a tap-capable openQA
instance to test this.
Reviewers: jskladan, garretraziel
Reviewed By: garretraziel
Subscribers: tflink
Differential Revision: https://phab.qadevel.cloud.fedoraproject.org/D880
2016-06-07 20:00:39 +00:00
|
|
|
# ternary: set $params to "" if it contains only spaces
|
|
|
|
$params = $params =~ /^\s+$/ ? "" : $params;
|
2015-02-04 13:45:37 +00:00
|
|
|
|
2016-05-04 18:53:11 +00:00
|
|
|
# set mutex wait if necessary
|
|
|
|
my $mutex = get_var("INSTALL_UNLOCK");
|
2015-01-27 12:35:27 +00:00
|
|
|
|
2019-08-16 00:51:57 +00:00
|
|
|
# we need a longer timeout for the PXE boot test
|
2024-01-19 23:24:19 +00:00
|
|
|
my $timeout = 60;
|
2023-11-22 00:23:25 +00:00
|
|
|
$timeout = 120 if (get_var("IS_PXE"));
|
2019-08-16 00:51:57 +00:00
|
|
|
|
2019-11-05 20:13:33 +00:00
|
|
|
# call do_bootloader with postinstall=0, the params, and the mutex,
|
|
|
|
# unless we're a VNC install client (no bootloader there)
|
|
|
|
unless (get_var("VNC_CLIENT")) {
|
2022-07-28 20:32:57 +00:00
|
|
|
do_bootloader(postinstall => 0, params => $params, mutex => $mutex, timeout => $timeout);
|
2019-11-05 20:13:33 +00:00
|
|
|
}
|
2015-02-04 13:05:20 +00:00
|
|
|
|
2019-05-16 13:01:52 +00:00
|
|
|
# Read variables for identification tests (see further).
|
|
|
|
my $identification = get_var('IDENTIFICATION');
|
2016-05-04 18:53:11 +00:00
|
|
|
# proceed to installer
|
2019-11-05 20:13:33 +00:00
|
|
|
if (get_var("KICKSTART") || get_var("VNC_SERVER")) {
|
2016-09-19 23:50:42 +00:00
|
|
|
# wait for the bootloader *here* - in a test that inherits from
|
|
|
|
# anacondatest - so that if something goes wrong during install,
|
2019-10-07 20:24:52 +00:00
|
|
|
# we get anaconda logs. sleep a bit first so we don't get a
|
|
|
|
# match for the installer bootloader if it hangs around for a
|
|
|
|
# while after do_bootloader finishes (in PXE case it does)
|
2022-12-14 20:29:00 +00:00
|
|
|
sleep 60;
|
2018-12-17 22:10:58 +00:00
|
|
|
assert_screen "bootloader", 1800;
|
2016-09-19 23:50:42 +00:00
|
|
|
}
|
|
|
|
else {
|
2016-09-07 08:34:54 +00:00
|
|
|
if (get_var("ANACONDA_TEXT")) {
|
|
|
|
# select that we don't want to start VNC; we want to run in text mode
|
2019-11-06 12:55:27 +00:00
|
|
|
if (get_var("SERIAL_CONSOLE")) {
|
2019-11-20 16:39:28 +00:00
|
|
|
# we direct the installer to virtio-console1, and use
|
|
|
|
# virtio-console as a root console
|
2023-05-02 00:07:06 +00:00
|
|
|
select_console('user-virtio-console');
|
2024-06-21 16:40:17 +00:00
|
|
|
my $match = wait_serial ["Use text mode", "Installation"], timeout => 120;
|
|
|
|
die "Anaconda has not started." unless ($match);
|
|
|
|
if ($match =~ m/Use text mode/) {
|
|
|
|
type_string "2\n";
|
|
|
|
die "Text version of Anaconda has not started." unless (wait_serial "Installation");
|
|
|
|
}
|
2019-11-06 12:55:27 +00:00
|
|
|
}
|
|
|
|
else {
|
2024-06-21 16:15:01 +00:00
|
|
|
assert_screen ["anaconda_use_text_mode", "anaconda_main_hub_text"], 300;
|
|
|
|
if (match_has_tag "anaconda_use_text_mode") {
|
|
|
|
type_string "2\n";
|
|
|
|
assert_screen "anaconda_main_hub_text", 60;
|
|
|
|
}
|
2019-11-06 12:55:27 +00:00
|
|
|
}
|
2019-05-16 13:01:52 +00:00
|
|
|
}
|
|
|
|
else {
|
2016-09-07 08:34:54 +00:00
|
|
|
if (get_var('LIVE')) {
|
2023-02-16 17:10:30 +00:00
|
|
|
# on lives, we have to explicitly launch anaconda
|
2023-08-21 22:32:08 +00:00
|
|
|
my $launched = 0;
|
2021-09-06 15:34:45 +00:00
|
|
|
my $count = 5;
|
|
|
|
while ($count > 0) {
|
|
|
|
$count -= 1;
|
2023-08-21 22:32:08 +00:00
|
|
|
assert_screen ["live_start_anaconda_icon", "apps_menu_button_active", "next_button"], 300;
|
|
|
|
if (match_has_tag "next_button") {
|
|
|
|
# we're on F39+ Workstation and looking at gnome-initial-setup
|
|
|
|
# completing g-i-s launches the installer
|
|
|
|
gnome_initial_setup(live => 1);
|
|
|
|
$launched = 1;
|
|
|
|
}
|
2021-09-06 15:34:45 +00:00
|
|
|
if (match_has_tag "apps_menu_button_active") {
|
|
|
|
# give GNOME some time to be sure it's done starting up
|
|
|
|
# and ready for input
|
|
|
|
wait_still_screen 5;
|
|
|
|
send_key "super";
|
|
|
|
wait_still_screen 5;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
# this means we saw the launcher, which is what we want
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
2023-08-21 22:32:08 +00:00
|
|
|
# if we hit the g-i-s flow we already launched
|
|
|
|
unless ($launched) {
|
|
|
|
# for KDE we need to double-click
|
|
|
|
my $relnum = get_release_number;
|
|
|
|
my $dclick = 0;
|
|
|
|
$dclick = 1 if (get_var("DESKTOP") eq "kde");
|
2024-06-04 00:22:39 +00:00
|
|
|
# FIXME launching the installer sometimes fails on KDE
|
|
|
|
# https://bugzilla.redhat.com/show_bug.cgi?id=2280840
|
|
|
|
my $tries = 5;
|
|
|
|
while ($tries) {
|
|
|
|
$tries -= 1;
|
|
|
|
assert_and_click("live_start_anaconda_icon", dclick => $dclick);
|
2024-06-04 05:55:28 +00:00
|
|
|
last if (check_screen "anaconda_select_install_lang", 180);
|
2024-06-04 00:22:39 +00:00
|
|
|
die "Launching installer failed after 5 tries!" unless ($tries);
|
2023-08-21 22:32:08 +00:00
|
|
|
}
|
2019-12-06 17:22:02 +00:00
|
|
|
}
|
2016-09-07 08:34:54 +00:00
|
|
|
}
|
2023-08-21 22:32:08 +00:00
|
|
|
# wait for anaconda to appear
|
|
|
|
assert_screen ["anaconda_select_install_lang", "anaconda_webui_welcome"], 300;
|
|
|
|
# on webUI path we are done now, also set a var so later
|
|
|
|
# tests know if we're on the webUI path
|
|
|
|
if (match_has_tag "anaconda_webui_welcome") {
|
|
|
|
set_var("_ANACONDA_WEBUI", 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
# we click to work around RHBZ #1566066 if it happens
|
|
|
|
click_lastmatch;
|
2016-09-07 08:34:54 +00:00
|
|
|
my $language = get_var('LANGUAGE') || 'english';
|
2022-07-28 20:32:57 +00:00
|
|
|
assert_and_click("anaconda_select_install_lang", timeout => 300);
|
2021-05-03 15:37:46 +00:00
|
|
|
|
2016-09-07 08:34:54 +00:00
|
|
|
# Select install language
|
2016-09-12 17:24:30 +00:00
|
|
|
wait_screen_change { assert_and_click "anaconda_select_install_lang_input"; };
|
|
|
|
type_safely $language;
|
2016-09-07 08:34:54 +00:00
|
|
|
# Needle filtering in main.pm ensures we will only look for the
|
|
|
|
# appropriate language, here
|
|
|
|
assert_and_click "anaconda_select_install_lang_filtered";
|
2019-03-05 01:32:25 +00:00
|
|
|
assert_screen "anaconda_select_install_lang_selected", 10;
|
2016-09-07 08:34:54 +00:00
|
|
|
assert_and_click "anaconda_select_install_lang_continue";
|
2015-01-30 09:35:13 +00:00
|
|
|
|
2019-01-14 23:22:01 +00:00
|
|
|
# wait 180 secs for hub or Rawhide warning dialog to appear
|
|
|
|
# (per https://bugzilla.redhat.com/show_bug.cgi?id=1666112
|
|
|
|
# the nag screen can take a LONG time to appear sometimes).
|
2018-05-23 23:35:08 +00:00
|
|
|
# If the hub appears, return - we're done now. If Rawhide
|
|
|
|
# warning dialog appears, accept it.
|
2019-01-14 23:22:01 +00:00
|
|
|
if (check_screen ["anaconda_rawhide_accept_fate", "anaconda_main_hub"], 180) {
|
2018-05-23 23:35:08 +00:00
|
|
|
if (match_has_tag("anaconda_rawhide_accept_fate")) {
|
|
|
|
assert_and_click "anaconda_rawhide_accept_fate";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
# this is when the hub appeared already, we're done
|
2024-03-06 22:15:39 +00:00
|
|
|
_handle_incomplete_hub;
|
2018-05-23 23:35:08 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-09-07 08:34:54 +00:00
|
|
|
}
|
2019-05-16 13:01:52 +00:00
|
|
|
|
|
|
|
# If we want to test self identification, in the test suite
|
|
|
|
# we set "identification" to "true".
|
|
|
|
# Here, we will watch for the graphical elements in Anaconda main hub.
|
|
|
|
my $branched = get_var('VERSION');
|
2023-06-19 14:01:39 +00:00
|
|
|
if ($identification eq 'true' or ($branched ne "Rawhide" && $branched ne "ELN")) {
|
2022-07-28 20:32:57 +00:00
|
|
|
check_left_bar(); # See utils.pm
|
2019-05-16 13:01:52 +00:00
|
|
|
check_prerelease();
|
|
|
|
check_version();
|
|
|
|
}
|
2018-05-23 23:35:08 +00:00
|
|
|
# This is where we get to if we accepted fate above, *or*
|
|
|
|
# didn't match anything: if the Rawhide warning didn't
|
|
|
|
# show by now it never will, so we'll just wait for the
|
|
|
|
# hub to show up.
|
2022-07-28 20:32:57 +00:00
|
|
|
assert_screen "anaconda_main_hub", 900;
|
2024-03-06 22:15:39 +00:00
|
|
|
_handle_incomplete_hub;
|
2016-09-07 08:34:54 +00:00
|
|
|
}
|
2015-01-27 12:35:27 +00:00
|
|
|
}
|
2015-01-26 14:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub test_flags {
|
2022-07-28 20:32:57 +00:00
|
|
|
return {fatal => 1};
|
2015-01-26 14:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
1;
|
|
|
|
|
|
|
|
# vim: set sw=4 et:
|