. "$BST_LIB"
# BST-TIMEOUT: 300 # ~12 wayland-scanner spawns over fixtures the suite writes,
# plus five bounded probe phases. Nothing here is large.
# wayland 1.22.0 -- the Wayland core: libwayland-client, libwayland-server,
# libwayland-cursor and wayland-scanner.
#
# THE PORT SPLITS THREE WAYS ON THIS IMAGE and the suite is organised by that
# split rather than by library, because two thirds of it is decided by the
# platform and not by wayland:
#
# 1. wayland-scanner RUNS. It is a pure XML-to-C transformation over expat and
# libxml2 and touches neither epoll nor libffi, so every assertion about it
# is an assertion about this build. It gets the bulk of the cases.
# 2. libwayland-client MARSHALS but cannot DISPATCH. Every inbound event
# reaches its listener through wl_closure_invoke -> ffi_call, and ffi_call
# on this arm is a stub that aborts -- HardwareJS has no runtime
# dynamic-call substrate (linux-in-the-browser#180), which is the same gap
# libffi's own port is labelled with. The outbound half is untouched by it
# and is measured in full.
# 3. libwayland-SERVER cannot start at all. wl_display_create() builds a
# wl_event_loop, wl_event_loop_create() is epoll_create1() with no
# alternative backend, and epoll is ENOSYS here (brintos/linux#53).
#
# Both gaps are asserted TWICE, and the pair is deliberate. The first case is a
# WITNESS: it asserts the exact failure the platform produces today, so a
# regression in either direction -- a different errno, a reworded abort, a call
# that silently starts returning garbage -- is caught. The second is an
# attributed `not ok' naming the capability that is missing, so the gap stays
# visible in the report and flips green by itself when the cited defect closes.
# A witness alone would read as full coverage of a library that cannot run.
#
# EVERY FIXTURE IS WRITTEN BY THIS FILE. Nothing is read from a compositor, a
# peer or a terminal, and no protocol XML is taken from wayland-protocols --
# the point of the scanner cases is to vary one construct at a time, which a
# shipped protocol cannot do.
tmp=${TMPDIR:-/tmp}/bst-wayland.$$
mkdir -p "$tmp"
# The first result line of a suite is dropped in transport
# (brintos/linux#96), so the first case is one whose loss costs nothing.
bst_ok wayland/suite-canary
# NOT `require': that ends the file, and the probe half below does not depend on
# the scanner at all. A missing scanner should cost the scanner cases, not the
# only coverage libwayland-client has.
if command -v wayland-scanner > /dev/null 2>&1; then
bst_ok wayland/scanner-present
have_scanner=yes
else
bst_skip wayland/scanner-present "wayland-scanner not on this image"
have_scanner=no
fi
if [ "$have_scanner" = yes ]; then
# ===========================================================================
# 1. wayland-scanner -- the code generator, which is the half that runs.
# ===========================================================================
check_out wayland/scanner-version "wayland-scanner 1.22.0" wayland-scanner --version
# --- 1a. The basic fixture: requests, events, an enum, new_id, array, fd. ---
cat > "$tmp/basic.xml" <<'XEOF'
BRINTOS-2226 fixture
A fixture interface.
XEOF
check wayland/scanner-client-header \
wayland-scanner client-header "$tmp/basic.xml" "$tmp/basic-client.h"
check wayland/scanner-server-header \
wayland-scanner server-header "$tmp/basic.xml" "$tmp/basic-server.h"
check wayland/scanner-private-code \
wayland-scanner private-code "$tmp/basic.xml" "$tmp/basic-code.c"
# THE OPCODES ARE THE ORDER OF THE REQUESTS IN THE FILE, and that is the whole
# of the wire contract a client and a compositor have to agree on. A generator
# that renumbered them would produce headers that compile, code that links, and
# a connection in which every request means a different request.
check_out wayland/scanner-opcode-0 "#define BST_THING_POKE 0" cat "$tmp/basic-client.h"
check_out wayland/scanner-opcode-1 "#define BST_THING_SPAWN 1" cat "$tmp/basic-client.h"
check_out wayland/scanner-opcode-2 "#define BST_THING_STASH 2" cat "$tmp/basic-client.h"
# `since' reaches the header as the version floor, per event.
check_out wayland/scanner-since-1 "#define BST_THING_POKED_SINCE_VERSION 1" \
cat "$tmp/basic-client.h"
check_out wayland/scanner-since-2 "#define BST_THING_GONE_SINCE_VERSION 2" \
cat "$tmp/basic-client.h"
# The enum: the entry VALUES, not a renumbering of them. 7 is in the fixture
# precisely because a generator that emitted a bare C enum would turn it into 2.
check_out wayland/scanner-enum-plain "BST_THING_FLAVOUR_PLAIN = 0" cat "$tmp/basic-client.h"
check_out wayland/scanner-enum-salted "BST_THING_FLAVOUR_SALTED = 1" cat "$tmp/basic-client.h"
check_out wayland/scanner-enum-sweet "BST_THING_FLAVOUR_SWEET = 7" cat "$tmp/basic-client.h"
check_out wayland/scanner-enum-guard "#ifndef BST_THING_FLAVOUR_ENUM" \
cat "$tmp/basic-client.h"
# THE TWO HEADERS NUMBER DIFFERENT THINGS UNDER ONE NAME SPACE, and that is the
# trap worth pinning. The client header's bare opcode defines are the REQUESTS
# (poke=0); the server header's are the EVENTS (poked=0, gone=1). So
# BST_THING_POKE exists only on the client side and BST_THING_POKED only on the
# server side, both numbered from zero, and a header included on the wrong side
# fails to compile rather than silently meaning something else -- which is the
# good outcome, and is only true because the names differ.
check_out wayland/scanner-server-event-0 "#define BST_THING_POKED 0" \
cat "$tmp/basic-server.h"
check_out wayland/scanner-server-event-1 "#define BST_THING_GONE 1" \
cat "$tmp/basic-server.h"
check_out wayland/scanner-server-iface \
"extern const struct wl_interface bst_thing_interface" cat "$tmp/basic-server.h"
# The event senders are the server side's entire generated API surface.
check_out wayland/scanner-server-send \
"bst_thing_send_poked(struct wl_resource *resource_, uint32_t serial, const char *what)" \
cat "$tmp/basic-server.h"
# THE SIGNATURE STRING IS THE MARSHALLING CONTRACT. Each letter is one wire
# argument: u uint, n new_id, a array, h fd, s string, and a leading `?' makes
# the argument nullable. `2' on the `gone' event is the since-version, which the
# signature carries as a leading digit rather than as a separate field.
check_out wayland/scanner-sig-poke '{ "poke", "u",' cat "$tmp/basic-code.c"
check_out wayland/scanner-sig-spawn '{ "spawn", "n",' cat "$tmp/basic-code.c"
check_out wayland/scanner-sig-stash '{ "stash", "ah",' cat "$tmp/basic-code.c"
check_out wayland/scanner-sig-poked '{ "poked", "u?s",' cat "$tmp/basic-code.c"
check_out wayland/scanner-sig-gone '{ "gone", "2",' cat "$tmp/basic-code.c"
check_out wayland/scanner-sig-destroy '{ "destroy", "",' cat "$tmp/basic-code.c"
# The interface object the two headers declare and this file defines, with its
# request and event counts.
check_out wayland/scanner-iface-def \
"WL_PRIVATE const struct wl_interface bst_thing_interface" cat "$tmp/basic-code.c"
check_out wayland/scanner-iface-counts "3, bst_thing_requests," cat "$tmp/basic-code.c"
# --- 1b. Every argument type the DTD allows, in one request. ---
cat > "$tmp/alltypes.xml" <<'XEOF'
XEOF
check wayland/scanner-alltypes \
wayland-scanner private-code "$tmp/alltypes.xml" "$tmp/alltypes.c"
# Eight declared arguments, eight signature letters, in declaration order.
check_out wayland/scanner-sig-alltypes '{ "all", "iufsonah",' cat "$tmp/alltypes.c"
check_out wayland/scanner-sig-nullable '{ "nullable", "?s?oa",' cat "$tmp/alltypes.c"
# AN UNTYPED new_id IS THREE WIRE ARGUMENTS, NOT ONE -- interface name, version,
# then the id -- which is why wl_registry.bind carries a string a client has to
# encode correctly. `sun' from three characters of XML is the whole rule, and
# the probe measures the same rule from the other end, as bytes on a socket.
check_out wayland/scanner-sig-untyped '{ "untyped", "sun",' cat "$tmp/alltypes.c"
# The types table is where an interface-typed argument is resolved; a NULL entry
# is an argument with no interface. Two of the three requests contribute one
# each, which is what pins the offsets the wl_message rows point into.
check_out wayland/scanner-types-table "&bst_every_interface," cat "$tmp/alltypes.c"
# --- 1c. What the scanner REFUSES, and how. ---
# Not well-formed XML. MEASURED: the scanner does not exit, it ABORTS --
# src/scanner.c:132 answers a failed xmlCtxtReadFd with abort() rather than
# exit(EXIT_FAILURE), so the caller sees SIGABRT (128+6=134) and a core, not a
# status. That is upstream's shape in 1.22.0 and is asserted at its measured
# value; a release that turns it into a clean exit turns this case red, which is
# the correct way to find out.
printf '\n\n \n' \
> "$tmp/malformed.xml"
mal=$(wayland-scanner client-header "$tmp/malformed.xml" "$tmp/mal.h" 2>&1)
mal_rc=$?
check_eq wayland/scanner-malformed-rc 134 "$mal_rc"
case "$mal" in
*"Failed to read XML"*) bst_ok wayland/scanner-malformed-diag ;;
*) bst_notok wayland/scanner-malformed-diag "$(_bst_1line "$mal")" ;;
esac
# The output file is CREATED AND TRUNCATED before the parse is known to have
# succeeded, so a refusal leaves a zero-length header behind rather than no file
# -- which a build system reading "did the header appear" would take for
# success.
check wayland/scanner-malformed-output-empty test ! -s "$tmp/mal.h"
check wayland/scanner-malformed-output-exists test -e "$tmp/mal.h"
# DTD validation. MEASURED, AND NOT WHAT THE NAME SUGGESTS: a protocol that
# fails the built-in DTD is a WARNING and the scanner exits 0, emitting the code
# anyway. Only --strict turns it into a refusal. Both halves are asserted,
# because the default is the one a build system gets by accident.
cat > "$tmp/invalid.xml" <<'XEOF'
XEOF
inv=$(wayland-scanner client-header "$tmp/invalid.xml" "$tmp/inv.h" 2>&1)
inv_rc=$?
check_eq wayland/scanner-dtd-lax-rc 0 "$inv_rc"
case "$inv" in
*"WARNING: XML failed validation against built-in DTD"*)
bst_ok wayland/scanner-dtd-lax-warns ;;
*) bst_notok wayland/scanner-dtd-lax-warns "$(_bst_1line "$inv")" ;;
esac
case "$inv" in
*"No declaration for element banana"*) bst_ok wayland/scanner-dtd-names-element ;;
*) bst_notok wayland/scanner-dtd-names-element "$(_bst_1line "$inv")" ;;
esac
wayland-scanner --strict client-header "$tmp/invalid.xml" "$tmp/inv2.h" > "$tmp/strict.out" 2>&1
check_eq wayland/scanner-dtd-strict-rc 1 "$?"
# allow-null ON AN ARRAY IS REFUSED BY A MESSAGE THAT SAYS ARRAYS ARE VALID.
# src/scanner.c:411 is_nullable_type() returns true for STRING and OBJECT and
# falls through to false for everything else, while the diagnostic at line 836
# names "objects, strings, and arrays". The wire format has a nullable array --
# a `?a' signature is representable -- so this is a generator that cannot emit
# something the protocol allows, and the error message is the reason nobody
# notices. UPSTREAM DEFECT, asserted at its wrong value: a wayland release that
# repairs either half turns this case red.
cat > "$tmp/arraynull.xml" <<'XEOF'
XEOF
an=$(wayland-scanner private-code "$tmp/arraynull.xml" "$tmp/an.c" 2>&1)
an_rc=$?
check_eq wayland/scanner-array-null-rc 1 "$an_rc"
case "$an" in
*"allow-null is only valid for objects, strings, and arrays"*)
bst_ok wayland/scanner-array-null-diag ;;
*) bst_notok wayland/scanner-array-null-diag "$(_bst_1line "$an")" ;;
esac
# The scanner with no arguments is a usage message and a non-zero exit, not a
# read of stdin that blocks -- which on a target with no terminal would hang the
# suite rather than fail it.
usg=$(wayland-scanner 2>&1)
usg_rc=$?
check_eq wayland/scanner-usage-rc 1 "$usg_rc"
case "$usg" in
*"usage: wayland-scanner"*) bst_ok wayland/scanner-usage-diag ;;
*) bst_notok wayland/scanner-usage-diag "$(_bst_1line "$usg")" ;;
esac
# --- 1d. The SHIPPED protocol, through the shipped scanner. ---
#
# Everything above this point is a fixture. wayland.xml is the real thing --
# the core protocol every Wayland client and compositor is generated from, and
# the same file libwayland-client's own marshalling code was generated from at
# build time. Running the target's scanner over it and getting back the
# signatures the library uses is what ties the two halves of this port together.
REALXML=/usr/share/wayland/wayland.xml
if [ -f "$REALXML" ] && wayland-scanner private-code "$REALXML" "$tmp/real.c" 2>"$tmp/real.err"; then
bst_ok wayland/scanner-real-protocol
check_out wayland/real-sig-sync '{ "sync", "n",' cat "$tmp/real.c"
check_out wayland/real-sig-get-registry '{ "get_registry", "n",' cat "$tmp/real.c"
check_out wayland/real-sig-create-surface '{ "create_surface", "n",' cat "$tmp/real.c"
# THE ONE THAT MATTERS, and the reason the untyped new_id case above is in
# this suite at all: wl_registry.bind is `usun' -- name, then the interface
# STRING and version the untyped new_id expands into, then the id. The probe
# measures the same four arguments from the other end, as bytes a client put
# on a socket. Two derivations of one rule, sharing no code.
check_out wayland/real-sig-bind '{ "bind", "usun",' cat "$tmp/real.c"
check_out wayland/real-iface-display \
"WL_PRIVATE const struct wl_interface wl_display_interface" cat "$tmp/real.c"
check_out wayland/real-iface-compositor \
"WL_PRIVATE const struct wl_interface wl_compositor_interface" cat "$tmp/real.c"
check_out wayland/real-display-request-count "2, wl_display_requests," cat "$tmp/real.c"
else
bst_notok wayland/scanner-real-protocol \
"$(_bst_1line "$(cat "$tmp/real.err" 2>/dev/null)")"
fi
fi # have_scanner
# ===========================================================================
# 2. The shipped tree.
#
# This image installs no -dev and no -staticdev package, so the archives, the
# headers and the .pc files are NOT on the target and are deliberately not
# asserted here -- a `test -f /usr/lib/libwayland-client.a' would be red on
# every image that ever built this recipe. That the archives are LINKABLE is
# established by the probe existing at all: it is cross-linked in do_compile
# against those very archives, and an undefined symbol in a static archive is
# not an error until something links it.
# ===========================================================================
check wayland/scanner-binary test -f /usr/bin/wayland-scanner
check wayland/protocol-xml test -f /usr/share/wayland/wayland.xml
check wayland/protocol-dtd test -f /usr/share/wayland/wayland.dtd
# ===========================================================================
# 3. The probe -- libwayland-client driven against a server that is not
# libwayland-server.
# ===========================================================================
ORACLE=$BST_DATA/wayland-native.txt
check wayland/probe-shipped test -f "$BST_DATA/wayland-probe"
check wayland/oracle-shipped test -f "$ORACLE"
probe=$tmp/wayland-probe
if cp "$BST_DATA/wayland-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then
bst_ok wayland/probe-stage
else
bst_notok wayland/probe-stage "cannot stage $BST_DATA/wayland-probe"
rm -rf "$tmp"
exit 0
fi
# --- 3a. Version, from the headers the probe compiled against. ---
bst_bound 20 "$probe" version > "$tmp/version.out" 2>&1
check_eq wayland/probe-version-rc 0 "$?"
check_out wayland/probe-version "WAYLAND_VERSION=1.22.0" cat "$tmp/version.out"
check_out wayland/probe-version-pv "PROBE_BUILT_AGAINST=1.22.0" cat "$tmp/version.out"
check_out wayland/probe-iface-display "WL_DISPLAY_INTERFACE=wl_display" cat "$tmp/version.out"
check_out wayland/probe-iface-registry "WL_REGISTRY_INTERFACE=wl_registry" cat "$tmp/version.out"
# --- 3b. wl_display_connect's negative paths, which must FAIL rather than block.
#
# A client asked for a compositor that is not there has to come back, and come
# back with an errno the caller can print. This is the one client-library path
# that needs no dispatch and therefore works in full on this image.
bst_bound 20 "$probe" connect > "$tmp/connect.out" 2>&1
check_eq wayland/probe-connect-rc 0 "$?"
# ENOENT (2), from connect(2) on a socket path that does not exist.
check_out wayland/connect-missing-socket "CONNECT_MISSING=null errno=2" cat "$tmp/connect.out"
# No XDG_RUNTIME_DIR: the library substitutes ENOENT ITSELF, deliberately, so
# that a caller does not report "failed to create display: Success".
check_out wayland/connect-no-xdg "CONNECT_NOXDG=null errno=2" cat "$tmp/connect.out"
check_out wayland/connect-no-xdg-diag \
"XDG_RUNTIME_DIR is invalid or not set" cat "$tmp/connect.out"
# WAYLAND_SOCKET naming a descriptor that is not open.
check_out wayland/connect-bad-fd "CONNECT_BADFD=null" cat "$tmp/connect.out"
# --- 3c. Marshalling: the client's bytes, read back by a hand-written server.
#
# This is the half of libwayland-client this image can run, and it is compared
# against the SAME probe built natively at do_compile, phase for phase. Two
# builds of one source agreeing on the flushed byte count, the ids the client
# allocated and the length-prefixed interface string is the strongest statement
# available about an encoder whose failure mode is bytes nobody looks at.
bst_bound 20 "$probe" marshal > "$tmp/marshal.out" 2>&1
check_eq wayland/probe-marshal-rc 0 "$?"
check_out wayland/marshal-fork "M_FORK=0" cat "$tmp/marshal.out"
check_out wayland/marshal-connect "M_CONNECT=ok" cat "$tmp/marshal.out"
check_out wayland/marshal-registry "M_GET_REGISTRY=1" cat "$tmp/marshal.out"
check_out wayland/marshal-bind "M_BIND=1" cat "$tmp/marshal.out"
check_out wayland/marshal-surface "M_CREATE_SURFACE=1" cat "$tmp/marshal.out"
check_out wayland/marshal-sync "M_SYNC=1" cat "$tmp/marshal.out"
check_out wayland/marshal-no-error "M_DISPLAY_ERROR=0" cat "$tmp/marshal.out"
# 76 bytes: get_registry 12, bind 40 (8 header + 4 name + 4+14+2 pad for
# "wl_compositor" + 4 version + 4 new_id), create_surface 12, sync 12. The count
# is asserted rather than merely non-zero because it is the one number that goes
# wrong when a string is padded to the wrong boundary.
check_out wayland/marshal-flush-bytes "M_FLUSH=76" cat "$tmp/marshal.out"
# What the server SAW -- one request of each shape, and nothing it could not
# parse. SRV_UNEXPECTED counts messages that matched no rule; SRV_TAIL counts
# bytes left over at EOF, so a short or over-long size field shows up as a
# remainder rather than as a silent truncation.
check_out wayland/srv-getregistry "SRV_GETREGISTRY=1" cat "$tmp/marshal.out"
check_out wayland/srv-bind "SRV_BIND=1" cat "$tmp/marshal.out"
check_out wayland/srv-bind-name "SRV_BIND_NAME=1" cat "$tmp/marshal.out"
check_out wayland/srv-bind-iface "SRV_BIND_IFACE=wl_compositor" cat "$tmp/marshal.out"
# 14 = strlen("wl_compositor") + 1. The NUL is part of the wire length; the two
# bytes of padding after it are not.
check_out wayland/srv-bind-iface-len "SRV_BIND_IFACE_LEN=14" cat "$tmp/marshal.out"
check_out wayland/srv-bind-version "SRV_BIND_VERSION=4" cat "$tmp/marshal.out"
# The client allocates ids from 2 upward: registry 2, the bound compositor 3,
# the surface 4. That sequence is the client's own id map, asserted from the
# other end of the socket.
check_out wayland/srv-bind-newid "SRV_BIND_NEWID=3" cat "$tmp/marshal.out"
check_out wayland/srv-create-surface "SRV_CREATE_SURFACE=1" cat "$tmp/marshal.out"
check_out wayland/srv-surface-id "SRV_SURFACE_ID=4" cat "$tmp/marshal.out"
check_out wayland/srv-unexpected "SRV_UNEXPECTED=0" cat "$tmp/marshal.out"
check_out wayland/srv-tail "SRV_TAIL=0" cat "$tmp/marshal.out"
# And the whole block against the native build of the same source, byte for
# byte. The wire is host byte order and both arms are little-endian and
# 32-bit-clean for these fields, so anything that differs here is a real
# divergence rather than an arch fact.
sed -n '/^== marshal$/,/^== session$/p' "$ORACLE" | grep -v '^== ' > "$tmp/marshal.want"
grep -v '^== ' "$tmp/marshal.out" > "$tmp/marshal.got"
if [ -s "$tmp/marshal.want" ]; then
if diff "$tmp/marshal.want" "$tmp/marshal.got" > "$tmp/marshal.diff" 2>&1; then
bst_ok wayland/marshal-matches-native
else
bst_notok wayland/marshal-matches-native "$(_bst_1line "$(cat "$tmp/marshal.diff")")"
fi
else
bst_notok wayland/marshal-matches-native "no marshal block in $ORACLE"
fi
# --- 3d. libwayland-server: the witness, and the capability it costs. ---
bst_bound 20 "$probe" server > "$tmp/server.out" 2>&1
check_eq wayland/probe-server-rc 0 "$?"
# ENOSYS is 38. wl_event_loop_create() returns NULL because epoll_create1()
# does, and wl_display_create() returns NULL because it builds an event loop
# first -- so the errno the second reports is still epoll's.
check_out wayland/server-event-loop-null "EVENT_LOOP_CREATE=null errno=38" cat "$tmp/server.out"
check_out wayland/server-display-null "DISPLAY_CREATE=null errno=38" cat "$tmp/server.out"
bst_notok wayland/server-usable \
"libwayland-server cannot start: wl_event_loop_create is epoll_create1, ENOSYS on this kernel -- brintos/linux#53"
# --- 3e. libwayland-client dispatch: the witness, and the capability it costs.
#
# The session phase is the marshal phase plus a round trip. Everything up to the
# first inbound event is asserted here, because it is reached and because a
# phase that dies later must not take the facts it already established with it.
bst_bound 20 "$probe" session > "$tmp/session.out" 2>&1
sess_rc=$?
check_out wayland/session-socketpair "SOCKETPAIR=0" cat "$tmp/session.out"
check_out wayland/session-fork "FORK=0" cat "$tmp/session.out"
check_out wayland/session-connect "CONNECT_TO_FD=ok" cat "$tmp/session.out"
check_out wayland/session-own-fd "DISPLAY_FD_IS_OURS=1" cat "$tmp/session.out"
check_out wayland/session-registry "GET_REGISTRY=1" cat "$tmp/session.out"
# The witness. The text is asserted VERBATIM because libffi's own port treats it
# as an interface -- a future real backend removes the call, it does not reword
# the message -- and because a stub that quietly started returning is the one
# failure this cannot otherwise see.
case "$(cat "$tmp/session.out")" in
*"libffi-wasm32-hwjs: ffi_call: unimplemented (runtime call generation unavailable); aborting"*)
bst_ok wayland/session-ffi-abort-diag ;;
*) bst_notok wayland/session-ffi-abort-diag \
"$(_bst_1line "$(tail -n 3 "$tmp/session.out")")" ;;
esac
# SIGABRT, 128+6. Asserting the status as well as the text is what rules out a
# probe that printed the diagnostic and then carried on.
check_eq wayland/session-ffi-abort-rc 134 "$sess_rc"
# It dies at the FIRST dispatched event and not before: nothing after the
# round trip can have been reached.
case "$(cat "$tmp/session.out")" in
*ROUNDTRIP1=*) bst_notok wayland/session-dies-at-first-event \
"ROUNDTRIP1 returned; dispatch is no longer aborting" ;;
*) bst_ok wayland/session-dies-at-first-event ;;
esac
bst_notok wayland/client-dispatch \
"libwayland-client cannot dispatch: wl_closure_invoke calls ffi_call, a stub on this arm -- linux-in-the-browser#180"
rm -rf "$tmp"