2021-05-25 18:31:47 +02:00
|
|
|
name: CI
|
|
|
|
|
|
|
|
on:
|
|
|
|
push:
|
|
|
|
paths-ignore:
|
|
|
|
- '.devcontainer/**'
|
|
|
|
- 'examples/**'
|
|
|
|
- 'lib/**'
|
|
|
|
- 'man/**'
|
|
|
|
- 'priv/**'
|
|
|
|
- '**.md'
|
|
|
|
pull_request:
|
|
|
|
paths-ignore:
|
|
|
|
- '.devcontainer/**'
|
|
|
|
- 'examples/**'
|
|
|
|
- 'lib/**'
|
|
|
|
- 'man/**'
|
|
|
|
- 'priv/**'
|
|
|
|
- '**.md'
|
|
|
|
|
|
|
|
jobs:
|
|
|
|
|
|
|
|
tests:
|
|
|
|
name: Tests
|
|
|
|
strategy:
|
|
|
|
fail-fast: false
|
|
|
|
matrix:
|
2024-05-23 11:04:05 +02:00
|
|
|
otp: ['20.0', '25', '26', '27']
|
2022-09-02 11:28:02 +02:00
|
|
|
runs-on: ubuntu-20.04
|
2021-05-25 18:31:47 +02:00
|
|
|
services:
|
|
|
|
redis:
|
|
|
|
image: redis
|
|
|
|
ports:
|
|
|
|
- 6379:6379
|
|
|
|
|
|
|
|
steps:
|
|
|
|
|
2023-09-11 08:16:03 +02:00
|
|
|
- uses: actions/checkout@v4
|
2021-05-25 18:31:47 +02:00
|
|
|
|
2021-12-22 17:52:54 +01:00
|
|
|
- name: Test shell scripts
|
2024-02-14 21:27:00 +01:00
|
|
|
if: matrix.otp == '26'
|
2021-12-22 17:52:54 +01:00
|
|
|
run: |
|
|
|
|
shellcheck test/ejabberd_SUITE_data/gencerts.sh
|
|
|
|
shellcheck tools/captcha.sh
|
|
|
|
shellcheck ejabberd.init.template
|
2021-12-23 12:11:30 +01:00
|
|
|
shellcheck -x ejabberdctl.template
|
2021-12-22 17:52:54 +01:00
|
|
|
|
2022-02-22 12:11:21 +01:00
|
|
|
- name: Get specific Erlang/OTP
|
2022-09-02 11:28:02 +02:00
|
|
|
uses: erlef/setup-beam@v1
|
2021-05-25 18:31:47 +02:00
|
|
|
with:
|
|
|
|
otp-version: ${{ matrix.otp }}
|
|
|
|
|
2022-09-02 11:28:02 +02:00
|
|
|
- name: Get a compatible Rebar3
|
2024-02-19 16:04:54 +01:00
|
|
|
if: matrix.otp < 24
|
2022-04-27 13:31:14 +02:00
|
|
|
run: |
|
2022-09-02 11:28:02 +02:00
|
|
|
rm rebar3
|
|
|
|
wget https://github.com/processone/ejabberd/raw/21.12/rebar3
|
|
|
|
chmod +x rebar3
|
2022-04-27 13:31:14 +02:00
|
|
|
|
2023-01-20 21:51:42 +01:00
|
|
|
- name: Install MS SQL Server
|
|
|
|
run: |
|
|
|
|
docker run -d -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=ejabberd_Test1" \
|
|
|
|
-v $(pwd)/test/docker/db/mssql/initdb/initdb_mssql.sql:/initdb_mssql.sql:ro \
|
|
|
|
-v $(pwd)/sql/mssql.sql:/mssql.sql:ro \
|
|
|
|
-v $(pwd)/sql/mssql.new.sql:/mssql.new.sql:ro \
|
|
|
|
-p 1433:1433 --name ejabberd-mssql "mcr.microsoft.com/mssql/server:2019-latest"
|
|
|
|
sleep 10
|
|
|
|
|
2021-05-25 18:31:47 +02:00
|
|
|
- name: Prepare databases
|
|
|
|
run: |
|
2024-08-08 11:33:30 +02:00
|
|
|
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql
|
|
|
|
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql
|
2021-05-25 18:31:47 +02:00
|
|
|
sudo systemctl start mysql.service
|
|
|
|
sudo systemctl start postgresql.service
|
2023-01-20 18:41:09 +01:00
|
|
|
mysql -u root -proot -e "CREATE DATABASE ejabberd_test;"
|
2021-05-25 18:31:47 +02:00
|
|
|
mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost'
|
|
|
|
IDENTIFIED BY 'ejabberd_test';"
|
|
|
|
mysql -u root -proot -e "GRANT ALL ON ejabberd_test.*
|
|
|
|
TO 'ejabberd_test'@'localhost';"
|
|
|
|
pg_isready
|
2023-01-20 18:41:09 +01:00
|
|
|
sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;"
|
2021-05-25 18:31:47 +02:00
|
|
|
sudo -u postgres psql -c "CREATE USER ejabberd_test
|
|
|
|
WITH PASSWORD 'ejabberd_test';"
|
|
|
|
sudo -u postgres psql -c "GRANT ALL PRIVILEGES
|
|
|
|
ON DATABASE ejabberd_test TO ejabberd_test;"
|
|
|
|
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
|
|
|
TABLES IN SCHEMA public
|
|
|
|
TO ejabberd_test;"
|
|
|
|
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
|
|
|
SEQUENCES IN SCHEMA public
|
|
|
|
TO ejabberd_test;"
|
|
|
|
|
|
|
|
- name: Prepare libraries
|
|
|
|
run: |
|
|
|
|
sudo apt-get -qq update
|
2022-07-26 12:28:07 +02:00
|
|
|
sudo apt-get -y purge libgd3 nginx
|
2021-05-25 18:31:47 +02:00
|
|
|
sudo apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \
|
|
|
|
libsqlite3-dev libwebp-dev libyaml-dev
|
|
|
|
|
2022-08-04 11:46:39 +02:00
|
|
|
- name: Remove syntax_tools from release
|
|
|
|
run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script
|
|
|
|
|
2024-08-26 13:40:19 +02:00
|
|
|
- name: Cache Hex.pm
|
2024-02-01 17:02:44 +01:00
|
|
|
uses: actions/cache@v4
|
2021-05-25 18:31:47 +02:00
|
|
|
with:
|
|
|
|
path: |
|
2021-12-13 15:30:20 +01:00
|
|
|
~/.cache/rebar3/
|
2022-09-02 11:28:02 +02:00
|
|
|
key: ${{matrix.otp}}-${{hashFiles('rebar.config')}}
|
2021-05-25 18:31:47 +02:00
|
|
|
|
2024-12-03 13:44:18 +01:00
|
|
|
- name: Get compatible Rebar binaries
|
|
|
|
if: matrix.otp < 21
|
|
|
|
run: ./rebar3 unlock eredis
|
|
|
|
|
2021-12-13 15:32:26 +01:00
|
|
|
- name: Download test logs
|
2024-02-14 21:27:00 +01:00
|
|
|
if: matrix.otp == '26' && github.repository == 'processone/ejabberd'
|
2021-12-13 15:32:26 +01:00
|
|
|
continue-on-error: true
|
|
|
|
run: |
|
|
|
|
mkdir -p _build/test
|
|
|
|
curl -sSL https://github.com/processone/ecil/tarball/gh-pages |
|
|
|
|
tar -C _build/test --strip-components=1 --wildcards -xzf -
|
|
|
|
rm -rf _build/test/logs/last/
|
2021-05-25 18:31:47 +02:00
|
|
|
|
|
|
|
- name: Compile
|
|
|
|
run: |
|
|
|
|
./autogen.sh
|
2022-09-02 11:28:02 +02:00
|
|
|
./configure --with-rebar=./rebar3 \
|
2021-05-25 18:31:47 +02:00
|
|
|
--prefix=/tmp/ejabberd \
|
|
|
|
--enable-all \
|
|
|
|
--disable-elixir \
|
2022-02-03 19:32:03 +01:00
|
|
|
--disable-mssql \
|
2021-05-25 18:31:47 +02:00
|
|
|
--disable-odbc
|
|
|
|
make
|
|
|
|
|
|
|
|
- run: make install -s
|
|
|
|
- run: make hooks
|
|
|
|
- run: make options
|
|
|
|
- run: make xref
|
2022-09-02 11:28:02 +02:00
|
|
|
- run: make dialyzer
|
2024-06-25 12:04:31 +02:00
|
|
|
- run: make test-eunit
|
2024-08-26 13:19:30 +02:00
|
|
|
- run: make elvis
|
|
|
|
if: matrix.otp >= 23
|
2021-12-13 15:32:26 +01:00
|
|
|
|
2022-02-10 16:53:30 +01:00
|
|
|
- name: Check Production Release
|
|
|
|
run: |
|
|
|
|
make rel
|
|
|
|
RE=_build/prod/rel/ejabberd
|
|
|
|
$RE/bin/ejabberdctl start
|
|
|
|
$RE/bin/ejabberdctl started
|
|
|
|
$RE/bin/ejabberdctl stop
|
|
|
|
$RE/bin/ejabberdctl stopped
|
2022-04-27 13:31:37 +02:00
|
|
|
cat $RE/logs/ejabberd.log
|
|
|
|
grep -q "is stopped in" $RE/logs/ejabberd.log
|
2022-02-10 16:53:30 +01:00
|
|
|
|
2024-09-26 21:43:43 +02:00
|
|
|
- name: Start Development Release
|
2022-02-10 16:53:30 +01:00
|
|
|
run: |
|
|
|
|
make dev
|
|
|
|
RE=_build/dev/rel/ejabberd
|
2024-09-26 21:43:43 +02:00
|
|
|
sed -i 's/starttls_required: true/starttls_required: false/g' $RE/conf/ejabberd.yml
|
2022-02-10 16:53:30 +01:00
|
|
|
$RE/bin/ejabberdctl start
|
|
|
|
$RE/bin/ejabberdctl started
|
2024-09-26 21:43:43 +02:00
|
|
|
$RE/bin/ejabberdctl register admin localhost admin
|
|
|
|
grep -q "is started in" $RE/logs/ejabberd.log
|
|
|
|
|
|
|
|
- name: Run XMPP Interoperability Tests against CI server.
|
2024-09-27 09:55:18 +02:00
|
|
|
if: matrix.otp == '26'
|
2024-10-28 11:06:47 +01:00
|
|
|
continue-on-error: true
|
2024-09-26 21:43:43 +02:00
|
|
|
uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.4.0
|
|
|
|
with:
|
|
|
|
domain: 'localhost'
|
|
|
|
adminAccountUsername: 'admin'
|
|
|
|
adminAccountPassword: 'admin'
|
2024-10-07 12:45:04 +02:00
|
|
|
disabledSpecifications: RFC6121,XEP-0030,XEP-0045,XEP-0054,XEP-0060,XEP-0080,XEP-0115,XEP-0118,XEP-0215,XEP-0347,XEP-0363,XEP-0384
|
2024-09-26 21:43:43 +02:00
|
|
|
|
|
|
|
- name: Stop Development Release
|
|
|
|
if: always()
|
|
|
|
run: |
|
|
|
|
RE=_build/dev/rel/ejabberd
|
2022-02-10 16:53:30 +01:00
|
|
|
$RE/bin/ejabberdctl stop
|
|
|
|
$RE/bin/ejabberdctl stopped
|
2022-04-27 13:31:37 +02:00
|
|
|
cat $RE/logs/ejabberd.log
|
|
|
|
grep -q "is stopped in" $RE/logs/ejabberd.log
|
2022-02-10 16:53:30 +01:00
|
|
|
|
2022-03-25 16:21:40 +01:00
|
|
|
- name: Run tests
|
2021-12-13 15:32:26 +01:00
|
|
|
id: ct
|
|
|
|
run: |
|
|
|
|
(cd priv && ln -sf ../sql)
|
2023-02-23 12:52:55 +01:00
|
|
|
sed -i -e 's/ct:pal/ct:log/' test/suite.erl
|
2021-12-13 15:32:26 +01:00
|
|
|
COMMIT=`echo $GITHUB_SHA | cut -c 1-7`
|
|
|
|
DATE=`date +%s`
|
|
|
|
REF_NAME=`echo $GITHUB_REF_NAME | tr "/" "_"`
|
|
|
|
NODENAME=$DATE@$GITHUB_RUN_NUMBER-$GITHUB_ACTOR-$REF_NAME-$COMMIT
|
|
|
|
LABEL=`git show -s --format=%s | cut -c 1-30`
|
2022-09-02 11:28:02 +02:00
|
|
|
./rebar3 ct --name $NODENAME --label "$LABEL"
|
|
|
|
./rebar3 cover
|
2021-05-25 18:31:47 +02:00
|
|
|
|
|
|
|
- name: Check results
|
2022-03-25 16:21:40 +01:00
|
|
|
if: always() && (steps.ct.outcome != 'skipped' || steps.ct2.outcome != 'skipped')
|
2022-02-10 23:52:32 +01:00
|
|
|
id: ctresults
|
2021-05-25 18:31:47 +02:00
|
|
|
run: |
|
2021-12-14 20:00:10 +01:00
|
|
|
[[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
|
2021-05-25 18:31:47 +02:00
|
|
|
ln `find logs/ -name suite.log` logs/suite.log
|
|
|
|
grep 'TEST COMPLETE' logs/suite.log
|
|
|
|
grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
|
|
|
|
test $(find logs/ -empty -name error.log)
|
|
|
|
|
2021-12-13 15:30:20 +01:00
|
|
|
- name: View logs failures
|
2022-02-10 23:52:32 +01:00
|
|
|
if: failure() && steps.ctresults.outcome == 'failure'
|
2021-12-13 15:30:20 +01:00
|
|
|
run: |
|
|
|
|
cat logs/suite.log | awk \
|
|
|
|
'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
|
|
|
|
find logs/ -name error.log -exec cat '{}' ';'
|
|
|
|
find logs/ -name exunit.log -exec cat '{}' ';'
|
2021-05-25 18:31:47 +02:00
|
|
|
|
|
|
|
- name: Send to coveralls
|
2024-02-14 21:27:00 +01:00
|
|
|
if: matrix.otp == '26'
|
2021-05-25 18:31:47 +02:00
|
|
|
env:
|
|
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
2021-06-03 16:48:10 +02:00
|
|
|
run: |
|
2022-09-02 11:28:02 +02:00
|
|
|
DIAGNOSTIC=1 ./rebar3 as test coveralls send
|
2021-06-03 16:48:10 +02:00
|
|
|
curl -v -k https://coveralls.io/webhook \
|
|
|
|
--header "Content-Type: application/json" \
|
|
|
|
--data '{"repo_name":"$GITHUB_REPOSITORY",
|
|
|
|
"repo_token":"$GITHUB_TOKEN",
|
|
|
|
"payload":{"build_num":$GITHUB_RUN_ID,
|
|
|
|
"status":"done"}}'
|
2021-05-25 18:31:47 +02:00
|
|
|
|
2021-12-13 15:32:26 +01:00
|
|
|
- name: Upload test logs
|
|
|
|
if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd'
|
2024-04-18 18:57:41 +02:00
|
|
|
uses: peaceiris/actions-gh-pages@v4
|
2021-12-13 15:32:26 +01:00
|
|
|
with:
|
|
|
|
publish_dir: _build/test
|
|
|
|
exclude_assets: '.github,lib,plugins'
|
|
|
|
external_repository: processone/ecil
|
|
|
|
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
|
|
|
|
keep_files: true
|
|
|
|
|
|
|
|
- name: View ECIL address
|
|
|
|
if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd'
|
|
|
|
run: |
|
|
|
|
CTRUN=`ls -la _build/test/logs/last | sed 's|.*-> ||'`
|
|
|
|
echo "::notice::View CT results: https://processone.github.io/ecil/logs/$CTRUN/"
|
|
|
|
|
2023-01-20 18:41:09 +01:00
|
|
|
- name: Check for changes to trigger schema upgrade test
|
2024-02-01 17:02:44 +01:00
|
|
|
uses: dorny/paths-filter@v3
|
2023-01-20 18:41:09 +01:00
|
|
|
id: filter
|
|
|
|
with:
|
|
|
|
filters: |
|
|
|
|
sql:
|
|
|
|
- 'sql/**'
|
|
|
|
- 'src/mod_admin_update_sql.erl'
|
|
|
|
|
|
|
|
- name: Prepare for schema upgrade test
|
|
|
|
id: prepupgradetest
|
|
|
|
if: ${{ steps.filter.outputs.sql == 'true' }}
|
|
|
|
run: |
|
|
|
|
[[ -d logs ]] && rm -rf logs
|
|
|
|
[[ -d _build/test/logs ]] && rm -rf _build/test/logs || true
|
|
|
|
sed -i 's|update_sql, false|update_sql, true|g' test/suite.erl
|
2023-01-20 21:51:42 +01:00
|
|
|
- name: Run DB tests on upgraded schema (mssql, mysql, pgsql)
|
|
|
|
run: CT_BACKENDS=mssql,mysql,pgsql make test
|
2023-01-20 18:41:09 +01:00
|
|
|
if: always() && steps.prepupgradetest.outcome != 'skipped'
|
|
|
|
id: ctupgradedschema
|
|
|
|
- name: Check results
|
|
|
|
if: always() && steps.ctupgradedschema.outcome != 'skipped'
|
|
|
|
run: |
|
|
|
|
[[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
|
|
|
|
ln `find logs/ -name suite.log` logs/suite.log
|
|
|
|
grep 'TEST COMPLETE' logs/suite.log
|
|
|
|
grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
|
|
|
|
test $(find logs/ -empty -name error.log)
|
|
|
|
- name: View logs failures
|
|
|
|
if: failure() && steps.ctupgradedschema.outcome != 'skipped'
|
|
|
|
run: |
|
|
|
|
cat logs/suite.log | awk \
|
|
|
|
'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
|
|
|
|
find logs/ -name error.log -exec cat '{}' ';'
|
|
|
|
find logs/ -name exunit.log -exec cat '{}' ';'
|
|
|
|
|
2021-10-15 14:50:51 +02:00
|
|
|
- name: Prepare new schema
|
|
|
|
run: |
|
|
|
|
[[ -d logs ]] && rm -rf logs
|
2021-12-14 20:00:10 +01:00
|
|
|
[[ -d _build/test/logs ]] && rm -rf _build/test/logs || true
|
2024-08-08 11:33:30 +02:00
|
|
|
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop database [ejabberd_test];"
|
|
|
|
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop login [ejabberd_test];"
|
2021-10-15 14:50:51 +02:00
|
|
|
mysql -u root -proot -e "DROP DATABASE ejabberd_test;"
|
|
|
|
sudo -u postgres psql -c "DROP DATABASE ejabberd_test;"
|
2024-08-08 11:33:30 +02:00
|
|
|
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql
|
|
|
|
docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.new.sql
|
2021-10-15 14:50:51 +02:00
|
|
|
mysql -u root -proot -e "CREATE DATABASE ejabberd_test;"
|
|
|
|
mysql -u root -proot -e "GRANT ALL ON ejabberd_test.*
|
|
|
|
TO 'ejabberd_test'@'localhost';"
|
|
|
|
sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;"
|
|
|
|
sudo -u postgres psql -c "GRANT ALL PRIVILEGES
|
|
|
|
ON DATABASE ejabberd_test TO ejabberd_test;"
|
|
|
|
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
|
|
|
TABLES IN SCHEMA public
|
|
|
|
TO ejabberd_test;"
|
|
|
|
sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
|
|
|
|
SEQUENCES IN SCHEMA public
|
|
|
|
TO ejabberd_test;"
|
2022-08-04 11:46:39 +02:00
|
|
|
sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl
|
2023-01-20 21:51:42 +01:00
|
|
|
- name: Run DB tests on new schema (mssql, mysql, pgsql)
|
|
|
|
run: CT_BACKENDS=mssql,mysql,pgsql make test
|
2021-12-14 13:52:16 +01:00
|
|
|
id: ctnewschema
|
2021-10-15 14:50:51 +02:00
|
|
|
- name: Check results
|
2021-12-14 13:52:16 +01:00
|
|
|
if: always() && steps.ctnewschema.outcome != 'skipped'
|
2021-10-15 14:50:51 +02:00
|
|
|
run: |
|
2021-12-14 20:00:10 +01:00
|
|
|
[[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
|
2021-10-15 14:50:51 +02:00
|
|
|
ln `find logs/ -name suite.log` logs/suite.log
|
|
|
|
grep 'TEST COMPLETE' logs/suite.log
|
|
|
|
grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
|
|
|
|
test $(find logs/ -empty -name error.log)
|
2021-12-13 15:30:20 +01:00
|
|
|
- name: View logs failures
|
2021-12-14 13:52:16 +01:00
|
|
|
if: failure() && steps.ctnewschema.outcome != 'skipped'
|
2021-12-13 15:30:20 +01:00
|
|
|
run: |
|
|
|
|
cat logs/suite.log | awk \
|
|
|
|
'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
|
|
|
|
find logs/ -name error.log -exec cat '{}' ';'
|
|
|
|
find logs/ -name exunit.log -exec cat '{}' ';'
|
2023-02-22 16:45:38 +01:00
|
|
|
|
|
|
|
- name: Upload CT logs
|
|
|
|
if: failure()
|
2024-02-12 07:33:42 +01:00
|
|
|
uses: actions/upload-artifact@v4
|
2023-02-22 16:45:38 +01:00
|
|
|
with:
|
|
|
|
name: ejabberd-ct-logs-${{matrix.otp}}
|
|
|
|
#
|
|
|
|
# Appending the wildcard character ("*") is a trick to make
|
|
|
|
# "ejabberd-packages" the root directory of the uploaded ZIP file:
|
|
|
|
#
|
|
|
|
# https://github.com/actions/upload-artifact#upload-using-multiple-paths-and-exclusions
|
|
|
|
#
|
|
|
|
path: _build/test/logs
|
|
|
|
retention-days: 14
|