mirror of
https://github.com/Llewellynvdm/Tomb.git
synced 2024-06-27 15:43:30 +00:00
Compare commits
96 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0d06c994cf | ||
|
6c383ffd64 | ||
|
ddb2de6072 | ||
|
3398b7bf89 | ||
|
abe5704658 | ||
|
d0b84d78a0 | ||
|
91adbbe183 | ||
|
766cd27c1b | ||
|
dee2b0f8c4 | ||
|
13eeef7c6c | ||
|
194d60fe9a | ||
|
7f91cc917d | ||
|
09d981f0fc | ||
|
e1afecb832 | ||
|
e97c088a26 | ||
|
9385ddee43 | ||
27792f4421 | |||
8be3163022 | |||
|
21da75adab | ||
|
a0b633fb77 | ||
|
f9ec17d126 | ||
|
dcdf4cb3bb | ||
|
0c8d4cf477 | ||
|
51452e4e6a | ||
|
58035cf14a | ||
|
06ea734878 | ||
|
347462cb0c | ||
|
cd9fa46964 | ||
|
d8989e1955 | ||
|
59d7331cb0 | ||
|
4d72a2180a | ||
|
7249afe98e | ||
|
0eed3cf321 | ||
|
e316ef22cc | ||
|
82e5342334 | ||
|
4e3221e937 | ||
|
364a457fbc | ||
|
571cdeafbf | ||
|
b9cf58054a | ||
|
646d2c33fd | ||
|
713cfd3071 | ||
|
5eaa92e614 | ||
|
d65a3f1586 | ||
|
b5eae55c8b | ||
|
0130fcf86d | ||
|
30d2e92a77 | ||
|
0d004f312d | ||
|
b14be5022e | ||
|
ed841b9f32 | ||
|
6e8ef0a29a | ||
|
5772d39e19 | ||
|
1ae5845c61 | ||
|
fce89023a5 | ||
|
be911b1e16 | ||
|
12684e6740 | ||
|
42390b78c0 | ||
|
3639b2e0ec | ||
|
8502bdc722 | ||
|
e463a6e600 | ||
|
6af298e15f | ||
|
1655fd5a99 | ||
|
8782dab9ed | ||
|
3b44a6be3a | ||
|
7a1b699cea | ||
|
85af9459aa | ||
|
f4e2ae2f97 | ||
|
62806769af | ||
|
8be3e7d2a0 | ||
|
97343cf590 | ||
|
60034b0b55 | ||
|
b7822afaf0 | ||
|
6955719f04 | ||
|
8ceeca8769 | ||
|
03c93ef976 | ||
|
5a5eb6ddcf | ||
|
9323c1caf8 | ||
|
f7046c5941 | ||
|
b6ffe1a2f1 | ||
|
84ef4bef4b | ||
|
e0ba8c5f4d | ||
|
90eec3d830 | ||
|
930b414889 | ||
|
61a9d1a420 | ||
|
d2d35bc8db | ||
|
585af6a61b | ||
|
2a744fe89d | ||
|
b235f16ce4 | ||
|
ce521ed2e2 | ||
|
087ecd25a2 | ||
|
24a89b680d | ||
|
3860487a0b | ||
|
61386ca646 | ||
|
ae21619d04 | ||
|
7f2e22c517 | ||
|
fb3ffcec03 | ||
|
815b8f4218 |
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
github: dyne
|
||||
patreon: dyneorg
|
||||
open_collective: dyne
|
||||
|
45
.github/workflows/linux.yml
vendored
Normal file
45
.github/workflows/linux.yml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
name:
|
||||
💀 Linux Tomb
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'doc/**'
|
||||
- 'portable/**'
|
||||
- '*.md'
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'doc/**'
|
||||
- 'portable/**'
|
||||
- '*.md'
|
||||
branches:
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-matrix:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, ubuntu-20.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install tomb dependencies
|
||||
run: |
|
||||
sudo apt-get update -y -q
|
||||
sudo apt-get install -y -q zsh cryptsetup gpg gawk libgcrypt20-dev steghide qrencode python2 python3-pip python3-dev libssl-dev make gcc sudo gettext bsdmainutils file pinentry-curses xxd libsodium23 libsodium-dev
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build the KDF extras
|
||||
run: |
|
||||
make --directory=extras/kdf-keys
|
||||
sudo make --directory=extras/kdf-keys install
|
||||
- name: Disable swap
|
||||
run: sudo swapoff -a
|
||||
- name: Run main tests
|
||||
run: sudo make test
|
||||
- name: Run KDF tests
|
||||
run: sudo make -C extras/kdf-keys test
|
97
.github/workflows/portable.yml
vendored
Normal file
97
.github/workflows/portable.yml
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
name: ☠️ Portable tomb
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'portable/**'
|
||||
- '.github/workflows/portable.yml'
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
paths:
|
||||
- 'portable/**'
|
||||
- '.github/workflows/portable.yml'
|
||||
branches:
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
ubuntu-20:
|
||||
name: 🐧 test Ubuntu-20
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install portable tomb dependencies
|
||||
run: |
|
||||
sudo apt-get install -q -y make openssl wget fuse3 util-linux
|
||||
sudo make -C portable download-deps-ubuntu20
|
||||
- name: Run portable tomb test
|
||||
run: sudo make -C portable create-open-close
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ubuntu-test.tomb
|
||||
path: portable/test.tomb*
|
||||
retention-days: 1
|
||||
|
||||
freebsd:
|
||||
runs-on: macos-12
|
||||
name: 😈 test FreeBSD-13
|
||||
needs: [ubuntu-20]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Download portable tomb volume built on Ubuntu
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ubuntu-test.tomb
|
||||
path: portable
|
||||
- name: Test in FreeBSD
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v0
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y sudo gmake bash fusefs-libs3 fusefs-lkl e2fsprogs wget
|
||||
wget https://files.dyne.org/tomb3/third-party/veracrypt-freebsd13-amd64
|
||||
mv veracrypt-freebsd13-amd64 /usr/bin/veracrypt
|
||||
wget https://files.dyne.org/tomb3/third-party/zenroom-freebsd13-amd64
|
||||
mv zenroom-freebsd13-amd64 /usr/bin/zenroom
|
||||
chmod a+x /usr/bin/veracrypt /usr/bin/zenroom
|
||||
kldload fusefs
|
||||
run: |
|
||||
sudo gmake -C portable
|
||||
sudo gmake -C portable check-random-data
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: freebsd-test.tomb
|
||||
path: portable/test.tomb*
|
||||
retention-days: 1
|
||||
|
||||
ubuntu-last-cleanup:
|
||||
name: 🎯 final Ubuntu tests and cleanup
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [freebsd]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install compiler and dependencies
|
||||
run: |
|
||||
sudo apt-get install -q -y make openssl wget fuse3 util-linux
|
||||
sudo make -C portable download-deps-ubuntu20
|
||||
- name: Download tomb built on FreeBSD
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: freebsd-test.tomb
|
||||
path: portable
|
||||
- name: Check integrity FreeBSD->Ubuntu
|
||||
run: sudo make -C portable check-random-data
|
||||
- name: delete FreeBSD tomb artifacts
|
||||
uses: geekyeggo/delete-artifact@v2
|
||||
with:
|
||||
name: freebsd-test.tomb
|
||||
- name: delete Ubuntu tomb artifacts
|
||||
uses: geekyeggo/delete-artifact@v2
|
||||
with:
|
||||
name: ubuntu-test.tomb
|
28
AUTHORS.md
28
AUTHORS.md
|
@ -2,27 +2,28 @@ Cryptsetup was originally written in 2004 by Jana Saout
|
|||
|
||||
LUKS extensions are written in 2006 by Clemens Fruhwirth
|
||||
|
||||
Tomb is written and maintained since 2007 by Denis Roio <J@dyne.org>
|
||||
Tomb is written and maintained since 2007 by [Denis "Jaromil" Roio](https://jaromil.dyne.org)
|
||||
|
||||
Tomb includes code and advices by Anathema, Boyska, Hellekin O. Wolf,
|
||||
Daniel Rodriguez, Parazyd, Alexandre Pujol, AitorATuin, Narrat and
|
||||
Artur Malimonov.
|
||||
Daniel Rodriguez, Parazyd, Alexandre Pujol, AitorATuin, Narrat, Artur
|
||||
Malimonov and Chris Vogel.
|
||||
|
||||
The 'gtomb' GUI based on Zenity is written by Parazyd.
|
||||
The 'gtomb' GUI based on Zenity is written by Parazyd and Daniel Dias Rodrigues.
|
||||
|
||||
The Qt5 desktop tray GUI is written by Gianluca Montecchi.
|
||||
|
||||
Python Tomb wrappers are contributed by Reiven and Boyska.
|
||||
|
||||
The Docker Tomb wrapper is contributed by Greg Tczap
|
||||
The Docker Tomb wrapper is contributed by Greg Tczap and Jens Rischbieth.
|
||||
|
||||
Artwork is contributed by Jordi aka Mon Mort and Logan VanCuren.
|
||||
|
||||
Gettext internationalization and Spanish translation is contributed by
|
||||
Daniel Rodriguez. French translation by Hellekin and Roy Lockhart,
|
||||
Russian translation by fsLeg, German translation by Jerry Polfer,
|
||||
Italian translation by Massimiliano Augello and Swedish translation by
|
||||
PLJ / Kosovoper.
|
||||
Daniel Rodriguez and Francisco Serrador. French translation by
|
||||
Hellekin and Roy Lockhart, Russian translation by fsLeg and AHOHNMYC,
|
||||
German translation by Jerry Polfer, Italian translation by
|
||||
Massimiliano Augello and Swedish translation by PLJ / Kosovoper,
|
||||
general fixes contributed by Daniel Dias Rodrigues.
|
||||
|
||||
Testing, reviews and documentation contributed by Dreamer, Vlax,
|
||||
Shining the Translucent, Mancausoft, Asbesto Molesto, Nignux, TheJH,
|
||||
|
@ -31,13 +32,12 @@ Polfer, Jim Turner, Maxime Arthaud, RobertMX, mhogomchungu Mandeep
|
|||
Bhutani, Emil Lundberg, Joel Montes de Oca, Armin Mesbah, Arusekk,
|
||||
Stephan Schindel, Asbjørn Apeland, Victor Calvert, bjonnh, SargoDevel,
|
||||
AitorATuin, Alexis Danizan, Sven Geuer, Greg Tczap, Aaron Janse, Mark
|
||||
Mykkanen, Alexis Danizan, Steve Litt, James R, Matthieu Crapet and...
|
||||
the Linux Action Show!
|
||||
Mykkanen, Alexis Danizan, Steve Litt, James R, Matthieu Crapet, Selene
|
||||
ToyKeeper, Valentin Heidelberg and... the Linux Action Show!
|
||||
|
||||
Tomb includes an implementation of the "Password-Based Key Derivation
|
||||
Function v2" based on GCrypt and written by Anthony Thyssen, with
|
||||
fixes contributed by AitorATuin.
|
||||
|
||||
Tomb developers can be contacted via GitHub issues on
|
||||
https://www.github.com/dyne/Tomb or over IRC https://irc.dyne.org
|
||||
channel **#dyne** (or directly over port 9999 with SSL)
|
||||
Some of the Tomb developers can be contacted via [GitHub discussions](https://github.com/dyne/Tomb/discussions)
|
||||
or over Telegram via the [Dyne.org Chat Channel](https://t.me/dyne_chat).
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
# Tomb ChangeLog
|
||||
|
||||
## 2.10
|
||||
### Sep 2023
|
||||
|
||||
This release adds optional support for Argon2 KDF brute-force
|
||||
protection and introduces support for doas as an alternative to sudo
|
||||
for privilege escalation. It also improves support for BTRFS formatted
|
||||
Tombs, adds zram detection as swap memory, updates documentation and
|
||||
translations and cleans up the script code.
|
||||
|
||||
## 2.9
|
||||
### Jan 2021
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ The key can also be hidden in an image, to be used as key later
|
|||
tomb bury -k secrets.tomb.key nosferatu.jpg (hide the key in a jpeg image)
|
||||
tomb open -k nosferatu.jpg secrets.tomb (use the jpeg image to open the tomb)
|
||||
|
||||
Or backupped to a QRCode that can be printed on paper and hidden in
|
||||
Or backed up to a QRCode that can be printed on paper and hidden in
|
||||
books. QRCodes can be scanned with any mobile application, resulting
|
||||
into a block of text that can be used with `-k` just as a normal key.
|
||||
|
||||
|
|
|
@ -22,16 +22,16 @@ cryptsetup v2.1 a new default has been introduced (luks2) and the
|
|||
Using Tomb version 2.6 (and future releases) the problem opening tombs
|
||||
using recent GNU/Linux distributions is fixed.
|
||||
|
||||
# Whitespaces in KDF passwords
|
||||
# Whitespace in KDF passwords
|
||||
## Issue affecting passwords used with PBKDF2 keys (<2.6)
|
||||
|
||||
Up until and including Tomb's version 2.5 the PBKDF2 wrapper for keys
|
||||
in Tomb has a bug affecting passwords that contain whitespaces. Since
|
||||
in Tomb has a bug affecting passwords that contain whitespace. Since
|
||||
the passwords are trimmed at the first whitespace, this makes them
|
||||
weaker, while fortunately the KDF transformation still applies.
|
||||
|
||||
This issue is fixed in Tomb version 2.6: all users adopting KDF keys
|
||||
that have passwords containing whitespaces should change them,
|
||||
that have passwords containing whitespace should change them,
|
||||
knowing that their "old password" is trimmed until the whitespace.
|
||||
|
||||
Users adopting GPG keys or plain (without KDF wrapper) can ignore
|
||||
|
|
8
Makefile
8
Makefile
|
@ -2,6 +2,12 @@ PROG = tomb
|
|||
PREFIX ?= /usr/local
|
||||
MANDIR ?= ${PREFIX}/share/man
|
||||
|
||||
deps:
|
||||
@[ -r /etc/debian_version ] && { \
|
||||
apt-get install -qy zsh cryptsetup file gnupg pinentry-curses; }
|
||||
@[ -r /etc/fedora-release ] ^^ { \
|
||||
yum install -y zsh cryptsetup file gnupg pinentry-curses; }
|
||||
|
||||
all:
|
||||
@echo
|
||||
@echo "Tomb is a script and does not need compilation, it can be simply executed."
|
||||
|
@ -26,4 +32,4 @@ test:
|
|||
make -C extras/test
|
||||
|
||||
lint:
|
||||
shellcheck -s zsh -e SC1073,SC1027,SC1072,SC1083,SC1009 tomb
|
||||
shellcheck -s bash -e SC1058,SC1073,SC1072,SC1009 tomb
|
||||
|
|
295
README.md
295
README.md
|
@ -1,52 +1,14 @@
|
|||
..... ..
|
||||
.H8888888h. ~-. . uW8"
|
||||
888888888888x `> u. .. . : `t888
|
||||
X~ `?888888hx~ ...ue888b .888: x888 x888. 8888 .
|
||||
' x8.^"*88*" 888R Y888r ~`8888~'888X`?888f` 9888.z88N
|
||||
`-:- X8888x 888R I888> X888 888X '888> 9888 888E
|
||||
488888> 888R I888> X888 888X '888> 9888 888E
|
||||
.. `"88* 888R I888> X888 888X '888> 9888 888E
|
||||
x88888nX" . u8888cJ888 X888 888X '888> 9888 888E
|
||||
!"*8888888n.. : "*888*P" "*88%""*88" '888!` .8888 888"
|
||||
' "*88888888* 'Y" `~ " `"` `%888*%"
|
||||
^"***"` "`
|
||||
# Tomb: The Linux Crypto Undertaker
|
||||
|
||||
*A minimalistic commandline tool to manage encrypted volumes* aka **The Crypto Undertaker**
|
||||
[![Build Status](https://github.com/dyne/tomb/actions/workflows/linux.yml/badge.svg)](https://github.com/dyne/Tomb/actions)
|
||||
<!-- [![Build Status](https://github.com/dyne/tomb/actions/workflows/portable.yml/badge.svg)](https://github.com/dyne/Tomb/actions) -->
|
||||
|
||||
[![software by Dyne.org](https://files.dyne.org/software_by_dyne.png)](http://www.dyne.org)
|
||||
Minimalistic command line tool based on Linux dm-crypt and LUKS, trusted by hackers since 2007.
|
||||
|
||||
More information and updates on website: https://www.dyne.org/software/tomb
|
||||
|
||||
Get the stable .tar.gz signed release for production use!
|
||||
|
||||
Download it from https://files.dyne.org/tomb
|
||||
|
||||
For the instructions on how to get started using Tomb, see [INSTALL](INSTALL.md).
|
||||
You can keep your volumes secure and easily manageable with simple commands.
|
||||
|
||||
![tomb's logo](https://github.com/dyne/Tomb/blob/master/extras/images/monmort.png)
|
||||
|
||||
[![Build Status](https://travis-ci.org/dyne/Tomb.svg?branch=master)](https://travis-ci.org/dyne/Tomb)
|
||||
|
||||
# What is Tomb, the crypto undertaker?
|
||||
|
||||
Tomb aims to be a free and open source system for easy encryption and
|
||||
backup of personal files, written in code that is easy to review and
|
||||
links well reliable GNU/Linux components.
|
||||
|
||||
Tomb's ambition is to improve safety by way of:
|
||||
|
||||
- a minimalist design consisting in small and well readable code
|
||||
- facilitation of good practices, i.e: key/storage physical separation
|
||||
- adoption of a few standard and well tested implementations.
|
||||
|
||||
At present, Tomb consists of a simple shell script (Zsh) using
|
||||
standard filesystem tools (GNU) and the cryptographic API of the Linux
|
||||
kernel (cryptsetup and LUKS). Tomb can also produce machine parsable
|
||||
output to facilitate its use inside graphical applications.
|
||||
|
||||
# How does it work?
|
||||
|
||||
To create a Tomb, do:
|
||||
```
|
||||
$ tomb dig -s 100 secret.tomb
|
||||
$ tomb forge secret.tomb.key
|
||||
|
@ -56,254 +18,41 @@ To open it, do
|
|||
```
|
||||
$ tomb open secret.tomb -k secret.tomb.key
|
||||
```
|
||||
and after you are done
|
||||
And after you are done
|
||||
```
|
||||
$ tomb close
|
||||
```
|
||||
or if you are in a hurry
|
||||
Or, if you are in a hurry, kill all processes with open files inside your tomb and close it.
|
||||
```
|
||||
$ tomb slam all
|
||||
$ tomb slam
|
||||
```
|
||||
## [Get started on dyne.org/software/tomb](https://dyne.org/software/tomb)
|
||||
|
||||
```
|
||||
Syntax: tomb [options] command [arguments]
|
||||
<a href="https://dyne.org/software/tomb"><img src="https://files.dyne.org/software_by_dyne.png" width="30%"></a>
|
||||
|
||||
Commands:
|
||||
All information is found on our website.
|
||||
|
||||
// Creation:
|
||||
dig create a new empty TOMB file of size -s in MiB
|
||||
forge create a new KEY file and set its password
|
||||
lock installs a lock on a TOMB to use it with KEY
|
||||
Use only stable and signed releases in production!
|
||||
|
||||
// Operations on tombs:
|
||||
open open an existing TOMB (-k KEY file or - for stdin)
|
||||
index update the search indexes of tombs
|
||||
search looks for filenames matching text patterns
|
||||
list list of open TOMBs and information on them
|
||||
ps list of running processes inside open TOMBs
|
||||
close close a specific TOMB (or 'all')
|
||||
slam slam a TOMB killing all programs using it
|
||||
resize resize a TOMB to a new size -s (can only grow)
|
||||
### 💾 [Download from files.dyne.org/tomb](https://files.dyne.org/tomb/)
|
||||
|
||||
// Operations on keys:
|
||||
passwd change the password of a KEY (needs old pass)
|
||||
setkey change the KEY locking a TOMB (needs old key and pass)
|
||||
Tomb's development is community-based!
|
||||
|
||||
// Backup on paper:
|
||||
engrave makes a QR code of a KEY to be saved on paper
|
||||
## How can you help
|
||||
|
||||
// Steganography:
|
||||
bury hide a KEY inside a JPEG image (for use with -k)
|
||||
exhume extract a KEY from a JPEG image (prints to stdout)
|
||||
cloak transform a KEY into a TEXT using CIPHER (for use with -k)
|
||||
uncloak extract a KEY from a TEXT file using CIPHER (prints to stdout)
|
||||
Donations are very welcome on [dyne.org/donate](https://www.dyne.org/donate)
|
||||
|
||||
Options:
|
||||
Translations are also welcome: see our simple [translation guide](https://github.com/dyne/Tomb/blob/master/extras/translations/README.md)
|
||||
|
||||
-s size of the tomb file when creating/resizing one (in MiB)
|
||||
-k path to the key to be used ('-k -' to read from stdin)
|
||||
-n don't process the hooks found in tomb
|
||||
-o options passed to commands: open, lock, forge (see man)
|
||||
-f force operation (i.e. open even if swap is active)
|
||||
-g use a GnuPG key to encrypt a tomb key
|
||||
-r provide GnuPG recipients (separated by comma)
|
||||
-R provide GnuPG hidden recipients (separated by comma)
|
||||
--kdf forge keys armored against dictionary attacks
|
||||
Tomb's code is short and readable: don't be afraid to inspect it! If you plan to submit a PR, please remember that this is a minimalist tool, and the code should be short and readable. Also, first, read our small intro to [Tomb's coding style](doc/HACKING.txt).
|
||||
|
||||
-h print this help
|
||||
-v print version, license and list of available ciphers
|
||||
-q run quietly without printing informations
|
||||
-D print debugging information at runtime
|
||||
```
|
||||
We have a [space for issues](https://github.com/dyne/Tomb/issues) open for detailed bug reports. Always include the Tomb version being used when filing a case, please.
|
||||
|
||||
# What is this for, exactly?
|
||||
|
||||
This tool can be used to dig .tomb files (LUKS volumes), forge keys
|
||||
protected by a password (GnuPG encryption) and use the keys to lock
|
||||
the tombs. Tombs are like single files whose contents are inaccessible
|
||||
in the absence of the key they were locked with and its password.
|
||||
|
||||
Once open, the tombs are just like normal folders and can contain
|
||||
different files, plus they offer advanced functionalities like bind
|
||||
and execution hooks and fast search, or they can be slammed close even
|
||||
if busy. Keys can be stored on separate media like USB sticks, NFC,
|
||||
on-line SSH servers or bluetooth devices to make the transport of data
|
||||
safer: one always needs both the tomb and the key, plus its password,
|
||||
to access it.
|
||||
|
||||
The tomb script takes care of several details to improve user's
|
||||
behaviour and the security of tombs in everyday usage: protects the
|
||||
typing of passwords from keyloggers, facilitates hiding keys inside
|
||||
images, indexes and search a tomb's contents, mounts directories in
|
||||
place, lists open tombs and selectively closes them, warns the user
|
||||
about free space and last time usage, etc.
|
||||
|
||||
# How secure is this?
|
||||
|
||||
Death is the only sure thing in life. That said, Tomb is a pretty
|
||||
secure tool especially because it is kept minimal, its source is
|
||||
always open to review (even when installed) and its code is easy to
|
||||
read with a bit of shell script knowledge.
|
||||
|
||||
All encryption tools being used in Tomb are included as default in
|
||||
many GNU/Linux operating systems and therefore are regularly peer
|
||||
reviewed: we don't add anything else to them really, just a layer of
|
||||
usability.
|
||||
|
||||
The file [KNOWN_BUGS.md](KNOWN_BUGS.md) contains some notes on known
|
||||
vulnerabilities and threat model analysis.
|
||||
|
||||
In absence or malfunction of the Tomb script it is always possible to
|
||||
access the contents of a Tomb only using a dm-crypt enabled Linux
|
||||
kernel, cryptsetup, GnuPG and any shell interpreter issuing the
|
||||
following commands as root:
|
||||
```
|
||||
lo=$(losetup -f)
|
||||
losetup -f secret.tomb
|
||||
pass="$(gpg -d secret.key)"
|
||||
echo -n -e "$pass" | cryptsetup --key-file - luksOpen $lo secret
|
||||
mount /dev/mapper/secret /mnt
|
||||
unset pass
|
||||
```
|
||||
One can change the last argument `/mnt` to where the Tomb has to be
|
||||
mounted and made accessible. To close the tomb then use:
|
||||
```
|
||||
umount /mnt
|
||||
cryptsetup luksClose /dev/mapper/secret
|
||||
```
|
||||
|
||||
# Stage of development
|
||||
|
||||
Tomb is an evolution of the 'mknest' tool developed for the
|
||||
[dyne:bolic](http://www.dynebolic.org) 100% Free GNU/Linux
|
||||
distribution in 2001: its 'nesting' mechanism allowed the liveCD users
|
||||
to encrypt and make persistent home directories. Since then the same
|
||||
shell routines kept being maintained and used for dyne:bolic until
|
||||
2007, when they were ported to work on more GNU/Linux distributions.
|
||||
|
||||
As of today, Tomb is a very stable tool also used in mission critical
|
||||
situations by a number of activists in dangerous zones. It has been
|
||||
reviewed by forensics analysts and it can be considered safe for
|
||||
adoption where the integrity of information stored depends on the
|
||||
user's behaviour and the strength of a standard AES-256 (XTS plain)
|
||||
encryption algorithm (current default) or, at one's option, other
|
||||
equivalent standards supported by the Linux kernel.
|
||||
|
||||
## Compatibility
|
||||
|
||||
Tomb can be used in conjunction with some other software applications,
|
||||
some are developed by Dyne.org, but some also by third parties.
|
||||
|
||||
### Included extra applications
|
||||
|
||||
These auxiliary applications are found in the extras/ subdirectory of
|
||||
distributed Tomb's sourcecode:
|
||||
|
||||
- [GTomb](extras/gtomb) is a graphical interface using zenity
|
||||
- [gtk-tray](extras/gtk-tray) is a graphical tray icon for GTK panels
|
||||
- [qt-tray](extras/qt-tray) is a graphical tray icon for QT panels
|
||||
- [tomber](extras/tomber) is a wrapper to use Tomb in Python scripts
|
||||
- [docker](extras/docker) is a wrapper to use Tomb through Docker
|
||||
|
||||
![skulls and pythons](https://github.com/dyne/Tomb/blob/master/extras/images/python_for_tomb.png)
|
||||
|
||||
### External applications
|
||||
|
||||
The following applications are not included in Tomb's distributed
|
||||
sourcecode, but are known and tested to be compatible with Tomb:
|
||||
|
||||
- [Secrets](https://secrets.dyne.org) is a software that can be operated on-line and on-site to split a Tomb key in shares to be distributed to peers: some of them have to agree to combine back the shares in order to retrieve the key.
|
||||
|
||||
- [zuluCrypt](https://mhogomchungu.github.io/zuluCrypt/) is a graphical application to manage various types of encrypted volumes on GNU/Linux, among them also Tombs, written in C++.
|
||||
|
||||
- [Mausoleum](https://github.com/mandeep/Mausoleum) is a graphical interface to facilitate the creation and management of tombs, written in Python.
|
||||
|
||||
|
||||
- [pass-tomb](https://github.com/roddhjav/pass-tomb) is a console based wrapper of the excellent password keeping program [pass](https://www.passwordstore.org) that helps to keep the whole tree of password encrypted inside a tomb. It is written in Bash.
|
||||
|
||||
If you are writing a project supporting Tomb volumes or wrapping Tomb, let us know!
|
||||
|
||||
|
||||
## Compliancy
|
||||
|
||||
Tomb qualifies as sound for use on information rated as "top secret"
|
||||
when used on an underlying stack of carefully reviewed hardware
|
||||
(random number generator and other components) and software (Linux
|
||||
kernel build, crypto modules, device manager, compiler used to built,
|
||||
shell interpreter and packaged dependencies).
|
||||
|
||||
Tomb volumes are fully compliant with the FIPS 197 advanced encryption
|
||||
standard published by NIST and with the following industry standards:
|
||||
|
||||
- Information technology -- Security techniques -- Encryption algorithms
|
||||
- [ISO/IEC 18033-1:2015](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=54530) -- Part 1: General
|
||||
- [ISO/IEC 18033-3:2010](http://www.iso.org/iso/home/store/catalogue_ics/catalogue_detail_ics.htm?csnumber=54531) -- Part 3: Block ciphers
|
||||
|
||||
Tomb implementation is known to address at least partially issues raised in:
|
||||
|
||||
- Information technology -- Security techniques -- Key management
|
||||
- [ISO/IEC 11770-1:2010](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=53456) -- Part 1: Framework
|
||||
- [ISO/IEC 11770-2:2008](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=46370) -- Part 2: Mechanisms using symmetric techniques
|
||||
- [ISO/IEC 27005:2011](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=56742) Information technology -- Security techniques -- Information security risk management
|
||||
- [ISO/IEC 24759:2014](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=59142) Information technology -- Security techniques -- Test requirements for cryptographic modules
|
||||
|
||||
Any help on further verification of compliancy is very welcome, as the
|
||||
access to ISO/IEC document is limited due to its expensive nature.
|
||||
|
||||
|
||||
# Use stable releases in production!
|
||||
|
||||
Anyone planning to use Tomb to store and access secrets should not use
|
||||
the latest development version in Git, but use instead the .tar.gz
|
||||
release on https://files.dyne.org/tomb . The stable version will
|
||||
always ensure backward compatibility with older tombs: we make sure it
|
||||
creates sane tombs and keys by running various tests before releasing
|
||||
it. The development version in Git might introduce sudden bugs and is
|
||||
not guaranteed to produce backward- or forward-compatible tombs and keys.
|
||||
The development version in Git should be used to report bugs, test new
|
||||
features and develop patches.
|
||||
|
||||
So be warned: do not use the latest Git version in production
|
||||
environments, but use a stable release versioned and packed as
|
||||
tarball on https://files.dyne.org/tomb
|
||||
|
||||
![Day of the dead](https://github.com/dyne/Tomb/blob/master/extras/images/DayOfTheDead.jpg)
|
||||
|
||||
# How can you help
|
||||
|
||||
Donations are very welcome, please go to https://www.dyne.org/donate
|
||||
|
||||
Translations are also welcome: they can be contributed editing sending
|
||||
the .po files in [extras/translations](extras/translations).
|
||||
|
||||
The code is pretty short and readable. There is also a collection of
|
||||
specifications and design materials in the [doc](doc) directory.
|
||||
|
||||
To contribute code and reviews visit https://github.com/dyne/Tomb
|
||||
|
||||
If you plan to commit code into Tomb, please keep in mind this is a
|
||||
minimalist tool and its code should be readable. Guidelines on the
|
||||
coding style are illustrated in [doc/HACKING.txt](doc/HACKING.txt).
|
||||
|
||||
Tomb's developers can be contacted using the issues on GitHub or over
|
||||
IRC on https://irc.dyne.org channel **#dyne** (or direct port 9999 SSL)
|
||||
There is also a [space for discussion](https://github.com/dyne/Tomb/discussions) of new features, desiderata and whatnot on github.
|
||||
|
||||
# Licensing
|
||||
|
||||
Tomb is Copyright (C) 2007-2021 by the Dyne.org Foundation and
|
||||
maintained by Denis Roio <J@dyne.org>. More information on all
|
||||
the developers involved is found in the [AUTHORS](AUTHORS.md) file.
|
||||
Tomb is Copyright (C) 2007-2024 by the Dyne.org Foundation and maintained by [Jaromil](https://github.com/jaromil). The [AUTHORS](AUTHORS.md) file contains more information on all the developers involved. The license is GNU Public License v3.
|
||||
|
||||
This source code is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
## [More info on dyne.org/software/tomb](https://dyne.org/software/tomb)
|
||||
|
||||
This source code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
|
||||
to the GNU Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Public License along with
|
||||
this source code; if not, write to: Free Software Foundation, Inc.,
|
||||
675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
Overview
|
||||
=========
|
||||
|
||||
|
||||
What's a key?
|
||||
It basicly is a gpg simmetrically encrypted, ascii-armored file.
|
||||
It's encryption key is a function (see below, on KDF section) of your tomb
|
||||
What is a key?
|
||||
It is a gpg symmetrically encrypted, ascii-armored file.
|
||||
The encryption key is a function (see below, on KDF section) of your tomb
|
||||
passphrase.
|
||||
|
||||
|
||||
Layout
|
||||
======
|
||||
|
||||
|
|
|
@ -31,11 +31,11 @@ Linux hard disk encryption settings
|
|||
and LRW for standardisation. EME along with it's cousin CMC seems to
|
||||
provide the best security level, but imposes additional encryption
|
||||
steps. Plumb-IV is discussed only for reference, because it has the
|
||||
same performance penalty as CMC, but in constrast suffers from
|
||||
same performance penalty as CMC, but in contrast suffers from
|
||||
weaknesses of CBC encryption.
|
||||
|
||||
As convention, this document will use the term "blocks", when it
|
||||
referes to a single block of plain or cipher text (usually 16 byte),
|
||||
refers to a single block of plain or cipher text (usually 16 byte),
|
||||
and will use the term "sectors", when it refers to a 512-byte wide hard
|
||||
disk block.
|
||||
|
||||
|
@ -171,8 +171,8 @@ Content leaks
|
|||
cipher blocks. But how does this number grow in n? Obviously
|
||||
exponentially. Plotting a few a decimal powers shows that the chance
|
||||
for finding at least on identical cipher pair flips to 1 around n =
|
||||
10^20 (n = 10^40 for a 256-bit cipher). This inflexion point is reached
|
||||
for a 146 million TB storage (or a hundered thousand trillion trillions
|
||||
10^20 (n = 10^40 for a 256-bit cipher). This inflection point is reached
|
||||
for a 146 million TB storage (or a hundred thousand trillion trillions
|
||||
TB storage for a 256-bit cipher).
|
||||
|
||||
^1The blocks with available preceding cipher blocks is 62/1KB for all
|
||||
|
|
140
doc/tomb.1
140
doc/tomb.1
|
@ -1,4 +1,4 @@
|
|||
.TH tomb 1 "Jan 4, 2021" "tomb"
|
||||
.TH tomb 1 "Jun 25, 2023" "tomb"
|
||||
|
||||
.SH NAME
|
||||
Tomb \- the Crypto Undertaker
|
||||
|
@ -32,23 +32,24 @@ Generates a file that can be used as a tomb and will occupy as much
|
|||
space as its desired initial size, the unlocked \fI.tomb\fR file can
|
||||
then be locked using a \fIkey\fR. It takes a mandatory \fI-s\fR option
|
||||
which is the size in megabytes (MiB). Tombs are digged using random
|
||||
data gathered from a non-blocking source (/dev/urandom).
|
||||
data gathered from a non-blocking source (/dev/urandom). For very
|
||||
large tombs this may take up too much time and entropy, then it is
|
||||
possible to use \fIfallocate(1)\fR being aware it does not pre-fill
|
||||
with random data, decreasing the tomb's security.
|
||||
|
||||
.B
|
||||
.IP "forge"
|
||||
Creates a new \fIkey\fR and prompts the user for a \fIpassword\fR to
|
||||
protect its usage using symmetric encryption. This operation uses
|
||||
random data from a non-blocking source (/dev/urandom) and it may take
|
||||
long only in some cases; to switch using a blocking source the
|
||||
\fI--use-random\fR flag can be used. The \fI-g\fR option switches on
|
||||
the use of a GPG key instead of a password (asymmetric encryption),
|
||||
then the \fI-r\fR option indicates the recipient key; more recipient
|
||||
GPG ids can be indicated (comma separated). The default cipher to
|
||||
protect the key is AES256, a custom one can be specified using the
|
||||
\fI-o\fR option, for a list of supported ciphers use \fI-v\fR. For
|
||||
additional protection against dictionary attacks on keys, the
|
||||
\fI--kdf\fR option can be used when forging a key, making sure that
|
||||
the \fItomb-kdb-pbkdf2\fR binaries in \fIextras/kdf\fR were compiled
|
||||
Creates a new \fIkey\fR and prompts the user for a \fIpassword\fR to protect
|
||||
its usage using symmetric encryption. This operation uses random data from a
|
||||
non-blocking source (/dev/urandom) and it may take long only in some cases; to
|
||||
switch using a blocking source the \fI--use-random\fR flag can be used. The
|
||||
\fI-g\fR option switches on the use of a GPG key instead of a password
|
||||
(asymmetric encryption), then the \fI-r\fR option indicates the recipient key;
|
||||
more recipient GPG ids can be indicated (comma separated). The default cipher
|
||||
to protect the key is AES256, a custom one can be specified using the \fI-o\fR
|
||||
option, for a list of supported ciphers use \fI-v\fR. For additional protection
|
||||
against dictionary attacks on keys, the \fI--kdf\fR option can be used when
|
||||
forging a key, making sure that the binaries in \fIextras/kdf\fR were compiled
|
||||
and installed on the system.
|
||||
|
||||
.B
|
||||
|
@ -65,11 +66,29 @@ option can be used to specify the cipher specification: default is
|
|||
If you are looking for something exotic, also try
|
||||
"serpent-xts-plain64". More options may be found in cryptsetup(8) and
|
||||
Linux documentation. The \fI--filesystem\fR option can be used to
|
||||
specify "btrfs" as an alternative filesystem used to format the tomb,
|
||||
specify an alternative filesystem used to format the tomb,
|
||||
in place of the default "ext4". This operation requires root
|
||||
privileges to loopback mount, format the tomb (using LUKS and Ext4),
|
||||
privileges to loopback mount, format the tomb (using LUKS and mkfs),
|
||||
then set the key in its first LUKS slot.
|
||||
|
||||
.RS
|
||||
Supported filesystems for \fI--filesystem\fR:
|
||||
.PD 0
|
||||
.IP "ext3" 15
|
||||
using operating system defaults
|
||||
.IP "ext4"
|
||||
using operating system defaults
|
||||
.IP "btrfs"
|
||||
for tombs >= 47MB using operating system defaults
|
||||
.IP "btrfsmixedmode"
|
||||
for tombs >=18MB btrfs mixed mode (see mkfs.btrfs(8))
|
||||
.IP "ext3maxinodes"
|
||||
ext3 with a maximum of inodes (for many small files)
|
||||
.IP "ext4maxinodes"
|
||||
ext4 with a maximum of inodes (for many small files)
|
||||
.PD
|
||||
.RE
|
||||
|
||||
.B
|
||||
.IP "open"
|
||||
Opens an existing \fItomb file\fR (first argument) using a key
|
||||
|
@ -103,7 +122,7 @@ internally to enumerate processes running in one or all tombs.
|
|||
.IP "index"
|
||||
Creates or updates the search indexes of all tombs currently open:
|
||||
enables use of the \fIsearch\fR command using simple word patterns on
|
||||
file names. Indexes are created using mlocate's updatedb(8) and
|
||||
file names. Indexes are created using mlocate/plocate's updatedb(8) and
|
||||
swish-e(1) if they are found on the system. Indexes allow one to search
|
||||
very fast for filenames and contents inside a tomb, they are stored
|
||||
inside it and are not accessible if the Tomb is closed. To avoid
|
||||
|
@ -113,7 +132,7 @@ indexing a specific tomb simply touch a \fI.noindex\fR file in it.
|
|||
.IP "search"
|
||||
Takes any string as argument and searches for them through all tombs
|
||||
currently open and previously indexed using the \fIindex\fR command.
|
||||
The search matches filenames if mlocate is installed and then also
|
||||
The search matches filenames if mlocate/plocate is installed and then also
|
||||
file contents if swish++ is present on the system, results are listed
|
||||
on the console.
|
||||
|
||||
|
@ -257,7 +276,8 @@ the \fIsize\fR of the new file to be created. Units are megabytes (MiB).
|
|||
.B
|
||||
.IP "-g"
|
||||
Tell tomb to use a asymmetric GnuPG key encryption instead of a
|
||||
symmetric passphrase to protect a tomb key. This option can be followed by \fI-r\fR when the command needs to specify recipient(s).
|
||||
symmetric passphrase to protect a tomb key. This option can be
|
||||
followed by \fI-r\fR when the command needs to specify recipient(s).
|
||||
.B
|
||||
.IP "-r \fI<gpg_id>[,<gpg_id2>]\fR"
|
||||
Provide a new set of recipient(s) to encrypt a tomb key. \fIgpg_ids\fR
|
||||
|
@ -265,17 +285,34 @@ can be one or more GPG key ID, comma separated. All GPG keys must be
|
|||
trusted keys in GPG.
|
||||
.B
|
||||
.IP "--kdf \fI<itertime>\fR"
|
||||
Activate the KDF feature against dictionary attacks when creating a
|
||||
key: forces a delay of \fI<itertime>\fR times every time this key is
|
||||
used. The actual time to wait depends on the CPU speed of the
|
||||
computer where the key is used. Using 5 or 10 is a sane amount for
|
||||
modern computers, the value is multiplied by 1 million.
|
||||
Activate the KDF feature against dictionary attacks when creating a key: forces
|
||||
a delay of \fI<itertime>\fR times every time this key is used. The actual time
|
||||
to wait depends on the CPU speed (default) or the RAM size (argon2) of the
|
||||
computer where the key is used. Using 5 or 10 is a sane amount for modern
|
||||
computers, the value is multiplied by 1 million.
|
||||
.B
|
||||
.IP "--kdftype \fIargon2 | pbkdf2\fR"
|
||||
Adopt the \fIargon2\fR algorithm for KDF, stressing the RAM capacity rather
|
||||
than the CPU speed of the computer decrypting the tomb. Requires the
|
||||
\fIargon2\fR binary by P-H-C to be installed, as packaged by most distros.
|
||||
Default is \fIpbkdf2\fR.
|
||||
.B
|
||||
.IP "--kdfmem \fI<memory>\fR"
|
||||
In case of \fIargon2\fR KDF algorithm, this value specifies the size of RAM
|
||||
used: it consists of a number which is the elevated power of two in kilobytes.
|
||||
Default is 18 which is 250 MiB (2^18 = 262,144 kilobytes).
|
||||
.B
|
||||
.IP "--sudo \fI<executable>\fR"
|
||||
Select a different tool than sudo for privilege escalation.
|
||||
Alternatives supported so far are: pkexec, doas, sup, sud. For any
|
||||
alternative to work the executable must be included in the current
|
||||
PATH.
|
||||
.B
|
||||
.IP "--sphx-user \fI<username>\fR"
|
||||
Activate the SPHINX feature for password-authenticated key agreement.
|
||||
This option indicates the \fI<username>\fR used to retrieve the
|
||||
password from a sphinx oracle key reachable via TCP/IP.
|
||||
|
||||
.B
|
||||
.IP "--sphx-host \fI<domain>\fR"
|
||||
Activate the SPHINX feature for password-authenticated key agreement.
|
||||
This option indicates the \fI<domain>\fR used to retrieve the password
|
||||
|
@ -338,7 +375,7 @@ base root of the tomb.
|
|||
.IP "bind-hooks"
|
||||
This hook file consists of a simple text file named \fIbind-hooks\fR
|
||||
containing a two column list of paths to files or directories inside
|
||||
the tomb. The files and directories will be be made directly
|
||||
the tomb. The files and directories will be made directly
|
||||
accessible by the tomb \fIopen\fR command inside the current user's
|
||||
home directory. Tomb uses internally the "mount \-o bind" command to
|
||||
bind locations inside the tomb to locations found in $HOME. In the
|
||||
|
@ -346,10 +383,10 @@ first column are indicated paths relative to the tomb and in the
|
|||
second column are indicated paths relative to $HOME contents, for
|
||||
example:
|
||||
.EX
|
||||
mail mail
|
||||
.gnupg .gnupg
|
||||
.fmrc .fetchmailrc
|
||||
.mozilla .mozilla
|
||||
mail mail
|
||||
.gnupg .gnupg
|
||||
.fmrc .fetchmailrc
|
||||
.mozilla .mozilla
|
||||
.EE
|
||||
|
||||
.B
|
||||
|
@ -365,9 +402,9 @@ command.
|
|||
.SH PRIVILEGE ESCALATION
|
||||
|
||||
The tomb commandline tool needs to acquire super user rights to
|
||||
execute most of its operations: to do so it uses sudo(8), while
|
||||
pinentry(1) is adopted to collect passwords from the user. Tomb
|
||||
executes as super user only when required.
|
||||
execute most of its operations: so it uses sudo(8) or other configured
|
||||
tools, while pinentry(1) is adopted to collect passwords from the
|
||||
user. Tomb executes as super user only when required.
|
||||
|
||||
To be made available on multi user systems, the superuser execution of
|
||||
the tomb script can be authorized for users without jeopardizing the
|
||||
|
@ -380,8 +417,8 @@ whole system's security: just add such a line to \fI/etc/sudoers\fR:
|
|||
To avoid that tomb execution is logged by \fIsyslog\fR also add:
|
||||
|
||||
.EX
|
||||
Cmnd_Alias TOMB = /usr/local/bin/tomb
|
||||
Defaults!TOMB !syslog
|
||||
Cmnd_Alias TOMB = /usr/local/bin/tomb
|
||||
Defaults!TOMB !syslog
|
||||
.EE
|
||||
|
||||
.SH PASSWORD INPUT
|
||||
|
@ -432,16 +469,21 @@ prefix all invocations of tomb with a blank space, including two lines
|
|||
in ".zshrc":
|
||||
|
||||
.EX
|
||||
export HISTIGNORESPACE=1
|
||||
alias tomb=' tomb'
|
||||
export HISTIGNORESPACE=1
|
||||
alias tomb=' tomb'
|
||||
.EE
|
||||
|
||||
.SH PASSWORD INPUT
|
||||
|
||||
Tomb uses the external program "pinentry" to let users type the key password into a terminal or a graphical window. This program works in conjunction with "gpg-agent", a daemon running in background to facilitate secret key management with gpg. It is recommended one runs "gpg-agent" launching it from the X session initialization ("~/.xsession" or "~/.xinitrc" files) with this command:
|
||||
Tomb uses the external program "pinentry" to let users type the key password
|
||||
into a terminal or a graphical window. This program works in conjunction with
|
||||
"gpg-agent", a daemon running in background to facilitate secret key
|
||||
management with gpg. It is recommended one runs "gpg-agent" launching it from
|
||||
the X session initialization ("~/.xsession" or "~/.xinitrc" files) with this
|
||||
command:
|
||||
|
||||
.EX
|
||||
eval $(gpg-agent --daemon --write-env-file "${HOME}/.gpg-agent-info")
|
||||
eval $(gpg-agent --daemon --write-env-file "${HOME}/.gpg-agent-info")
|
||||
.EE
|
||||
|
||||
In the future it may become mandatory to run gpg-agent when using tomb.
|
||||
|
@ -463,7 +505,7 @@ commands: \fIopen\fR, \fIforge\fR \fIsetkey\fR, \fIpasswd\fR,
|
|||
Using the package libsphinx
|
||||
.UR https://github.com/stef/libsphinx
|
||||
.UE
|
||||
and its python client/daemon implementation pwdsphinx
|
||||
and its python client/daemon implementation pwdsphinx
|
||||
.UR https://github.com/stef/pwdsphinx
|
||||
.UE
|
||||
is possible to store and retrieve safely the password that locks the
|
||||
|
@ -528,11 +570,11 @@ keeping all its profile data inside it:
|
|||
cat <<EOF > /media/FOX.tomb/exec-hooks
|
||||
#!/bin/sh
|
||||
if [ "$1" = "open" ]; then
|
||||
firefox -no-remote -profile "$2"/firefox-pro &
|
||||
firefox -no-remote -profile "$2"/firefox-pro &
|
||||
fi
|
||||
EOF
|
||||
chmod +x /media/FOX.tomb/exec-hooks
|
||||
mkdir /media/FOX.tomb/firefox-pro
|
||||
chmod +x /media/FOX.tomb/exec-hooks
|
||||
mkdir /media/FOX.tomb/firefox-pro
|
||||
.EE
|
||||
|
||||
.IP \(bu
|
||||
|
@ -543,13 +585,13 @@ Script a tomb to archive Pictures using Shotwell, launching it on open:
|
|||
cat <<EOF > /media/Pictures.tomb/bind-hooks
|
||||
Pictures Pictures
|
||||
EOF
|
||||
cat <<EOF > /media/Pictures.tomb/exec-hooks
|
||||
cat <<EOF > /media/Pictures.tomb/exec-hooks
|
||||
#!/bin/sh
|
||||
if [ "$1" = "open" ]; then
|
||||
which shotwell > /dev/null
|
||||
if [ "$?" = "0" ]; then
|
||||
shotwell -d "$2"/Pictures/.shotwell &
|
||||
fi
|
||||
which shotwell > /dev/null
|
||||
if [ "$?" = "0" ]; then
|
||||
shotwell -d "$2"/Pictures/.shotwell &
|
||||
fi
|
||||
fi
|
||||
EOF
|
||||
chmod +x /media/Pictures.tomb/exec-hooks
|
||||
|
|
Binary file not shown.
|
@ -1647,7 +1647,7 @@ change_tomb_key() {
|
|||
|
||||
_sudo cryptsetup luksClose "${mapper}" || _failure "Unexpected error in luksClose."
|
||||
|
||||
_success "Succesfully changed key for tomb: ::1 tomb file::" $TOMBFILE
|
||||
_success "Successfully changed key for tomb: ::1 tomb file::" $TOMBFILE
|
||||
_message "The new key is: ::1 new key::" $TOMBKEYFILE
|
||||
|
||||
return 0
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
##
|
||||
# gregtzar/tomb
|
||||
#
|
||||
# This creates an Ubuntu derived base image and installs the tomb libarary
|
||||
# This creates an Ubuntu derived base image and installs the tomb library
|
||||
# along with it's dependencies.
|
||||
|
||||
FROM ubuntu:bionic
|
||||
FROM dyne/devuan:chimaera
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG TOMB_VERSION=2.5
|
||||
ARG TOMB_VERSION=2.9
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update -y && \
|
||||
apt-get install -y \
|
||||
apt-get install -y -q --no-install-recommends\
|
||||
make \
|
||||
sudo \
|
||||
curl \
|
||||
rsync \
|
||||
build-essential \
|
||||
gettext \
|
||||
zsh \
|
||||
gnupg \
|
||||
cryptsetup \
|
||||
pinentry-curses \
|
||||
steghide
|
||||
file xxd \
|
||||
steghide \
|
||||
mlocate \
|
||||
swish-e
|
||||
|
||||
# Build and install Tomb from remote repo
|
||||
RUN curl https://files.dyne.org/tomb/Tomb-$TOMB_VERSION.tar.gz -o /tmp/Tomb-$TOMB_VERSION.tar.gz && \
|
||||
RUN curl https://files.dyne.org/tomb/releases/Tomb-$TOMB_VERSION.tar.gz -o /tmp/Tomb-$TOMB_VERSION.tar.gz && \
|
||||
cd /tmp && \
|
||||
tar -zxvf /tmp/Tomb-$TOMB_VERSION.tar.gz && \
|
||||
cd /tmp/Tomb-$TOMB_VERSION && \
|
||||
make install
|
||||
make install
|
||||
|
|
|
@ -18,15 +18,18 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this source code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
ver="0.8"
|
||||
TOMBPATH="/usr/local/bin/tomb" # Set this to your tomb executable's path
|
||||
KDFPATH="/usr/local/bin/" # Set this to the path of your KDF binaries (if you're using them)
|
||||
# {{{ SETTINGS
|
||||
ver="0.9.1"
|
||||
KDFPATH="/usr/libexec/tomb" # Path of your KDF binaries (if you're using them).
|
||||
SWAPOFF="false" # Set to "true" to swapoff, or "false" to use -f (force) flag.
|
||||
# The ones below should not need changing
|
||||
TOMBPATH="$(which tomb)" # Tomb executable's path
|
||||
HEXENC="$KDFPATH/tomb-kdb-hexencode"
|
||||
GENSALT="$KDFPATH/tomb-kdb-pbkdf2-gensalt"
|
||||
GETITER="$KDFPATH/tomb-kdb-pbkdf2-getiter"
|
||||
PBKDF="$KDFPATH/tomb-kdb-pbkdf2"
|
||||
_DD=/bin/dd
|
||||
_DD="$(which dd)"
|
||||
# }}}
|
||||
|
||||
# {{{ monmort icon
|
||||
MONMORT="/tmp/monmort.png"
|
||||
|
@ -41,337 +44,165 @@ b2RpZnkAMjAxMS0wMS0xMlQwOTozNDoyNCswMTowMNKiZVMAAAAASUVORK5CYII="
|
|||
echo -e "$ICONB64" | base64 --decode > $MONMORT
|
||||
# }}}
|
||||
|
||||
# {{{ sudo functions
|
||||
function _sudo {
|
||||
sudoassword=$(ask_password "Insert sudo password for user $USER")
|
||||
echo -e "$sudoassword\n" | sudo -S -v
|
||||
_sudowrong
|
||||
}
|
||||
|
||||
function _sudowrong {
|
||||
[[ $? = 0 ]] || {
|
||||
sudoassword=$(ask_password "Wrong password. Insert sudo password for user $USER")
|
||||
echo -e "$sudoassword\n" | sudo -S -v
|
||||
_sudowrong
|
||||
}
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ Zenity dialogs
|
||||
Icon="--window-icon="$MONMORT""
|
||||
function _zenques {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--question \
|
||||
--text="$1"
|
||||
zenity \
|
||||
$Icon \
|
||||
--question \
|
||||
--text="$1"
|
||||
}
|
||||
function _fsel {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--file-selection \
|
||||
--title="$1"
|
||||
zenity \
|
||||
$Icon \
|
||||
--file-selection \
|
||||
--title="$1"
|
||||
}
|
||||
function _fsave {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--file-selection \
|
||||
--save \
|
||||
--title="$1" \
|
||||
--filename="$2"
|
||||
zenity \
|
||||
$Icon \
|
||||
--file-selection \
|
||||
--save \
|
||||
--title="$1" \
|
||||
--filename="$2"
|
||||
}
|
||||
function _zenwarn {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--warning \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
zenity \
|
||||
$Icon \
|
||||
--warning \
|
||||
--title="Warning" \
|
||||
--text="$1"
|
||||
}
|
||||
function _info {
|
||||
which notify-send > /dev/null
|
||||
if [[ $? == "0" ]]; then
|
||||
_zenotif $*
|
||||
else
|
||||
_zeninfo $*
|
||||
fi
|
||||
which notify-send > /dev/null
|
||||
if [[ $? == "0" ]]; then
|
||||
_zenotif $*
|
||||
else
|
||||
_zeninfo $*
|
||||
fi
|
||||
}
|
||||
function _zenotif {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--notification \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
zenity \
|
||||
$Icon \
|
||||
--notification \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
}
|
||||
function _zeninfo {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--info \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
zenity \
|
||||
$Icon \
|
||||
--info \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
}
|
||||
function _zenerr {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--error \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
zenity \
|
||||
$Icon \
|
||||
--error \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
}
|
||||
function _zenprog {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--progress \
|
||||
--auto-close \
|
||||
--pulsate \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
zenity \
|
||||
$Icon \
|
||||
--progress \
|
||||
--auto-close \
|
||||
--pulsate \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
}
|
||||
function _zenprognc {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--progress \
|
||||
--auto-close \
|
||||
--no-cancel \
|
||||
--pulsate \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
zenity \
|
||||
$Icon \
|
||||
--progress \
|
||||
--auto-close \
|
||||
--no-cancel \
|
||||
--pulsate \
|
||||
--title="$1" \
|
||||
--text="$2"
|
||||
}
|
||||
function _zenentry {
|
||||
zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--entry \
|
||||
--title="$1" \
|
||||
--text="$2" \
|
||||
--entry-text="$3"
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ Some pinentry code shamelessly stolen from tomb
|
||||
# Ask user for a password
|
||||
# Wraps around the pinentry command, from the GnuPG project, as it
|
||||
# provides better security and conveniently use the right toolkit.
|
||||
ask_password() {
|
||||
local description="$1"
|
||||
local title="${2:-Enter tomb password.}"
|
||||
local output
|
||||
local password
|
||||
local gtkrc
|
||||
local theme
|
||||
|
||||
# Distributions have broken wrappers for pinentry: they do
|
||||
# implement fallback, but they disrupt the output somehow. We are
|
||||
# better off relying on less intermediaries, so we implement our
|
||||
# own fallback mechanisms. Pinentry supported: curses, gtk-2, qt4
|
||||
# and x11.
|
||||
|
||||
# make sure LANG is set, default to C
|
||||
LANG=${LANG:-C}
|
||||
|
||||
_verbose "asking password with tty=$TTY lc-ctype=$LANG"
|
||||
|
||||
if [[ "$DISPLAY" = "" ]]; then
|
||||
|
||||
if _is_found "pinentry-curses"; then
|
||||
_verbose "using pinentry-curses"
|
||||
output=`cat <<EOF | pinentry-curses
|
||||
OPTION ttyname=$TTY
|
||||
OPTION lc-ctype=$LANG
|
||||
SETTITLE $title
|
||||
SETDESC $description
|
||||
SETPROMPT Password:
|
||||
GETPIN
|
||||
EOF`
|
||||
else
|
||||
_failure "Cannot find pinentry-curses and no DISPLAY detected."
|
||||
fi
|
||||
|
||||
else # a DISPLAY is found to be active
|
||||
|
||||
# customized gtk2 dialog with a skull (if extras are installed)
|
||||
if _is_found "pinentry-gtk-2"; then
|
||||
_verbose "using pinentry-gtk2"
|
||||
|
||||
gtkrc=""
|
||||
theme=/share/themes/tomb/gtk-2.0-key/gtkrc
|
||||
for i in /usr/local /usr; do
|
||||
[[ -r $i/$theme ]] && {
|
||||
gtkrc="$i/$theme"
|
||||
break
|
||||
}
|
||||
done
|
||||
[[ "$gtkrc" = "" ]] || {
|
||||
gtkrc_old="$GTK2_RC_FILES"
|
||||
export GTK2_RC_FILES="$gtkrc"
|
||||
}
|
||||
output=`cat <<EOF | pinentry-gtk-2
|
||||
OPTION ttyname=$TTY
|
||||
OPTION lc-ctype=$LANG
|
||||
SETTITLE $title
|
||||
SETDESC $description
|
||||
SETPROMPT Password:
|
||||
GETPIN
|
||||
EOF`
|
||||
[[ "$gtkrc" = "" ]] || export GTK2_RC_FILES="$gtkrc_old"
|
||||
|
||||
# TODO QT4 customization of dialog
|
||||
elif _is_found "pinentry-qt4"; then
|
||||
_verbose "using pinentry-qt4"
|
||||
|
||||
output=`cat <<EOF | pinentry-qt4
|
||||
OPTION ttyname=$TTY
|
||||
OPTION lc-ctype=$LANG
|
||||
SETTITLE $title
|
||||
SETDESC $description
|
||||
SETPROMPT Password:
|
||||
GETPIN
|
||||
EOF`
|
||||
|
||||
# TODO X11 customization of dialog
|
||||
elif _is_found "pinentry-x11"; then
|
||||
_verbose "using pinentry-x11"
|
||||
|
||||
output=`cat <<EOF | pinentry-x11
|
||||
OPTION ttyname=$TTY
|
||||
OPTION lc-ctype=$LANG
|
||||
SETTITLE $title
|
||||
SETDESC $description
|
||||
SETPROMPT Password:
|
||||
GETPIN
|
||||
EOF`
|
||||
|
||||
else
|
||||
|
||||
if _is_found "pinentry-curses"; then
|
||||
_verbose "using pinentry-curses"
|
||||
|
||||
_warning "Detected DISPLAY, but only pinentry-curses is found."
|
||||
output=`cat <<EOF | pinentry-curses
|
||||
OPTION ttyname=$TTY
|
||||
OPTION lc-ctype=$LANG
|
||||
SETTITLE $title
|
||||
SETDESC $description
|
||||
SETPROMPT Password:
|
||||
GETPIN
|
||||
EOF`
|
||||
else
|
||||
_failure "Cannot find any pinentry: impossible to ask for password."
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
fi # end of DISPLAY block
|
||||
|
||||
# parse the pinentry output
|
||||
for i in ${(f)output}; do
|
||||
[[ "$i" =~ "^ERR.*" ]] && {
|
||||
_warning "Pinentry error: ::1 error::" ${i[(w)3]}
|
||||
print "canceled"
|
||||
return 1 }
|
||||
|
||||
# here the password is found
|
||||
[[ "$i" =~ "^D .*" ]] && password="${i##D }"
|
||||
done
|
||||
|
||||
[[ "$password" = "" ]] && {
|
||||
_warning "Empty password"
|
||||
print "empty"
|
||||
return 1 }
|
||||
|
||||
print "$password"
|
||||
return 0
|
||||
}
|
||||
|
||||
_is_found() {
|
||||
# returns 0 if binary is found in path
|
||||
[[ "$1" = "" ]] && return 1
|
||||
command -v "$1" 1>/dev/null 2>/dev/null
|
||||
return $?
|
||||
}
|
||||
|
||||
function _warning no() {
|
||||
option_is_set -q || _msg warning $@
|
||||
return 1
|
||||
}
|
||||
|
||||
function _verbose xxx() {
|
||||
option_is_set -D && _msg verbose $@
|
||||
return 0
|
||||
}
|
||||
|
||||
function _failure die() {
|
||||
typeset -i exitcode=${exitv:-1}
|
||||
option_is_set -q || _msg failure $@
|
||||
# be sure we forget the secrets we were told
|
||||
exit $exitcode
|
||||
zenity \
|
||||
$Icon \
|
||||
--entry \
|
||||
--title="$1" \
|
||||
--text="$2" \
|
||||
--entry-text="$3"
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ _clean - Clean function, removes sensitive stuff from memory
|
||||
function _clean {
|
||||
unset $?
|
||||
local rr="$RANDOM"
|
||||
while [[ ${#rr} -lt 500 ]]; do
|
||||
rr+="$RANDOM"
|
||||
done
|
||||
unset $?
|
||||
local rr="$RANDOM"
|
||||
while [[ ${#rr} -lt 500 ]]; do
|
||||
rr+="$RANDOM"
|
||||
done
|
||||
|
||||
cmnd="$rr"; unset cmnd
|
||||
tombname="$rr"; unset tombname
|
||||
tombsize="$rr"; unset tombsize
|
||||
keyfile="$rr"; unset keyfile
|
||||
sudoassword="$rr"; unset sudoassword
|
||||
tombtmp="/tmp/tombtmp"
|
||||
if [ -f $tombtmp ]; then
|
||||
dd if=/dev/urandom of=$tombtmp bs=800 count=1
|
||||
rm -f $tombtmp
|
||||
fi
|
||||
tombtmp="$rr"; unset tombtmp
|
||||
newkey="$rr"; unset newkey
|
||||
jpegfile="$rr"; unset jpegfile
|
||||
cmnd="$rr"; unset cmnd
|
||||
tombname="$rr"; unset tombname
|
||||
tombsize="$rr"; unset tombsize
|
||||
keyfile="$rr"; unset keyfile
|
||||
tombtmp="/tmp/tombtmp"
|
||||
if [ -f $tombtmp ]; then
|
||||
dd if=/dev/urandom of=$tombtmp bs=800 count=1
|
||||
rm -f $tombtmp
|
||||
fi
|
||||
tombtmp="$rr"; unset tombtmp
|
||||
newkey="$rr"; unset newkey
|
||||
jpegfile="$rr"; unset jpegfile
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ _main - Main window
|
||||
function _main {
|
||||
_clean
|
||||
cmnd=`zenity \
|
||||
--window-icon="$MONMORT" \
|
||||
--title="gtomb" \
|
||||
--width=640 \
|
||||
--height=420 \
|
||||
--list \
|
||||
--hide-header \
|
||||
--text="gtomb v$ver\nThe GUI wrapper for Tomb, the crypto undertaker." \
|
||||
--separator=" & " \
|
||||
--column=Function \
|
||||
--column=Description \
|
||||
"dig" "Dig a new tomb of chosen size" \
|
||||
"forge" "Forge a new key used to lock tombs" \
|
||||
"lock" "Lock a non-locked tomb using an existing key" \
|
||||
"open" "Open an existing tomb" \
|
||||
"index" "Index the contents of all tombs." \
|
||||
"search" "Search the content of indexed tombs." \
|
||||
"list" "List all open tombs and information on them" \
|
||||
"close" "Close a specific tomb (or all)" \
|
||||
"slam" "Slam a tomb (or all) killing all programs using it" \
|
||||
"resize" "Resize a tomb to a new size (can only grow)" \
|
||||
"passwd" "Change the passphrase of a key" \
|
||||
"setkey" "Change the key of an existing tomb" \
|
||||
"engrave" "Generates a QR code of a key to be saved on paper" \
|
||||
"bury" "Hide a key inside a JPEG image" \
|
||||
"exhume" "Extract a key from a JPEG image"`
|
||||
eval "_$cmnd"
|
||||
_clean
|
||||
cmnd=`zenity \
|
||||
$Icon \
|
||||
--title="gtomb" \
|
||||
--width=400 \
|
||||
--height=445 \
|
||||
--list \
|
||||
--hide-header \
|
||||
--text="gtomb v$ver\nThe GUI wrapper for Tomb, the crypto undertaker." \
|
||||
--separator=" & " \
|
||||
--column=Function \
|
||||
--column=Description \
|
||||
"dig" "Dig a new tomb of chosen size" \
|
||||
"forge" "Forge a new key used to lock tombs" \
|
||||
"lock" "Lock a non-locked tomb using an existing key" \
|
||||
"open" "Open an existing tomb" \
|
||||
"index" "Index the contents of all tombs." \
|
||||
"search" "Search the content of indexed tombs." \
|
||||
"list" "List all open tombs and information on them" \
|
||||
"close" "Close a specific tomb (or all)" \
|
||||
"slam" "Slam a tomb (or all) killing all programs using it" \
|
||||
"resize" "Resize a tomb to a new size (can only grow)" \
|
||||
"passwd" "Change the passphrase of a key" \
|
||||
"setkey" "Change the key of an existing tomb" \
|
||||
"engrave" "Generates a QR code of a key to be saved on paper" \
|
||||
"bury" "Hide a key inside a JPEG image" \
|
||||
"exhume" "Extract a key from a JPEG image"`
|
||||
if [[ "$?" = 1 && $SWAPOFF = "true" ]]; then
|
||||
zenity --password --title="sudo swapon -a" | sudo swapon -a
|
||||
unset $?
|
||||
fi
|
||||
eval "_$cmnd"
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ dig - Dig a new tomb
|
||||
function _dig {
|
||||
tombname=`_fsave "Choose where to dig your tomb" "secret.tomb"`
|
||||
res=$?
|
||||
tombname=`_fsave "Choose where to dig your tomb" "secret.tomb"`
|
||||
res=$?
|
||||
|
||||
if [[ -f "$tombname" ]]; then
|
||||
_zenerr "Error" "This tomb already exists. I am not digging here."
|
||||
exec _main
|
||||
elif [[ -z "$tombname" ]]; then
|
||||
_info "gtomb" "Cancelled"
|
||||
exec _main
|
||||
fi
|
||||
if [[ -f "$tombname" ]]; then
|
||||
_zenerr "Error" "This tomb already exists. I am not digging here."
|
||||
exec _main
|
||||
elif [[ -z "$tombname" ]]; then
|
||||
_info "gtomb" "Cancelled"
|
||||
exec _main
|
||||
fi
|
||||
|
||||
[[ $res = 0 ]] || exec _main
|
||||
|
||||
|
@ -387,7 +218,7 @@ function _dig {
|
|||
exec _main
|
||||
fi
|
||||
|
||||
[[ $res = 0 ]] || { _zenwarn "Warning" "Tomb digging canceled." ; exec _main }
|
||||
[[ $res = 0 ]] || { _zenwarn "Tomb digging cancelled." ; exec _main }
|
||||
|
||||
"$TOMBPATH" dig -s "$tombsize" "$tombname" | \
|
||||
_zenprog "Digging new tomb" "Please wait while your tomb is being dug..." &
|
||||
|
@ -401,10 +232,12 @@ function _dig {
|
|||
|
||||
[[ -n "$PID_DD" && -z "$PID_ZEN" ]] && {
|
||||
kill -9 $PID_DD
|
||||
_zenwarn "Warning" "Tomb digging cancelled."
|
||||
_zenwarn "Tomb digging cancelled."
|
||||
rm -f "$tombname"
|
||||
exec _main
|
||||
}
|
||||
|
||||
wait
|
||||
|
||||
_info "Success" "Your tomb has been dug in $tombname"
|
||||
exec _main
|
||||
|
@ -413,42 +246,42 @@ function _dig {
|
|||
|
||||
# {{{ forge - Forge a new key
|
||||
function _forge {
|
||||
keyfile=`_fsave "Choose where to forge your key" "secret.tomb.key"`
|
||||
res=$?
|
||||
keyfile=`_fsave "Choose where to forge your key" "secret.tomb.key"`
|
||||
res=$?
|
||||
|
||||
if [[ -f $keyfile ]]; then
|
||||
_zenerr "Error" "This key already exists. I am not overwriting."
|
||||
exec _main
|
||||
elif [[ -z $keyfile ]]; then
|
||||
_info "gtomb" "Canceled"
|
||||
exec _main
|
||||
fi
|
||||
if [[ -f $keyfile ]]; then
|
||||
_zenerr "Error" "This key already exists. I am not overwriting."
|
||||
exec _main
|
||||
elif [[ -z $keyfile ]]; then
|
||||
_info "gtomb" "Cancelled"
|
||||
exec _main
|
||||
fi
|
||||
|
||||
kdf=""
|
||||
kdfiter=""
|
||||
if [[ -x $HEXENC ]] && [[ -x $GENSALT ]] && [[ -x $GETITER ]] && [[ -x $PBKDF ]]; then
|
||||
_zenques "Do you want to use KDF? (Generates passwords armored against dictionary attacks)"
|
||||
[[ $? == "0" ]] && {
|
||||
kdf="--kdf"
|
||||
kdfiter=`_zenentry "Iterations" "Enter the delay (itertime) in seconds for each time \n\
|
||||
kdf=""
|
||||
kdfiter=""
|
||||
if [[ -x $GENSALT ]] && [[ -x $GETITER ]] && [[ -x $PBKDF ]]; then
|
||||
_zenques "Do you want to use KDF? (Generates passwords armored against dictionary attacks)"
|
||||
if [[ $? == "0" ]]; then
|
||||
kdf="--kdf"
|
||||
kdfiter=`_zenentry "Iterations" "Enter the delay (itertime) in seconds for each time \n\
|
||||
this key is used:" "2"`
|
||||
|
||||
re='^[0-9]+$'
|
||||
if ! [[ $kdfiter =~ $re ]]; then
|
||||
_zenerr "Error" "Please choose a valid number."
|
||||
exec _main
|
||||
elif [[ -z $kdfiter ]]; then
|
||||
_info "gtomb" "Canceled"
|
||||
exec _main
|
||||
fi
|
||||
}
|
||||
else
|
||||
_zenotif "gtomb" "KDF binaries not found."
|
||||
fi
|
||||
re='^[0-9]+$'
|
||||
if ! [[ $kdfiter =~ $re ]]; then
|
||||
_zenerr "Error" "Please choose a valid number."
|
||||
exec _main
|
||||
elif [[ -z $kdfiter ]]; then
|
||||
_info "gtomb" "Cancelled"
|
||||
exec _main
|
||||
fi
|
||||
fi
|
||||
else
|
||||
_zenotif "gtomb" "KDF binaries not found."
|
||||
fi
|
||||
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
"$TOMBPATH" forge "$keyfile" "$kdf" "$kdfiter" | \
|
||||
"$TOMBPATH" forge "$keyfile" "$kdf" "$kdfiter" "$FLAG" | \
|
||||
_zenprog "Forging key" "Please wait while your key is being forged...\n\
|
||||
You can move your mouse around and use your computer to speed up the process." &
|
||||
|
||||
|
@ -460,10 +293,12 @@ You can move your mouse around and use your computer to speed up the process." &
|
|||
done
|
||||
[[ -n "$PID_DD" && -z "$PID_ZEN" ]] && {
|
||||
kill -9 $PID_DD
|
||||
_zenwarn "Warning" "Forging cancelled."
|
||||
_zenwarn "Forging cancelled."
|
||||
rm -f $keyfile
|
||||
exec _main
|
||||
}
|
||||
|
||||
wait
|
||||
|
||||
_info "Success" "Your key is now forged in $keyfile"
|
||||
exec _main
|
||||
|
@ -472,17 +307,19 @@ You can move your mouse around and use your computer to speed up the process." &
|
|||
|
||||
# {{{ lock - Lock a non-locked tomb
|
||||
function _lock {
|
||||
tombname=`_fsel "Select a tomb to lock"`
|
||||
[[ -n $tombname ]] || { _zenotif "gtomb" "Cancelled" ; exec _main }
|
||||
tombname=`_fsel "Select a tomb to lock"`
|
||||
[[ -n $tombname ]] || { _zenotif "gtomb" "Cancelled" ; exec _main }
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
keyfile=`_fsel "Choose the key for your tomb"`
|
||||
[[ -n $keyfile ]] || { _zenotif "gtomb" "Cancelled" ; exec _main }
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
_sudo
|
||||
"$TOMBPATH" lock "$tombname" -k "$keyfile" | \
|
||||
_zenprognc "Locking your tomb" "Please wait while your tomb is being locked..."
|
||||
|
||||
wait
|
||||
|
||||
_info "Success" "Your tomb is now locked."
|
||||
exec _main
|
||||
}
|
||||
|
@ -490,14 +327,16 @@ function _lock {
|
|||
|
||||
# {{{ open - Open an existing tomb
|
||||
function _open {
|
||||
tombname=`_fsel "Choose a tomb to open"`
|
||||
tombname=`_fsel "Choose a tomb to open"`
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
keyfile=`_fsel "Choose the key for your tomb"`
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
_sudo
|
||||
"$TOMBPATH" open "$tombname" -k "$keyfile"
|
||||
"$TOMBPATH" open "$tombname" -k "$keyfile" "$FLAG"
|
||||
|
||||
wait
|
||||
|
||||
_info "Success" "Your tomb is now open."
|
||||
exec _main
|
||||
}
|
||||
|
@ -505,33 +344,33 @@ function _open {
|
|||
|
||||
# {{{ list - list all open tombs, along with their mountpoint
|
||||
function _list {
|
||||
tombtmp="/tmp/tombtmp"
|
||||
"$TOMBPATH" list --get-mountpoint > $tombtmp
|
||||
tombname=`cat $tombtmp | \
|
||||
sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
|
||||
zenity \
|
||||
--title="Currently open tombs" \
|
||||
--window-icon="$MONMORT" \
|
||||
--width=640 --height=380 --list \
|
||||
--separator=" & " \
|
||||
--text="Here are your open tombs" \
|
||||
--column=Tomb \
|
||||
--column=Path `
|
||||
tombtmp="/tmp/tombtmp"
|
||||
"$TOMBPATH" list --get-mountpoint > $tombtmp
|
||||
tombname=`cat $tombtmp | \
|
||||
sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
|
||||
zenity \
|
||||
--title="Currently open tombs" \
|
||||
$Icon \
|
||||
--width=400 --height=380 --list \
|
||||
--separator=" & " \
|
||||
--text="Here are your open tombs" \
|
||||
--column="Tomb" \
|
||||
--column="Path" `
|
||||
|
||||
tombname=`echo "$tombname" | cut -c1-16`
|
||||
tombname=`echo "$tombname" | cut -c1-16`
|
||||
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
listchoice=`zenity \
|
||||
--title="Choose action" \
|
||||
--window-icon="$MONMORT" \
|
||||
--width=640 --height=400 --list \
|
||||
$Icon \
|
||||
--width=400 --height=380 --list \
|
||||
--separator=" & " \
|
||||
--text="What do you want to do with this tomb?" \
|
||||
--column=Command \
|
||||
--column=Description \
|
||||
--column="Command" \
|
||||
--column="Description" \
|
||||
"disindex" "Disable indexing of this tomb." \
|
||||
"enindex" "Enable indexing of this tomb." \
|
||||
"enindex" "Enable indexing of this tomb." \
|
||||
"close" "Close the selected tomb." \
|
||||
"slam" "Slam the selected tomb." \
|
||||
"binds" "Edit current bind-hooks." \
|
||||
|
@ -541,13 +380,11 @@ function _list {
|
|||
|
||||
case $listchoice in
|
||||
close)
|
||||
_sudo
|
||||
"$TOMBPATH" close "$tombname"
|
||||
_zeninfo "Success" "Tomb closed successfully!"
|
||||
exec _main
|
||||
;;
|
||||
slam)
|
||||
_sudo
|
||||
"$TOMBPATH" slam "$tombname"
|
||||
_info "Success" "$tombname slammed successfully!"
|
||||
exec _main
|
||||
|
@ -595,22 +432,21 @@ function _list {
|
|||
|
||||
# {{{ close - Close open tomb(s)
|
||||
function _close {
|
||||
tombtmp="/tmp/tombtmp"
|
||||
"$TOMBPATH" list --get-mountpoint > $tombtmp
|
||||
echo "/all" >> $tombtmp
|
||||
tombname=`cat $tombtmp | \
|
||||
sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
|
||||
zenity \
|
||||
--title="Choose a tomb to close" \
|
||||
--window-icon="$MONMORT" \
|
||||
--width=640 --height=380 --list \
|
||||
--separator=" & " \
|
||||
--column=Tomb \
|
||||
--column=Path `
|
||||
tombtmp="/tmp/tombtmp"
|
||||
"$TOMBPATH" list --get-mountpoint > $tombtmp
|
||||
echo "/all" >> $tombtmp
|
||||
tombname=`cat $tombtmp | \
|
||||
sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
|
||||
zenity \
|
||||
--title="Choose a tomb to close" \
|
||||
$Icon \
|
||||
--width=640 --height=380 --list \
|
||||
--separator=" & " \
|
||||
--column=Tomb \
|
||||
--column=Path `
|
||||
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
_sudo
|
||||
tombname=`echo "$tombname" | cut -c1-16`
|
||||
"$TOMBPATH" close "$tombname"
|
||||
_info "Success" "Closed successfully!"
|
||||
|
@ -620,22 +456,21 @@ function _close {
|
|||
|
||||
# {{{ slam - Slam open tombs
|
||||
function _slam {
|
||||
tombtmp="/tmp/tombtmp"
|
||||
"$TOMBPATH" list --get-mountpoint > $tombtmp
|
||||
echo "/all" >> $tombtmp
|
||||
tombname=`cat $tombtmp | \
|
||||
sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
|
||||
zenity \
|
||||
--title="Choose a tomb to slam" \
|
||||
--window-icon="$MONMORT" \
|
||||
--width=640 --height=380 --list \
|
||||
--separator=" & " \
|
||||
--column=Tomb \
|
||||
--column=Path `
|
||||
tombtmp="/tmp/tombtmp"
|
||||
"$TOMBPATH" list --get-mountpoint > $tombtmp
|
||||
echo "/all" >> $tombtmp
|
||||
tombname=`cat $tombtmp | \
|
||||
sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
|
||||
zenity \
|
||||
--title="Choose a tomb to slam" \
|
||||
$Icon \
|
||||
--width=640 --height=380 --list \
|
||||
--separator=" & " \
|
||||
--column=Tomb \
|
||||
--column=Path `
|
||||
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
_sudo
|
||||
tombname=`echo "$tombname" | cut -c1-16`
|
||||
"$TOMBPATH" slam "$tombname"
|
||||
_info "Success" "Slammed successfully!"
|
||||
|
@ -645,11 +480,11 @@ function _slam {
|
|||
|
||||
# {{{ resize - Resize an existing *closed* tomb
|
||||
function _resize {
|
||||
tombname=`_fsel "Choose a tomb to resize"`
|
||||
res=$?
|
||||
_zenques "Is your tomb closed?"
|
||||
tombname=`_fsel "Choose a tomb to resize"`
|
||||
res=$?
|
||||
_zenques "Is your tomb closed?"
|
||||
|
||||
[[ $? = 0 ]] || { _zenwarn "gtomb" "Please close the tomb before resizing." ; exec _main }
|
||||
[[ $? = 0 ]] || { _zenwarn "Please close the tomb before resizing." ; exec _main }
|
||||
|
||||
[[ $res = 0 ]] || exec _main
|
||||
|
||||
|
@ -670,7 +505,6 @@ function _resize {
|
|||
keyfile=`_fsel "Choose according keyfile"`
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
_sudo
|
||||
"$TOMBPATH" resize "$tombname" -s "$tombsize" -k "$keyfile" | \
|
||||
_zenprognc "Resizing tomb." "Please wait while your tomb is being resized..."
|
||||
_info "Success" "Tomb resized successfully!"
|
||||
|
@ -680,10 +514,10 @@ function _resize {
|
|||
|
||||
# {{{ passwd - Change existing key's passphrase
|
||||
function _passwd {
|
||||
keyfile=`_fsel "Choose a keyfile"`
|
||||
keyfile=`_fsel "Choose a keyfile"`
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
"$TOMBPATH" passwd -k "$keyfile" | \
|
||||
"$TOMBPATH" passwd -k "$keyfile" "$FLAG" | \
|
||||
_zenprognc "Changing passphrase" "Please wait while your key's passphrase is being changed..."
|
||||
|
||||
_info "Success" "$keyfile passphrase changed successfully!"
|
||||
|
@ -693,7 +527,7 @@ function _passwd {
|
|||
|
||||
# {{{ setkey - Change a tomb's keyfile
|
||||
function _setkey {
|
||||
tombname=`_fsel "Choose a tomb to change its keyfile"`
|
||||
tombname=`_fsel "Choose a tomb to change its keyfile"`
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
keyfile=`_fsel "Chosse your tomb's old keyfile"`
|
||||
|
@ -702,19 +536,18 @@ function _setkey {
|
|||
newkey=`_fsel "Choose your tomb's new keyfile"`
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
_sudo
|
||||
"$TOMBPATH" setkey -k "$newkey" "$keyfile" "$tombname" | \
|
||||
"$TOMBPATH" setkey -k "$newkey" "$keyfile" "$tombname" "$FLAG" | \
|
||||
_zenprognc "Changing key" "Please wait while your tomb's key is being changed..."
|
||||
|
||||
_info "Success" "$tombname keyfile successfully changed! Now using $newkey"
|
||||
exec _main
|
||||
exec _main
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ engrave - generate QR code of a key
|
||||
function _engrave {
|
||||
which qrencode || _zenwarn "Warning" "qrencode is not installed. Install it and try again"
|
||||
keyfile=`_fsel "Choose a keyfile to engrave"`
|
||||
which qrencode || _zenwarn "qrencode is not installed. Install it and try again."
|
||||
keyfile=`_fsel "Choose a keyfile to engrave"`
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
jpegfile=`_fsave "Choose where to save your keyfile (PNG format)"`
|
||||
|
@ -730,8 +563,8 @@ function _engrave {
|
|||
|
||||
# {{{ bury - hide a keyfile in a JPEG image
|
||||
function _bury {
|
||||
which steghide || _zenwarn "Warning" "steghide is not installed. Install it and try again"
|
||||
keyfile=`_fsel "Choose keyfile"`
|
||||
which steghide || _zenwarn "steghide is not installed. Install it and try again."
|
||||
keyfile=`_fsel "Choose keyfile"`
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
jpegfile=`_fsel "Choose JPEG file"`
|
||||
|
@ -745,8 +578,8 @@ function _bury {
|
|||
|
||||
# {{{ exhume - extract keyfile from JPEG
|
||||
function _exhume {
|
||||
which steghide || _zenwarn "Warning" "steghide is not installed. Install it and try again"
|
||||
jpegfile=`_fsel "Choose JPEG file"`
|
||||
which steghide || _zenwarn "steghide is not installed. Install it and try again."
|
||||
jpegfile=`_fsel "Choose JPEG file"`
|
||||
[[ $? = 0 ]] || exec _main
|
||||
|
||||
keyfile=`_fsave "Choose where to extract your key"`
|
||||
|
@ -760,18 +593,18 @@ function _exhume {
|
|||
|
||||
# {{{ index - index the contents of open tombs
|
||||
function _index {
|
||||
which locate || _zenwarn "Warning" "mlocate is not installed. Install it and try again"
|
||||
"$TOMBPATH" index | _zenprognc "Indexing" "Please wait while the open tombs are being indexed..."
|
||||
_info "Success" "Tombs indexed!"
|
||||
exec _main
|
||||
which locate || _zenwarn "mlocate is not installed. Install it and try again."
|
||||
"$TOMBPATH" index | _zenprognc "Indexing" "Please wait while the open tombs are being indexed..."
|
||||
_info "Success" "Tombs indexed!"
|
||||
exec _main
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ search - searches the contents of indexed tombs
|
||||
function _search {
|
||||
strings=""
|
||||
_searchstring
|
||||
exec _main
|
||||
strings=""
|
||||
_searchstring
|
||||
exec _main
|
||||
}
|
||||
|
||||
function _searchstring {
|
||||
|
@ -799,7 +632,16 @@ function _searchstring {
|
|||
function _ { _clean } # I like cleaning :)
|
||||
|
||||
[[ -x $TOMBPATH ]] || {
|
||||
_zenwarn "Warning" "Tomb binary is not executable or doesn't exist in the current path. Install it or edit the script to point to the correct path."
|
||||
_zenwarn "Tomb binary is not executable or doesn't exist in the current path. Install it or edit the script to point to the correct path."
|
||||
exit 1 }
|
||||
|
||||
if [[ $SWAPOFF = "true" ]]; then
|
||||
FLAG=""
|
||||
zenity --password --title="sudo swapoff -a" | sudo swapoff -a
|
||||
unset $?
|
||||
else
|
||||
FLAG="-f"
|
||||
fi
|
||||
|
||||
_main
|
||||
|
||||
|
|
|
@ -5,8 +5,11 @@ cd libsphinx
|
|||
git submodule update --init --recursive --remote
|
||||
cd src
|
||||
sed -i 's|/usr/local|/usr|' makefile
|
||||
make
|
||||
sudo make install
|
||||
ldconfig
|
||||
pip3 install pwdsphinx
|
||||
sudo mkdir -p /etc/sphinx
|
||||
make && make install && ldconfig
|
||||
cd ../..
|
||||
git clone https://github.com/stef/pwdsphinx
|
||||
cd pwdsphinx
|
||||
python3 setup.py install
|
||||
mkdir -p /etc/sphinx && cp ../test/sphinx.cfg /etc/sphinx/config && cd /etc/sphinx
|
||||
openssl req -new -x509 -nodes -out server.crt -keyout server.key -subj '/CN=localhost'
|
||||
sphinx init
|
||||
|
|
|
@ -41,7 +41,7 @@ int main(int argc, char *argv[]) {
|
|||
} else {
|
||||
while( (read_bytes=fread(buf, sizeof(char), 2, stdin)) != 0) {
|
||||
if(read_bytes == 1) buf[1]='\0';
|
||||
sscanf(buf, "%x", &c);
|
||||
sscanf(buf, "%s", &c);
|
||||
printf("%c", c);
|
||||
}
|
||||
return 0;
|
||||
|
|
46
extras/portable/GNUmakefile
Normal file
46
extras/portable/GNUmakefile
Normal file
|
@ -0,0 +1,46 @@
|
|||
.PHONY: test
|
||||
|
||||
system := $(shell uname -s)
|
||||
# if [ ${system} == "FreeBSD" ]; then sudo kldload fusefs; fi
|
||||
|
||||
TOMB ?= test.tomb
|
||||
|
||||
#########
|
||||
## BUILD
|
||||
#########
|
||||
test: create-open-close check-random-data
|
||||
|
||||
########
|
||||
## TEST
|
||||
########
|
||||
create-open-close:
|
||||
TOMB=${TOMB} ./test/bats/bin/bats ./test/create_open_close.bats
|
||||
chmod a+r ${TOMB}
|
||||
|
||||
# arg: TOMB='path to tomb containing random.data'
|
||||
# assumes $TOMB.hash is the pre-calculated hash on creation
|
||||
check-random-data:
|
||||
TOMB=${TOMB} ./test/bats/bin/bats test/check-random-data.bats
|
||||
|
||||
########
|
||||
## LINT
|
||||
########
|
||||
shellcheck:
|
||||
shellcheck -s sh tomb -e SC2006,SC2059,SC2034
|
||||
|
||||
########
|
||||
## DEPS
|
||||
########
|
||||
download-deps-ubuntu22:
|
||||
curl https://files.dyne.org/tomb3/third-party/veracrypt-ubuntu22-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
|
||||
curl https://files.dyne.org/zenroom/nightly/zenroom-linux-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
|
||||
|
||||
download-deps-ubuntu20:
|
||||
curl https://files.dyne.org/tomb3/third-party/veracrypt-ubuntu20-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
|
||||
curl https://files.dyne.org/zenroom/nightly/zenroom-linux-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
|
||||
|
||||
download-deps-freebsd13:
|
||||
curl https://files.dyne.org/tomb3/third-party/veracrypt-freebsd13-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
|
||||
curl https://files.dyne.org/tomb3/third-party/zenroom-freebsd13-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
|
||||
|
||||
#curl https://files.dyne.org/zenroom/nightly/zenroom-freebsd13-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
|
69
extras/portable/README.md
Normal file
69
extras/portable/README.md
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
# Portable Tomb :: the crypto undertaker runs everywhere
|
||||
|
||||
[![continuous integration tests badge](https://github.com/dyne/tomb/actions/workflows/portable.yml/badge.svg)](https://github.com/dyne/Tomb/actions) test coverage status for portability
|
||||
|
||||
## ⚠️ WORK IN PROGRESS 🛠️
|
||||
|
||||
This is the portable version of [Tomb](https://github.com/dyne/tomb)
|
||||
|
||||
[![software by Dyne.org](https://files.dyne.org/software_by_dyne.png)](http://www.dyne.org)
|
||||
|
||||
# Purpose
|
||||
|
||||
Portable tomb achieves direct **interoperable access to tomb volumes** between:
|
||||
|
||||
- GNU base Linux (Ubuntu)
|
||||
- Busybox based Linux (Alpine)
|
||||
- MS/Windows using WSL2 (Ubuntu)
|
||||
- FreeBSD (WIP using libluksde)
|
||||
- ~~Apple/OSX using [MacFUSE](https://osxfuse.github.io/)~~
|
||||
|
||||
After some extensive testing during 2022, __adoption of Veracrypt in portable Tomb has been dropped__ because of unreliability and bad performance.
|
||||
|
||||
Portable tomb stays as an experimental branch that aims to reduce dependencies and in particular uses only the **POSIX sh interpreter**.
|
||||
|
||||
# Status
|
||||
|
||||
Portable tomb development is in progress and tracked via issues and the [portable milestone](https://github.com/dyne/Tomb/milestone/9).
|
||||
|
||||
## Features
|
||||
|
||||
The following features will be implemented where possible:
|
||||
|
||||
- mount bind (Linux only)
|
||||
- ps / slam
|
||||
- resize (pending investigation)
|
||||
- index & search ([recoll based](https://github.com/dyne/Tomb/issues/211))
|
||||
- bury / exhume
|
||||
|
||||
## Dependencies
|
||||
|
||||
- FreeBSD: `fusefs-libs3 fusefs-lkl e2fsprogs util-linux libluksde`
|
||||
- Linux: `fuse3 util-linux`
|
||||
- crossplatform [Veracrypt binaries](https://files.dyne.org/tomb3/third-party) console-only
|
||||
|
||||
## Note on Veracrypt
|
||||
|
||||
The way upstream developers distribute Veracrypt is far from meeting our minimalist needs, but the console-only binary once installed has a few library dependencies and is all what we need.
|
||||
|
||||
I setup [my own build](https://github.com/jaromil/veracrypt) and provide binary builds of Veracrypt v1.25.9 for download on Tomb's file repository for testing purposes.
|
||||
|
||||
# Disclaimer
|
||||
|
||||
Tomb is Copyright (C) 2007-2023 by the Dyne.org Foundation and
|
||||
developed by [Jaromil](https://github.com/jaromil).
|
||||
|
||||
This source code is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This source code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
|
||||
to the GNU Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Public License along with
|
||||
this source code; if not, write to: Free Software Foundation, Inc.,
|
||||
675 Mass Ave, Cambridge, MA 02139, USA.
|
59
extras/portable/test/bats/bin/bats
Executable file
59
extras/portable/test/bats/bin/bats
Executable file
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if command -v greadlink >/dev/null; then
|
||||
bats_readlinkf() {
|
||||
greadlink -f "$1"
|
||||
}
|
||||
else
|
||||
bats_readlinkf() {
|
||||
readlink -f "$1"
|
||||
}
|
||||
fi
|
||||
|
||||
fallback_to_readlinkf_posix() {
|
||||
bats_readlinkf() {
|
||||
[ "${1:-}" ] || return 1
|
||||
max_symlinks=40
|
||||
CDPATH='' # to avoid changing to an unexpected directory
|
||||
|
||||
target=$1
|
||||
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
|
||||
[ -d "${target:-/}" ] && target="$target/"
|
||||
|
||||
cd -P . 2>/dev/null || return 1
|
||||
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
|
||||
if [ ! "$target" = "${target%/*}" ]; then
|
||||
case $target in
|
||||
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
|
||||
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
|
||||
esac
|
||||
target=${target##*/}
|
||||
fi
|
||||
|
||||
if [ ! -L "$target" ]; then
|
||||
target="${PWD%/}${target:+/}${target}"
|
||||
printf '%s\n' "${target:-/}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
|
||||
# <file mode>, <number of links>, <owner name>, <group name>,
|
||||
# <size>, <date and time>, <pathname of link>, <contents of link>
|
||||
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
|
||||
link=$(ls -dl -- "$target" 2>/dev/null) || break
|
||||
target=${link#*" $target -> "}
|
||||
done
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
if ! BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}" 2>/dev/null); then
|
||||
fallback_to_readlinkf_posix
|
||||
BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}")
|
||||
fi
|
||||
|
||||
export BATS_ROOT=${BATS_PATH%/*/*}
|
||||
export -f bats_readlinkf
|
||||
exec env BATS_ROOT="$BATS_ROOT" "$BATS_ROOT/libexec/bats-core/bats" "$@"
|
98
extras/portable/test/bats/lib/bats-core/common.bash
Normal file
98
extras/portable/test/bats/lib/bats-core/common.bash
Normal file
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
bats_prefix_lines_for_tap_output() {
|
||||
while IFS= read -r line; do
|
||||
printf '# %s\n' "$line" || break # avoid feedback loop when errors are redirected into BATS_OUT (see #353)
|
||||
done
|
||||
if [[ -n "$line" ]]; then
|
||||
printf '# %s\n' "$line"
|
||||
fi
|
||||
}
|
||||
|
||||
function bats_replace_filename() {
|
||||
local line
|
||||
while read -r line; do
|
||||
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
|
||||
done
|
||||
if [[ -n "$line" ]]; then
|
||||
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
|
||||
fi
|
||||
}
|
||||
|
||||
bats_quote_code() { # <var> <code>
|
||||
printf -v "$1" -- "%s%s%s" "$BATS_BEGIN_CODE_QUOTE" "$2" "$BATS_END_CODE_QUOTE"
|
||||
}
|
||||
|
||||
bats_check_valid_version() {
|
||||
if [[ ! $1 =~ [0-9]+.[0-9]+.[0-9]+ ]]; then
|
||||
printf "ERROR: version '%s' must be of format <major>.<minor>.<patch>!\n" "$1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# compares two versions. Return 0 when version1 < version2
|
||||
bats_version_lt() { # <version1> <version2>
|
||||
bats_check_valid_version "$1"
|
||||
bats_check_valid_version "$2"
|
||||
|
||||
local -a version1_parts version2_parts
|
||||
IFS=. read -ra version1_parts <<< "$1"
|
||||
IFS=. read -ra version2_parts <<< "$2"
|
||||
|
||||
for i in {0..2}; do
|
||||
if (( version1_parts[i] < version2_parts[i] )); then
|
||||
return 0
|
||||
elif (( version1_parts[i] > version2_parts[i] )); then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
# if we made it this far, they are equal -> also not less then
|
||||
return 2 # use other failing return code to distinguish equal from gt
|
||||
}
|
||||
|
||||
# ensure a minimum version of bats is running or exit with failure
|
||||
bats_require_minimum_version() { # <required version>
|
||||
local required_minimum_version=$1
|
||||
|
||||
if bats_version_lt "$BATS_VERSION" "$required_minimum_version"; then
|
||||
printf "BATS_VERSION=%s does not meet required minimum %s\n" "$BATS_VERSION" "$required_minimum_version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if bats_version_lt "$BATS_GUARANTEED_MINIMUM_VERSION" "$required_minimum_version"; then
|
||||
BATS_GUARANTEED_MINIMUM_VERSION="$required_minimum_version"
|
||||
fi
|
||||
}
|
||||
|
||||
bats_binary_search() { # <search-value> <array-name>
|
||||
if [[ $# -ne 2 ]]; then
|
||||
printf "ERROR: bats_binary_search requires exactly 2 arguments: <search value> <array name>\n" >&2
|
||||
return 2
|
||||
fi
|
||||
|
||||
local -r search_value=$1 array_name=$2
|
||||
|
||||
# we'd like to test if array is set but we cannot distinguish unset from empty arrays, so we need to skip that
|
||||
|
||||
local start=0 mid end mid_value
|
||||
# start is inclusive, end is exclusive ...
|
||||
eval "end=\${#${array_name}[@]}"
|
||||
|
||||
# so start == end means empty search space
|
||||
while (( start < end )); do
|
||||
mid=$(( (start + end) / 2 ))
|
||||
eval "mid_value=\${${array_name}[$mid]}"
|
||||
if [[ "$mid_value" == "$search_value" ]]; then
|
||||
return 0
|
||||
elif [[ "$mid_value" < "$search_value" ]]; then
|
||||
# This branch excludes equality -> +1 to skip the mid element.
|
||||
# This +1 also avoids endless recursion on odd sized search ranges.
|
||||
start=$((mid + 1))
|
||||
else
|
||||
end=$mid
|
||||
fi
|
||||
done
|
||||
|
||||
# did not find it -> its not there
|
||||
return 1
|
||||
}
|
116
extras/portable/test/bats/lib/bats-core/formatter.bash
Normal file
116
extras/portable/test/bats/lib/bats-core/formatter.bash
Normal file
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# reads (extended) bats tap streams from stdin and calls callback functions for each line
|
||||
# bats_tap_stream_plan <number of tests> -> when the test plan is encountered
|
||||
# bats_tap_stream_begin <test index> <test name> -> when a new test is begun WARNING: extended only
|
||||
# bats_tap_stream_ok [--duration <milliseconds] <test index> <test name> -> when a test was successful
|
||||
# bats_tap_stream_not_ok [--duration <milliseconds>] <test index> <test name> -> when a test has failed
|
||||
# bats_tap_stream_skipped <test index> <test name> <skip reason> -> when a test was skipped
|
||||
# bats_tap_stream_comment <comment text without leading '# '> <scope> -> when a comment line was encountered,
|
||||
# scope tells the last encountered of plan, begin, ok, not_ok, skipped, suite
|
||||
# bats_tap_stream_suite <file name> -> when a new file is begun WARNING: extended only
|
||||
# bats_tap_stream_unknown <full line> <scope> -> when a line is encountered that does not match the previous entries,
|
||||
# scope @see bats_tap_stream_comment
|
||||
# forwards all input as is, when there is no TAP test plan header
|
||||
function bats_parse_internal_extended_tap() {
|
||||
local header_pattern='[0-9]+\.\.[0-9]+'
|
||||
IFS= read -r header
|
||||
|
||||
if [[ "$header" =~ $header_pattern ]]; then
|
||||
bats_tap_stream_plan "${header:3}"
|
||||
else
|
||||
# If the first line isn't a TAP plan, print it and pass the rest through
|
||||
printf '%s\n' "$header"
|
||||
exec cat
|
||||
fi
|
||||
|
||||
ok_line_regexpr="ok ([0-9]+) (.*)"
|
||||
skip_line_regexpr="ok ([0-9]+) (.*) # skip( (.*))?$"
|
||||
not_ok_line_regexpr="not ok ([0-9]+) (.*)"
|
||||
|
||||
timing_expr="in ([0-9]+)ms$"
|
||||
local test_name begin_index ok_index not_ok_index index scope
|
||||
begin_index=0
|
||||
index=0
|
||||
scope=plan
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
'begin '*) # this might only be called in extended tap output
|
||||
((++begin_index))
|
||||
scope=begin
|
||||
test_name="${line#* "$begin_index" }"
|
||||
bats_tap_stream_begin "$begin_index" "$test_name"
|
||||
;;
|
||||
'ok '*)
|
||||
((++index))
|
||||
if [[ "$line" =~ $ok_line_regexpr ]]; then
|
||||
ok_index="${BASH_REMATCH[1]}"
|
||||
test_name="${BASH_REMATCH[2]}"
|
||||
if [[ "$line" =~ $skip_line_regexpr ]]; then
|
||||
scope=skipped
|
||||
test_name="${BASH_REMATCH[2]}" # cut off name before "# skip"
|
||||
local skip_reason="${BASH_REMATCH[4]}"
|
||||
bats_tap_stream_skipped "$ok_index" "$test_name" "$skip_reason"
|
||||
else
|
||||
scope=ok
|
||||
if [[ "$line" =~ $timing_expr ]]; then
|
||||
bats_tap_stream_ok --duration "${BASH_REMATCH[1]}" "$ok_index" "$test_name"
|
||||
else
|
||||
bats_tap_stream_ok "$ok_index" "$test_name"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
printf "ERROR: could not match ok line: %s" "$line" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
'not ok '*)
|
||||
((++index))
|
||||
scope=not_ok
|
||||
if [[ "$line" =~ $not_ok_line_regexpr ]]; then
|
||||
not_ok_index="${BASH_REMATCH[1]}"
|
||||
test_name="${BASH_REMATCH[2]}"
|
||||
if [[ "$line" =~ $timing_expr ]]; then
|
||||
bats_tap_stream_not_ok --duration "${BASH_REMATCH[1]}" "$not_ok_index" "$test_name"
|
||||
else
|
||||
bats_tap_stream_not_ok "$not_ok_index" "$test_name"
|
||||
fi
|
||||
else
|
||||
printf "ERROR: could not match not ok line: %s" "$line" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
'# '*)
|
||||
bats_tap_stream_comment "${line:2}" "$scope"
|
||||
;;
|
||||
'#')
|
||||
bats_tap_stream_comment "" "$scope"
|
||||
;;
|
||||
'suite '*)
|
||||
scope=suite
|
||||
# pass on the
|
||||
bats_tap_stream_suite "${line:6}"
|
||||
;;
|
||||
*)
|
||||
bats_tap_stream_unknown "$line" "$scope"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
normalize_base_path() { # <target variable> <base path>
|
||||
# the relative path root to use for reporting filenames
|
||||
# this is mainly intended for suite mode, where this will be the suite root folder
|
||||
local base_path="$2"
|
||||
# use the containing directory when --base-path is a file
|
||||
if [[ ! -d "$base_path" ]]; then
|
||||
base_path="$(dirname "$base_path")"
|
||||
fi
|
||||
# get the absolute path
|
||||
base_path="$(cd "$base_path" && pwd)"
|
||||
# ensure the path ends with / to strip that later on
|
||||
if [[ "${base_path}" != *"/" ]]; then
|
||||
base_path="$base_path/"
|
||||
fi
|
||||
printf -v "$1" "%s" "$base_path"
|
||||
}
|
22
extras/portable/test/bats/lib/bats-core/preprocessing.bash
Normal file
22
extras/portable/test/bats/lib/bats-core/preprocessing.bash
Normal file
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
BATS_TMPNAME="$BATS_RUN_TMPDIR/bats.$$"
|
||||
BATS_PARENT_TMPNAME="$BATS_RUN_TMPDIR/bats.$PPID"
|
||||
# shellcheck disable=SC2034
|
||||
BATS_OUT="${BATS_TMPNAME}.out" # used in bats-exec-file
|
||||
|
||||
bats_preprocess_source() {
|
||||
# export to make it visible to bats_evaluate_preprocessed_source
|
||||
# since the latter runs in bats-exec-test's bash while this runs in bats-exec-file's
|
||||
export BATS_TEST_SOURCE="${BATS_TMPNAME}.src"
|
||||
bats-preprocess "$BATS_TEST_FILENAME" >"$BATS_TEST_SOURCE"
|
||||
}
|
||||
|
||||
bats_evaluate_preprocessed_source() {
|
||||
if [[ -z "${BATS_TEST_SOURCE:-}" ]]; then
|
||||
BATS_TEST_SOURCE="${BATS_PARENT_TMPNAME}.src"
|
||||
fi
|
||||
# Dynamically loaded user files provided outside of Bats.
|
||||
# shellcheck disable=SC1090
|
||||
source "$BATS_TEST_SOURCE"
|
||||
}
|
107
extras/portable/test/bats/lib/bats-core/semaphore.bash
Normal file
107
extras/portable/test/bats/lib/bats-core/semaphore.bash
Normal file
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# setup the semaphore environment for the loading file
|
||||
bats_semaphore_setup() {
|
||||
export -f bats_semaphore_get_free_slot_count
|
||||
export -f bats_semaphore_acquire_while_locked
|
||||
export BATS_SEMAPHORE_DIR="$BATS_RUN_TMPDIR/semaphores"
|
||||
|
||||
if command -v flock >/dev/null; then
|
||||
bats_run_under_lock() {
|
||||
flock "$BATS_SEMAPHORE_DIR" "$@"
|
||||
}
|
||||
elif command -v shlock >/dev/null; then
|
||||
bats_run_under_lock() {
|
||||
local lockfile="$BATS_SEMAPHORE_DIR/shlock.lock"
|
||||
while ! shlock -p $$ -f "$lockfile"; do
|
||||
sleep 1
|
||||
done
|
||||
# we got the lock now, execute the command
|
||||
"$@"
|
||||
local status=$?
|
||||
# free the lock
|
||||
rm -f "$lockfile"
|
||||
return $status
|
||||
}
|
||||
else
|
||||
printf "ERROR: flock/shlock is required for parallelization within files!\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# $1 - output directory for stdout/stderr
|
||||
# $@ - command to run
|
||||
# run the given command in a semaphore
|
||||
# block when there is no free slot for the semaphore
|
||||
# when there is a free slot, run the command in background
|
||||
# gather the output of the command in files in the given directory
|
||||
bats_semaphore_run() {
|
||||
local output_dir=$1
|
||||
shift
|
||||
local semaphore_slot
|
||||
semaphore_slot=$(bats_semaphore_acquire_slot)
|
||||
bats_semaphore_release_wrapper "$output_dir" "$semaphore_slot" "$@" &
|
||||
printf "%d\n" "$!"
|
||||
}
|
||||
|
||||
# $1 - output directory for stdout/stderr
|
||||
# $@ - command to run
|
||||
# this wraps the actual function call to install some traps on exiting
|
||||
bats_semaphore_release_wrapper() {
|
||||
local output_dir="$1"
|
||||
local semaphore_name="$2"
|
||||
shift 2 # all other parameters will be use for the command to execute
|
||||
|
||||
# shellcheck disable=SC2064 # we want to expand the semaphore_name right now!
|
||||
trap "status=$?; bats_semaphore_release_slot '$semaphore_name'; exit $status" EXIT
|
||||
|
||||
mkdir -p "$output_dir"
|
||||
"$@" 2>"$output_dir/stderr" >"$output_dir/stdout"
|
||||
local status=$?
|
||||
|
||||
# bash bug: the exit trap is not called for the background process
|
||||
bats_semaphore_release_slot "$semaphore_name"
|
||||
trap - EXIT # avoid calling release twice
|
||||
return $status
|
||||
}
|
||||
|
||||
bats_semaphore_acquire_while_locked() {
|
||||
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
|
||||
local slot=0
|
||||
while [[ -e "$BATS_SEMAPHORE_DIR/slot-$slot" ]]; do
|
||||
(( ++slot ))
|
||||
done
|
||||
if [[ $slot -lt $BATS_SEMAPHORE_NUMBER_OF_SLOTS ]]; then
|
||||
touch "$BATS_SEMAPHORE_DIR/slot-$slot" && printf "%d\n" "$slot" && return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# block until a semaphore slot becomes free
|
||||
# prints the number of the slot that it received
|
||||
bats_semaphore_acquire_slot() {
|
||||
mkdir -p "$BATS_SEMAPHORE_DIR"
|
||||
# wait for a slot to become free
|
||||
# TODO: avoid busy waiting by using signals -> this opens op prioritizing possibilities as well
|
||||
while true; do
|
||||
# don't lock for reading, we are fine with spuriously getting no free slot
|
||||
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
|
||||
bats_run_under_lock bash -c bats_semaphore_acquire_while_locked && break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
bats_semaphore_release_slot() {
|
||||
# we don't need to lock this, since only our process owns this file
|
||||
# and freeing a semaphore cannot lead to conflicts with others
|
||||
rm "$BATS_SEMAPHORE_DIR/slot-$1" # this will fail if we had not acquired a semaphore!
|
||||
}
|
||||
|
||||
bats_semaphore_get_free_slot_count() {
|
||||
# find might error out without returning something useful when a file is deleted,
|
||||
# while the directory is traversed -> only continue when there was no error
|
||||
until used_slots=$(find "$BATS_SEMAPHORE_DIR" -name 'slot-*' 2>/dev/null | wc -l); do :; done
|
||||
echo $(( BATS_SEMAPHORE_NUMBER_OF_SLOTS - used_slots ))
|
||||
}
|
357
extras/portable/test/bats/lib/bats-core/test_functions.bash
Normal file
357
extras/portable/test/bats/lib/bats-core/test_functions.bash
Normal file
|
@ -0,0 +1,357 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
BATS_TEST_DIRNAME="${BATS_TEST_FILENAME%/*}"
|
||||
BATS_TEST_NAMES=()
|
||||
|
||||
# shellcheck source=lib/bats-core/warnings.bash
|
||||
source "$BATS_ROOT/lib/bats-core/warnings.bash"
|
||||
|
||||
# find_in_bats_lib_path echoes the first recognized load path to
|
||||
# a library in BATS_LIB_PATH or relative to BATS_TEST_DIRNAME.
|
||||
#
|
||||
# Libraries relative to BATS_TEST_DIRNAME take precedence over
|
||||
# BATS_LIB_PATH.
|
||||
#
|
||||
# Library load paths are recognized using find_library_load_path.
|
||||
#
|
||||
# If no library is found find_in_bats_lib_path returns 1.
|
||||
find_in_bats_lib_path() { # <return-var> <library-name>
|
||||
local return_var="${1:?}"
|
||||
local library_name="${2:?}"
|
||||
|
||||
local -a bats_lib_paths
|
||||
IFS=: read -ra bats_lib_paths <<< "$BATS_LIB_PATH"
|
||||
|
||||
for path in "${bats_lib_paths[@]}"; do
|
||||
if [[ -f "$path/$library_name" ]]; then
|
||||
printf -v "$return_var" "%s" "$path/$library_name"
|
||||
# A library load path was found, return
|
||||
return 0
|
||||
elif [[ -f "$path/$library_name/load.bash" ]]; then
|
||||
printf -v "$return_var" "%s" "$path/$library_name/load.bash"
|
||||
# A library load path was found, return
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# bats_internal_load expects an absolute path that is a library load path.
|
||||
#
|
||||
# If the library load path points to a file (a library loader) it is
|
||||
# sourced.
|
||||
#
|
||||
# If it points to a directory all files ending in .bash inside of the
|
||||
# directory are sourced.
|
||||
#
|
||||
# If the sourcing of the library loader or of a file in a library
|
||||
# directory fails bats_internal_load prints an error message and returns 1.
|
||||
#
|
||||
# If the passed library load path is not absolute or is not a valid file
|
||||
# or directory bats_internal_load prints an error message and returns 1.
|
||||
bats_internal_load() {
|
||||
local library_load_path="${1:?}"
|
||||
|
||||
if [[ "${library_load_path:0:1}" != / ]]; then
|
||||
printf "Passed library load path is not an absolute path: %s\n" "$library_load_path" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# library_load_path is a library loader
|
||||
if [[ -f "$library_load_path" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
if ! source "$library_load_path"; then
|
||||
printf "Error while sourcing library loader at '%s'\n" "$library_load_path" >&2
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf "Passed library load path is neither a library loader nor library directory: %s\n" "$library_load_path" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# bats_load_safe accepts an argument called 'slug' and attempts to find and
|
||||
# source a library based on the slug.
|
||||
#
|
||||
# A slug can be an absolute path, a library name or a relative path.
|
||||
#
|
||||
# If the slug is an absolute path bats_load_safe attempts to find the library
|
||||
# load path using find_library_load_path.
|
||||
# What is considered a library load path is documented in the
|
||||
# documentation for find_library_load_path.
|
||||
#
|
||||
# If the slug is not an absolute path it is considered a library name or
|
||||
# relative path. bats_load_safe attempts to find the library load path using
|
||||
# find_in_bats_lib_path.
|
||||
#
|
||||
# If bats_load_safe can find a library load path it is passed to bats_internal_load.
|
||||
# If bats_internal_load fails bats_load_safe returns 1.
|
||||
#
|
||||
# If no library load path can be found bats_load_safe prints an error message
|
||||
# and returns 1.
|
||||
bats_load_safe() {
|
||||
local slug="${1:?}"
|
||||
if [[ ${slug:0:1} != / ]]; then # relative paths are relative to BATS_TEST_DIRNAME
|
||||
slug="$BATS_TEST_DIRNAME/$slug"
|
||||
fi
|
||||
|
||||
if [[ -f "$slug.bash" ]]; then
|
||||
bats_internal_load "$slug.bash"
|
||||
return $?
|
||||
elif [[ -f "$slug" ]]; then
|
||||
bats_internal_load "$slug"
|
||||
return $?
|
||||
fi
|
||||
|
||||
# loading from PATH (retained for backwards compatibility)
|
||||
if [[ ! -f "$1" ]] && type -P "$1" >/dev/null; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$1"
|
||||
return $?
|
||||
fi
|
||||
|
||||
# No library load path can be found
|
||||
printf "bats_load_safe: Could not find '%s'[.bash]\n" "$slug" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
bats_require_lib_path() {
|
||||
if [[ -z "${BATS_LIB_PATH:-}" ]]; then
|
||||
printf "%s: requires BATS_LIB_PATH to be set!\n" "${FUNCNAME[1]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
bats_load_library_safe() { # <slug>
|
||||
local slug="${1:?}" library_path
|
||||
|
||||
bats_require_lib_path
|
||||
|
||||
# Check for library load paths in BATS_TEST_DIRNAME and BATS_LIB_PATH
|
||||
if [[ ${slug:0:1} != / ]]; then
|
||||
find_in_bats_lib_path library_path "$slug"
|
||||
if [[ -z "$library_path" ]]; then
|
||||
printf "Could not find library '%s' relative to test file or in BATS_LIB_PATH\n" "$slug" >&2
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# absolute paths are taken as is
|
||||
library_path="$slug"
|
||||
if [[ ! -f "$library_path" ]]; then
|
||||
printf "Could not find library on absolute path '%s'\n" "$library_path" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
bats_internal_load "$library_path"
|
||||
return $?
|
||||
}
|
||||
|
||||
# immediately exit on error, use bats_load_library_safe to catch and handle errors
|
||||
bats_load_library() { # <slug>
|
||||
bats_require_lib_path
|
||||
if ! bats_load_library_safe "$@"; then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# load acts like bats_load_safe but exits the shell instead of returning 1.
|
||||
load() {
|
||||
if ! bats_load_safe "$@"; then
|
||||
echo "${FUNCNAME[0]} $LINENO" >&3
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
bats_redirect_stderr_into_file() {
|
||||
"$@" 2>>"$bats_run_separate_stderr_file" # use >> to see collisions' content
|
||||
}
|
||||
|
||||
bats_merge_stdout_and_stderr() {
|
||||
"$@" 2>&1
|
||||
}
|
||||
|
||||
# write separate lines from <input-var> into <output-array>
|
||||
bats_separate_lines() { # <output-array> <input-var>
|
||||
local output_array_name="$1"
|
||||
local input_var_name="$2"
|
||||
if [[ $keep_empty_lines ]]; then
|
||||
local bats_separate_lines_lines=()
|
||||
if [[ -n "${!input_var_name}" ]]; then # avoid getting an empty line for empty input
|
||||
while IFS= read -r line; do
|
||||
bats_separate_lines_lines+=("$line")
|
||||
done <<<"${!input_var_name}"
|
||||
fi
|
||||
eval "${output_array_name}=(\"\${bats_separate_lines_lines[@]}\")"
|
||||
else
|
||||
# shellcheck disable=SC2034,SC2206
|
||||
IFS=$'\n' read -d '' -r -a "$output_array_name" <<<"${!input_var_name}" || true # don't fail due to EOF
|
||||
fi
|
||||
}
|
||||
|
||||
run() { # [!|-N] [--keep-empty-lines] [--separate-stderr] [--] <command to run...>
|
||||
# This has to be restored on exit from this function to avoid leaking our trap INT into surrounding code.
|
||||
# Non zero exits won't restore under the assumption that they will fail the test before it can be aborted,
|
||||
# which allows us to avoid duplicating the restore code on every exit path
|
||||
trap bats_interrupt_trap_in_run INT
|
||||
local expected_rc=
|
||||
local keep_empty_lines=
|
||||
local output_case=merged
|
||||
local has_flags=
|
||||
# parse options starting with -
|
||||
while [[ $# -gt 0 ]] && [[ $1 == -* || $1 == '!' ]]; do
|
||||
has_flags=1
|
||||
case "$1" in
|
||||
'!')
|
||||
expected_rc=-1
|
||||
;;
|
||||
-[0-9]*)
|
||||
expected_rc=${1#-}
|
||||
if [[ $expected_rc =~ [^0-9] ]]; then
|
||||
printf "Usage error: run: '-NNN' requires numeric NNN (got: %s)\n" "$expected_rc" >&2
|
||||
return 1
|
||||
elif [[ $expected_rc -gt 255 ]]; then
|
||||
printf "Usage error: run: '-NNN': NNN must be <= 255 (got: %d)\n" "$expected_rc" >&2
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
--keep-empty-lines)
|
||||
keep_empty_lines=1
|
||||
;;
|
||||
--separate-stderr)
|
||||
output_case="separate"
|
||||
;;
|
||||
--)
|
||||
shift # eat the -- before breaking away
|
||||
break
|
||||
;;
|
||||
*)
|
||||
printf "Usage error: unknown flag '%s'" "$1" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ -n $has_flags ]]; then
|
||||
bats_warn_minimum_guaranteed_version "Using flags on \`run\`" 1.5.0
|
||||
fi
|
||||
|
||||
local pre_command=
|
||||
|
||||
case "$output_case" in
|
||||
merged) # redirects stderr into stdout and fills only $output/$lines
|
||||
pre_command=bats_merge_stdout_and_stderr
|
||||
;;
|
||||
separate) # splits stderr into own file and fills $stderr/$stderr_lines too
|
||||
local bats_run_separate_stderr_file
|
||||
bats_run_separate_stderr_file="$(mktemp "${BATS_TEST_TMPDIR}/separate-stderr-XXXXXX")"
|
||||
pre_command=bats_redirect_stderr_into_file
|
||||
;;
|
||||
esac
|
||||
|
||||
local origFlags="$-"
|
||||
set +eET
|
||||
local origIFS="$IFS"
|
||||
if [[ $keep_empty_lines ]]; then
|
||||
# 'output', 'status', 'lines' are global variables available to tests.
|
||||
# preserve trailing newlines by appending . and removing it later
|
||||
# shellcheck disable=SC2034
|
||||
output="$($pre_command "$@"; status=$?; printf .; exit $status)" && status=0 || status=$?
|
||||
output="${output%.}"
|
||||
else
|
||||
# 'output', 'status', 'lines' are global variables available to tests.
|
||||
# shellcheck disable=SC2034
|
||||
output="$($pre_command "$@")" && status=0 || status=$?
|
||||
fi
|
||||
|
||||
bats_separate_lines lines output
|
||||
|
||||
if [[ "$output_case" == separate ]]; then
|
||||
# shellcheck disable=SC2034
|
||||
read -d '' -r stderr < "$bats_run_separate_stderr_file"
|
||||
bats_separate_lines stderr_lines stderr
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
BATS_RUN_COMMAND="${*}"
|
||||
IFS="$origIFS"
|
||||
set "-$origFlags"
|
||||
|
||||
if [[ ${BATS_VERBOSE_RUN:-} ]]; then
|
||||
printf "%s\n" "$output"
|
||||
fi
|
||||
|
||||
|
||||
if [[ -n "$expected_rc" ]]; then
|
||||
if [[ "$expected_rc" = "-1" ]]; then
|
||||
if [[ "$status" -eq 0 ]]; then
|
||||
BATS_ERROR_SUFFIX=", expected nonzero exit code!"
|
||||
return 1
|
||||
fi
|
||||
elif [ "$status" -ne "$expected_rc" ]; then
|
||||
# shellcheck disable=SC2034
|
||||
BATS_ERROR_SUFFIX=", expected exit code $expected_rc, got $status"
|
||||
return 1
|
||||
fi
|
||||
elif [[ "$status" -eq 127 ]]; then # "command not found"
|
||||
bats_generate_warning 1 "$BATS_RUN_COMMAND"
|
||||
fi
|
||||
# don't leak our trap into surrounding code
|
||||
trap bats_interrupt_trap INT
|
||||
}
|
||||
|
||||
setup() {
|
||||
return 0
|
||||
}
|
||||
|
||||
teardown() {
|
||||
return 0
|
||||
}
|
||||
|
||||
skip() {
|
||||
# if this is a skip in teardown ...
|
||||
if [[ -n "${BATS_TEARDOWN_STARTED-}" ]]; then
|
||||
# ... we want to skip the rest of teardown.
|
||||
# communicate to bats_exit_trap that the teardown was completed without error
|
||||
# shellcheck disable=SC2034
|
||||
BATS_TEARDOWN_COMPLETED=1
|
||||
# if we are already in the exit trap (e.g. due to previous skip) ...
|
||||
if [[ "$BATS_TEARDOWN_STARTED" == as-exit-trap ]]; then
|
||||
# ... we need to do the rest of the tear_down_trap that would otherwise be skipped after the next call to exit
|
||||
bats_exit_trap
|
||||
# and then do the exit (at the end of this function)
|
||||
fi
|
||||
# if we aren't in exit trap, the normal exit handling should suffice
|
||||
else
|
||||
# ... this is either skip in test or skip in setup.
|
||||
# Following variables are used in bats-exec-test which sources this file
|
||||
# shellcheck disable=SC2034
|
||||
BATS_TEST_SKIPPED="${1:-1}"
|
||||
# shellcheck disable=SC2034
|
||||
BATS_TEST_COMPLETED=1
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
bats_test_begin() {
|
||||
BATS_TEST_DESCRIPTION="$1"
|
||||
if [[ -n "$BATS_EXTENDED_SYNTAX" ]]; then
|
||||
printf 'begin %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}$BATS_TEST_DESCRIPTION" >&3
|
||||
fi
|
||||
setup
|
||||
}
|
||||
|
||||
bats_test_function() {
|
||||
local test_name="$1"
|
||||
BATS_TEST_NAMES+=("$test_name")
|
||||
}
|
||||
|
||||
# decides whether a failed test should be run again
|
||||
bats_should_retry_test() {
|
||||
# test try number starts at 1
|
||||
# 0 retries means run only first try
|
||||
(( BATS_TEST_TRY_NUMBER <= BATS_TEST_RETRIES ))
|
||||
}
|
386
extras/portable/test/bats/lib/bats-core/tracing.bash
Normal file
386
extras/portable/test/bats/lib/bats-core/tracing.bash
Normal file
|
@ -0,0 +1,386 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# shellcheck source=lib/bats-core/common.bash
|
||||
source "$BATS_ROOT/lib/bats-core/common.bash"
|
||||
|
||||
bats_capture_stack_trace() {
|
||||
local test_file
|
||||
local funcname
|
||||
local i
|
||||
|
||||
BATS_DEBUG_LAST_STACK_TRACE=()
|
||||
|
||||
for ((i = 2; i != ${#FUNCNAME[@]}; ++i)); do
|
||||
# Use BATS_TEST_SOURCE if necessary to work around Bash < 4.4 bug whereby
|
||||
# calling an exported function erases the test file's BASH_SOURCE entry.
|
||||
test_file="${BASH_SOURCE[$i]:-$BATS_TEST_SOURCE}"
|
||||
funcname="${FUNCNAME[$i]}"
|
||||
BATS_DEBUG_LAST_STACK_TRACE+=("${BASH_LINENO[$((i-1))]} $funcname $test_file")
|
||||
case "$funcname" in
|
||||
"$BATS_TEST_NAME" | setup | teardown | setup_file | teardown_file | setup_suite | teardown_suite)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
if [[ "${BASH_SOURCE[$i + 1]:-}" == *"bats-exec-file" ]] && [[ "$funcname" == 'source' ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
bats_get_failure_stack_trace() {
|
||||
local stack_trace_var
|
||||
# See bats_debug_trap for details.
|
||||
if [[ -n "${BATS_DEBUG_LAST_STACK_TRACE_IS_VALID:-}" ]]; then
|
||||
stack_trace_var=BATS_DEBUG_LAST_STACK_TRACE
|
||||
else
|
||||
stack_trace_var=BATS_DEBUG_LASTLAST_STACK_TRACE
|
||||
fi
|
||||
# shellcheck disable=SC2016
|
||||
eval "$(printf \
|
||||
'%s=(${%s[@]+"${%s[@]}"})' \
|
||||
"${1}" \
|
||||
"${stack_trace_var}" \
|
||||
"${stack_trace_var}")"
|
||||
}
|
||||
|
||||
bats_print_stack_trace() {
|
||||
local frame
|
||||
local index=1
|
||||
local count="${#@}"
|
||||
local filename
|
||||
local lineno
|
||||
|
||||
for frame in "$@"; do
|
||||
bats_frame_filename "$frame" 'filename'
|
||||
bats_trim_filename "$filename" 'filename'
|
||||
bats_frame_lineno "$frame" 'lineno'
|
||||
|
||||
printf '%s' "${BATS_STACK_TRACE_PREFIX-# }"
|
||||
if [[ $index -eq 1 ]]; then
|
||||
printf '('
|
||||
else
|
||||
printf ' '
|
||||
fi
|
||||
|
||||
local fn
|
||||
bats_frame_function "$frame" 'fn'
|
||||
if [[ "$fn" != "$BATS_TEST_NAME" ]] &&
|
||||
# don't print "from function `source'"",
|
||||
# when failing in free code during `source $test_file` from bats-exec-file
|
||||
! [[ "$fn" == 'source' && $index -eq $count ]]; then
|
||||
local quoted_fn
|
||||
bats_quote_code quoted_fn "$fn"
|
||||
printf "from function %s " "$quoted_fn"
|
||||
fi
|
||||
|
||||
if [[ $index -eq $count ]]; then
|
||||
printf 'in test file %s, line %d)\n' "$filename" "$lineno"
|
||||
else
|
||||
printf 'in file %s, line %d,\n' "$filename" "$lineno"
|
||||
fi
|
||||
|
||||
((++index))
|
||||
done
|
||||
}
|
||||
|
||||
bats_print_failed_command() {
|
||||
local stack_trace=("${@}")
|
||||
if [[ ${#stack_trace[@]} -eq 0 ]]; then
|
||||
return
|
||||
fi
|
||||
local frame="${stack_trace[${#stack_trace[@]} - 1]}"
|
||||
local filename
|
||||
local lineno
|
||||
local failed_line
|
||||
local failed_command
|
||||
|
||||
bats_frame_filename "$frame" 'filename'
|
||||
bats_frame_lineno "$frame" 'lineno'
|
||||
bats_extract_line "$filename" "$lineno" 'failed_line'
|
||||
bats_strip_string "$failed_line" 'failed_command'
|
||||
local quoted_failed_command
|
||||
bats_quote_code quoted_failed_command "$failed_command"
|
||||
printf '# %s ' "${quoted_failed_command}"
|
||||
|
||||
if [[ "$BATS_ERROR_STATUS" -eq 1 ]]; then
|
||||
printf 'failed%s\n' "$BATS_ERROR_SUFFIX"
|
||||
else
|
||||
printf 'failed with status %d%s\n' "$BATS_ERROR_STATUS" "$BATS_ERROR_SUFFIX"
|
||||
fi
|
||||
}
|
||||
|
||||
bats_frame_lineno() {
|
||||
printf -v "$2" '%s' "${1%% *}"
|
||||
}
|
||||
|
||||
bats_frame_function() {
|
||||
local __bff_function="${1#* }"
|
||||
printf -v "$2" '%s' "${__bff_function%% *}"
|
||||
}
|
||||
|
||||
bats_frame_filename() {
|
||||
local __bff_filename="${1#* }"
|
||||
__bff_filename="${__bff_filename#* }"
|
||||
|
||||
if [[ "$__bff_filename" == "$BATS_TEST_SOURCE" ]]; then
|
||||
__bff_filename="$BATS_TEST_FILENAME"
|
||||
fi
|
||||
printf -v "$2" '%s' "$__bff_filename"
|
||||
}
|
||||
|
||||
bats_extract_line() {
|
||||
local __bats_extract_line_line
|
||||
local __bats_extract_line_index=0
|
||||
|
||||
while IFS= read -r __bats_extract_line_line; do
|
||||
if [[ "$((++__bats_extract_line_index))" -eq "$2" ]]; then
|
||||
printf -v "$3" '%s' "${__bats_extract_line_line%$'\r'}"
|
||||
break
|
||||
fi
|
||||
done <"$1"
|
||||
}
|
||||
|
||||
bats_strip_string() {
|
||||
[[ "$1" =~ ^[[:space:]]*(.*)[[:space:]]*$ ]]
|
||||
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
|
||||
}
|
||||
|
||||
bats_trim_filename() {
|
||||
printf -v "$2" '%s' "${1#"$BATS_CWD"/}"
|
||||
}
|
||||
|
||||
# normalize a windows path from e.g. C:/directory to /c/directory
|
||||
# The path must point to an existing/accessable directory, not a file!
|
||||
bats_normalize_windows_dir_path() { # <output-var> <path>
|
||||
local output_var="$1" path="$2"
|
||||
if [[ "$output_var" != NORMALIZED_INPUT ]]; then
|
||||
local NORMALIZED_INPUT
|
||||
fi
|
||||
if [[ $path == ?:* ]]; then
|
||||
NORMALIZED_INPUT="$(cd "$path" || exit 1; pwd)"
|
||||
else
|
||||
NORMALIZED_INPUT="$path"
|
||||
fi
|
||||
printf -v "$output_var" "%s" "$NORMALIZED_INPUT"
|
||||
}
|
||||
|
||||
bats_emit_trace() {
|
||||
if [[ $BATS_TRACE_LEVEL -gt 0 ]]; then
|
||||
local line=${BASH_LINENO[1]}
|
||||
# shellcheck disable=SC2016
|
||||
if [[ $BASH_COMMAND != '"$BATS_TEST_NAME" >> "$BATS_OUT" 2>&1 4>&1' && $BASH_COMMAND != "bats_test_begin "* ]] && # don't emit these internal calls
|
||||
[[ $BASH_COMMAND != "$BATS_LAST_BASH_COMMAND" || $line != "$BATS_LAST_BASH_LINENO" ]] &&
|
||||
# avoid printing a function twice (at call site and at definition site)
|
||||
[[ $BASH_COMMAND != "$BATS_LAST_BASH_COMMAND" || ${BASH_LINENO[2]} != "$BATS_LAST_BASH_LINENO" || ${BASH_SOURCE[3]} != "$BATS_LAST_BASH_SOURCE" ]]; then
|
||||
local file="${BASH_SOURCE[2]}" # index 2: skip over bats_emit_trace and bats_debug_trap
|
||||
if [[ $file == "${BATS_TEST_SOURCE}" ]]; then
|
||||
file="$BATS_TEST_FILENAME"
|
||||
fi
|
||||
local padding='$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$'
|
||||
if (( BATS_LAST_STACK_DEPTH != ${#BASH_LINENO[@]} )); then
|
||||
printf '%s [%s:%d]\n' "${padding::${#BASH_LINENO[@]}-4}" "${file##*/}" "$line" >&4
|
||||
fi
|
||||
printf '%s %s\n' "${padding::${#BASH_LINENO[@]}-4}" "$BASH_COMMAND" >&4
|
||||
BATS_LAST_BASH_COMMAND="$BASH_COMMAND"
|
||||
BATS_LAST_BASH_LINENO="$line"
|
||||
BATS_LAST_BASH_SOURCE="${BASH_SOURCE[2]}"
|
||||
BATS_LAST_STACK_DEPTH="${#BASH_LINENO[@]}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# bats_debug_trap tracks the last line of code executed within a test. This is
|
||||
# necessary because $BASH_LINENO is often incorrect inside of ERR and EXIT
|
||||
# trap handlers.
|
||||
#
|
||||
# Below are tables describing different command failure scenarios and the
|
||||
# reliability of $BASH_LINENO within different the executed DEBUG, ERR, and EXIT
|
||||
# trap handlers. Naturally, the behaviors change between versions of Bash.
|
||||
#
|
||||
# Table rows should be read left to right. For example, on bash version
|
||||
# 4.0.44(2)-release, if a test executes `false` (or any other failing external
|
||||
# command), bash will do the following in order:
|
||||
# 1. Call the DEBUG trap handler (bats_debug_trap) with $BASH_LINENO referring
|
||||
# to the source line containing the `false` command, then
|
||||
# 2. Call the DEBUG trap handler again, but with an incorrect $BASH_LINENO, then
|
||||
# 3. Call the ERR trap handler, but with a (possibly-different) incorrect
|
||||
# $BASH_LINENO, then
|
||||
# 4. Call the DEBUG trap handler again, but with $BASH_LINENO set to 1, then
|
||||
# 5. Call the EXIT trap handler, with $BASH_LINENO set to 1.
|
||||
#
|
||||
# bash version 4.4.20(1)-release
|
||||
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
|
||||
# -------------+-------------+--------------+---------+-------------+--------
|
||||
# false | OK | OK | OK | BAD[1] | BAD[1]
|
||||
# [[ 1 = 2 ]] | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
|
||||
# (( 1 = 2 )) | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
|
||||
# ! true | OK | --- | BAD[4] | --- | BAD[1]
|
||||
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
|
||||
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
|
||||
#
|
||||
# bash version 4.0.44(2)-release
|
||||
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
|
||||
# -------------+-------------+--------------+---------+-------------+--------
|
||||
# false | OK | BAD[3] | BAD[3] | BAD[1] | BAD[1]
|
||||
# [[ 1 = 2 ]] | OK | --- | BAD[3] | --- | BAD[1]
|
||||
# (( 1 = 2 )) | OK | --- | BAD[3] | --- | BAD[1]
|
||||
# ! true | OK | --- | BAD[3] | --- | BAD[1]
|
||||
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
|
||||
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
|
||||
#
|
||||
# [1] The reported line number is always 1.
|
||||
# [2] The reported source location is that of the beginning of the function
|
||||
# calling the command.
|
||||
# [3] The reported line is that of the last command executed in the DEBUG trap
|
||||
# handler.
|
||||
# [4] The reported source location is that of the call to the function calling
|
||||
# the command.
|
||||
bats_debug_trap() {
|
||||
# on windows we sometimes get a mix of paths (when install via nmp install -g)
|
||||
# which have C:/... or /c/... comparing them is going to be problematic.
|
||||
# We need to normalize them to a common format!
|
||||
local NORMALIZED_INPUT
|
||||
bats_normalize_windows_dir_path NORMALIZED_INPUT "${1%/*}"
|
||||
local file_excluded='' path
|
||||
for path in "${BATS_DEBUG_EXCLUDE_PATHS[@]}"; do
|
||||
if [[ "$NORMALIZED_INPUT" == "$path"* ]]; then
|
||||
file_excluded=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# don't update the trace within library functions or we get backtraces from inside traps
|
||||
# also don't record new stack traces while handling interruptions, to avoid overriding the interrupted command
|
||||
if [[ -z "$file_excluded" && "${BATS_INTERRUPTED-NOTSET}" == NOTSET ]]; then
|
||||
BATS_DEBUG_LASTLAST_STACK_TRACE=(
|
||||
${BATS_DEBUG_LAST_STACK_TRACE[@]+"${BATS_DEBUG_LAST_STACK_TRACE[@]}"}
|
||||
)
|
||||
|
||||
BATS_DEBUG_LAST_LINENO=(${BASH_LINENO[@]+"${BASH_LINENO[@]}"})
|
||||
BATS_DEBUG_LAST_SOURCE=(${BASH_SOURCE[@]+"${BASH_SOURCE[@]}"})
|
||||
bats_capture_stack_trace
|
||||
bats_emit_trace
|
||||
fi
|
||||
}
|
||||
|
||||
# For some versions of Bash, the `ERR` trap may not always fire for every
|
||||
# command failure, but the `EXIT` trap will. Also, some command failures may not
|
||||
# set `$?` properly. See #72 and #81 for details.
|
||||
#
|
||||
# For this reason, we call `bats_check_status_from_trap` at the very beginning
|
||||
# of `bats_teardown_trap` and check the value of `$BATS_TEST_COMPLETED` before
|
||||
# taking other actions. We also adjust the exit status value if needed.
|
||||
#
|
||||
# See `bats_exit_trap` for an additional EXIT error handling case when `$?`
|
||||
# isn't set properly during `teardown()` errors.
|
||||
bats_check_status_from_trap() {
|
||||
local status="$?"
|
||||
if [[ -z "${BATS_TEST_COMPLETED:-}" ]]; then
|
||||
BATS_ERROR_STATUS="${BATS_ERROR_STATUS:-$status}"
|
||||
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
|
||||
BATS_ERROR_STATUS=1
|
||||
fi
|
||||
trap - DEBUG
|
||||
fi
|
||||
}
|
||||
|
||||
bats_add_debug_exclude_path() { # <path>
|
||||
if [[ -z "$1" ]]; then # don't exclude everything
|
||||
printf "bats_add_debug_exclude_path: Exclude path must not be empty!\n" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$OSTYPE" == cygwin || "$OSTYPE" == msys ]]; then
|
||||
local normalized_dir
|
||||
bats_normalize_windows_dir_path normalized_dir "$1"
|
||||
BATS_DEBUG_EXCLUDE_PATHS+=("$normalized_dir")
|
||||
else
|
||||
BATS_DEBUG_EXCLUDE_PATHS+=("$1")
|
||||
fi
|
||||
}
|
||||
|
||||
bats_setup_tracing() {
|
||||
# Variables for capturing accurate stack traces. See bats_debug_trap for
|
||||
# details.
|
||||
#
|
||||
# BATS_DEBUG_LAST_LINENO, BATS_DEBUG_LAST_SOURCE, and
|
||||
# BATS_DEBUG_LAST_STACK_TRACE hold data from the most recent call to
|
||||
# bats_debug_trap.
|
||||
#
|
||||
# BATS_DEBUG_LASTLAST_STACK_TRACE holds data from two bats_debug_trap calls
|
||||
# ago.
|
||||
#
|
||||
# BATS_DEBUG_LAST_STACK_TRACE_IS_VALID indicates that
|
||||
# BATS_DEBUG_LAST_STACK_TRACE contains the stack trace of the test's error. If
|
||||
# unset, BATS_DEBUG_LAST_STACK_TRACE is unreliable and
|
||||
# BATS_DEBUG_LASTLAST_STACK_TRACE should be used instead.
|
||||
BATS_DEBUG_LASTLAST_STACK_TRACE=()
|
||||
BATS_DEBUG_LAST_LINENO=()
|
||||
BATS_DEBUG_LAST_SOURCE=()
|
||||
BATS_DEBUG_LAST_STACK_TRACE=()
|
||||
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=
|
||||
BATS_ERROR_SUFFIX=
|
||||
BATS_DEBUG_EXCLUDE_PATHS=()
|
||||
# exclude some paths by default
|
||||
bats_add_debug_exclude_path "$BATS_ROOT/lib/"
|
||||
bats_add_debug_exclude_path "$BATS_ROOT/libexec/"
|
||||
|
||||
|
||||
exec 4<&1 # used for tracing
|
||||
if [[ "${BATS_TRACE_LEVEL:-0}" -gt 0 ]]; then
|
||||
# avoid undefined variable errors
|
||||
BATS_LAST_BASH_COMMAND=
|
||||
BATS_LAST_BASH_LINENO=
|
||||
BATS_LAST_BASH_SOURCE=
|
||||
BATS_LAST_STACK_DEPTH=
|
||||
# try to exclude helper libraries if found, this is only relevant for tracing
|
||||
while read -r path; do
|
||||
bats_add_debug_exclude_path "$path"
|
||||
done < <(find "$PWD" -type d -name bats-assert -o -name bats-support)
|
||||
fi
|
||||
|
||||
local exclude_paths path
|
||||
# exclude user defined libraries
|
||||
IFS=':' read -r exclude_paths <<< "${BATS_DEBUG_EXCLUDE_PATHS:-}"
|
||||
for path in "${exclude_paths[@]}"; do
|
||||
if [[ -n "$path" ]]; then
|
||||
bats_add_debug_exclude_path "$path"
|
||||
fi
|
||||
done
|
||||
|
||||
# turn on traps after setting excludes to avoid tracing the exclude setup
|
||||
trap 'bats_debug_trap "$BASH_SOURCE"' DEBUG
|
||||
trap 'bats_error_trap' ERR
|
||||
}
|
||||
|
||||
bats_error_trap() {
|
||||
bats_check_status_from_trap
|
||||
|
||||
# If necessary, undo the most recent stack trace captured by bats_debug_trap.
|
||||
# See bats_debug_trap for details.
|
||||
if [[ "${BASH_LINENO[*]}" = "${BATS_DEBUG_LAST_LINENO[*]:-}"
|
||||
&& "${BASH_SOURCE[*]}" = "${BATS_DEBUG_LAST_SOURCE[*]:-}"
|
||||
&& -z "$BATS_DEBUG_LAST_STACK_TRACE_IS_VALID" ]]; then
|
||||
BATS_DEBUG_LAST_STACK_TRACE=(
|
||||
${BATS_DEBUG_LASTLAST_STACK_TRACE[@]+"${BATS_DEBUG_LASTLAST_STACK_TRACE[@]}"}
|
||||
)
|
||||
fi
|
||||
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
|
||||
}
|
||||
|
||||
bats_interrupt_trap() {
|
||||
# mark the interruption, to handle during exit
|
||||
BATS_INTERRUPTED=true
|
||||
BATS_ERROR_STATUS=130
|
||||
# debug trap fires before interrupt trap but gets wrong linenumber (line 1)
|
||||
# -> use last stack trace
|
||||
exit $BATS_ERROR_STATUS
|
||||
}
|
||||
|
||||
# this is used inside run()
|
||||
bats_interrupt_trap_in_run() {
|
||||
# mark the interruption, to handle during exit
|
||||
BATS_INTERRUPTED=true
|
||||
BATS_ERROR_STATUS=130
|
||||
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=true
|
||||
exit $BATS_ERROR_STATUS
|
||||
}
|
37
extras/portable/test/bats/lib/bats-core/validator.bash
Normal file
37
extras/portable/test/bats/lib/bats-core/validator.bash
Normal file
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
bats_test_count_validator() {
|
||||
trap '' INT # continue forwarding
|
||||
header_pattern='[0-9]+\.\.[0-9]+'
|
||||
IFS= read -r header
|
||||
# repeat the header
|
||||
printf "%s\n" "$header"
|
||||
|
||||
# if we detect a TAP plan
|
||||
if [[ "$header" =~ $header_pattern ]]; then
|
||||
# extract the number of tests ...
|
||||
local expected_number_of_tests="${header:3}"
|
||||
# ... count the actual number of [not ] oks...
|
||||
local actual_number_of_tests=0
|
||||
while IFS= read -r line; do
|
||||
# forward line
|
||||
printf "%s\n" "$line"
|
||||
case "$line" in
|
||||
'ok '*)
|
||||
(( ++actual_number_of_tests ))
|
||||
;;
|
||||
'not ok'*)
|
||||
(( ++actual_number_of_tests ))
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# ... and error if they are not the same
|
||||
if [[ "${actual_number_of_tests}" != "${expected_number_of_tests}" ]]; then
|
||||
printf '# bats warning: Executed %s instead of expected %s tests\n' "$actual_number_of_tests" "$expected_number_of_tests"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# forward output unchanged
|
||||
cat
|
||||
fi
|
||||
}
|
35
extras/portable/test/bats/lib/bats-core/warnings.bash
Normal file
35
extras/portable/test/bats/lib/bats-core/warnings.bash
Normal file
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# shellcheck source=lib/bats-core/tracing.bash
|
||||
source "$BATS_ROOT/lib/bats-core/tracing.bash"
|
||||
|
||||
BATS_WARNING_SHORT_DESCS=(
|
||||
# to start with 1
|
||||
'PADDING'
|
||||
# see issue #578 for context
|
||||
"\`run\`'s command \`%s\` exited with code 127, indicating 'Command not found'. Use run's return code checks, e.g. \`run -127\`, to fix this message."
|
||||
"%s requires at least BATS_VERSION=%s. Use \`bats_require_minimum_version %s\` to fix this message."
|
||||
)
|
||||
|
||||
# generate a warning report for the parent call's call site
|
||||
bats_generate_warning() { # <warning number> [<printf args for warning string>...]
|
||||
local warning_number="$1" padding="00"
|
||||
shift
|
||||
if [[ $warning_number =~ [0-9]+ ]] && ((warning_number < ${#BATS_WARNING_SHORT_DESCS[@]} )); then
|
||||
{
|
||||
printf "BW%s: ${BATS_WARNING_SHORT_DESCS[$warning_number]}\n" "${padding:${#warning_number}}${warning_number}" "$@"
|
||||
bats_capture_stack_trace
|
||||
BATS_STACK_TRACE_PREFIX=' ' bats_print_stack_trace "${BATS_DEBUG_LAST_STACK_TRACE[@]}"
|
||||
} >> "$BATS_WARNING_FILE" 2>&3
|
||||
else
|
||||
printf "Invalid Bats warning number '%s'. It must be an integer between 1 and %d." "$warning_number" "$((${#BATS_WARNING_SHORT_DESCS[@]} - 1))" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# generate a warning if the BATS_GUARANTEED_MINIMUM_VERSION is not high enough
|
||||
bats_warn_minimum_guaranteed_version() { # <feature> <minimum required version>
|
||||
if bats_version_lt "$BATS_GUARANTEED_MINIMUM_VERSION" "$2"; then
|
||||
bats_generate_warning 2 "$1" "$2" "$2"
|
||||
fi
|
||||
}
|
467
extras/portable/test/bats/libexec/bats-core/bats
Executable file
467
extras/portable/test/bats/libexec/bats-core/bats
Executable file
|
@ -0,0 +1,467 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
export BATS_VERSION='1.7.0'
|
||||
VALID_FORMATTERS="pretty, junit, tap, tap13"
|
||||
|
||||
version() {
|
||||
printf 'Bats %s\n' "$BATS_VERSION"
|
||||
}
|
||||
|
||||
abort() {
|
||||
local print_usage=1
|
||||
if [[ ${1:-} == --no-print-usage ]]; then
|
||||
print_usage=
|
||||
shift
|
||||
fi
|
||||
printf 'Error: %s\n' "$1" >&2
|
||||
if [[ -n $print_usage ]]; then
|
||||
usage >&2
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
local cmd="${0##*/}"
|
||||
local line
|
||||
|
||||
cat <<HELP_TEXT_HEADER
|
||||
Usage: ${cmd} [OPTIONS] <tests>
|
||||
${cmd} [-h | -v]
|
||||
|
||||
HELP_TEXT_HEADER
|
||||
|
||||
cat <<'HELP_TEXT_BODY'
|
||||
<tests> is the path to a Bats test file, or the path to a directory
|
||||
containing Bats test files (ending with ".bats")
|
||||
|
||||
-c, --count Count test cases without running any tests
|
||||
--code-quote-style <style>
|
||||
A two character string of code quote delimiters
|
||||
or 'custom' which requires setting $BATS_BEGIN_CODE_QUOTE and
|
||||
$BATS_END_CODE_QUOTE. Can also be set via $BATS_CODE_QUOTE_STYLE
|
||||
-f, --filter <regex> Only run tests that match the regular expression
|
||||
--filter-status <status> Only run tests with the given status in the last completed (no CTRL+C/SIGINT) run.
|
||||
Valid <status> values are:
|
||||
failed - runs tests that failed or were not present in the last run
|
||||
missed - runs tests that were not present in the last run
|
||||
-F, --formatter <type> Switch between formatters: pretty (default),
|
||||
tap (default w/o term), tap13, junit, /<absolute path to formatter>
|
||||
--gather-test-outputs-in <directory>
|
||||
Gather the output of failing *and* passing tests
|
||||
as files in directory (if existing, must be empty)
|
||||
-h, --help Display this help message
|
||||
-j, --jobs <jobs> Number of parallel jobs (requires GNU parallel)
|
||||
--no-tempdir-cleanup Preserve test output temporary directory
|
||||
--no-parallelize-across-files
|
||||
Serialize test file execution instead of running
|
||||
them in parallel (requires --jobs >1)
|
||||
--no-parallelize-within-files
|
||||
Serialize test execution within files instead of
|
||||
running them in parallel (requires --jobs >1)
|
||||
--report-formatter <type> Switch between reporters (same options as --formatter)
|
||||
-o, --output <dir> Directory to write report files (must exist)
|
||||
-p, --pretty Shorthand for "--formatter pretty"
|
||||
--print-output-on-failure Automatically print the value of `$output` on failed tests
|
||||
-r, --recursive Include tests in subdirectories
|
||||
--show-output-of-passing-tests
|
||||
Print output of passing tests
|
||||
-t, --tap Shorthand for "--formatter tap"
|
||||
-T, --timing Add timing information to tests
|
||||
-x, --trace Print test commands as they are executed (like `set -x`)
|
||||
--verbose-run Make `run` print `$output` by default
|
||||
-v, --version Display the version number
|
||||
|
||||
For more information, see https://github.com/bats-core/bats-core
|
||||
HELP_TEXT_BODY
|
||||
}
|
||||
|
||||
expand_path() {
|
||||
local path="${1%/}"
|
||||
local dirname="${path%/*}"
|
||||
local result="$2"
|
||||
|
||||
if [[ "$dirname" == "$path" ]]; then
|
||||
dirname="$PWD"
|
||||
else
|
||||
cd "$dirname"
|
||||
dirname="$PWD"
|
||||
cd "$OLDPWD"
|
||||
fi
|
||||
printf -v "$result" '%s/%s' "$dirname" "${path##*/}"
|
||||
}
|
||||
|
||||
BATS_LIBEXEC="$(cd "$(dirname "$(bats_readlinkf "${BASH_SOURCE[0]}")")"; pwd)"
|
||||
export BATS_LIBEXEC
|
||||
export BATS_CWD="$PWD"
|
||||
export BATS_TEST_FILTER=
|
||||
export PATH="$BATS_LIBEXEC:$PATH"
|
||||
export BATS_ROOT_PID=$$
|
||||
export BATS_TMPDIR="${TMPDIR:-/tmp}"
|
||||
BATS_TMPDIR=${BATS_TMPDIR%/} # chop off trailing / to avoid duplication
|
||||
export BATS_RUN_TMPDIR=
|
||||
export BATS_GUARANTEED_MINIMUM_VERSION=0.0.0
|
||||
|
||||
if [[ ! -d "${BATS_TMPDIR}" ]];then
|
||||
printf "Error: BATS_TMPDIR (%s) does not exist or is not a directory" "${BATS_TMPDIR}" >&2
|
||||
exit 1
|
||||
elif [[ ! -w "${BATS_TMPDIR}" ]];then
|
||||
printf "Error: BATS_TMPDIR (%s) is not writable" "${BATS_TMPDIR}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
arguments=()
|
||||
|
||||
# Unpack single-character options bundled together, e.g. -cr, -pr.
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" =~ ^-[^-]. ]]; then
|
||||
index=1
|
||||
while option="${arg:$((index++)):1}"; do
|
||||
if [[ -z "$option" ]]; then
|
||||
break
|
||||
fi
|
||||
arguments+=("-$option")
|
||||
done
|
||||
else
|
||||
arguments+=("$arg")
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
set -- "${arguments[@]}"
|
||||
arguments=()
|
||||
|
||||
unset flags recursive formatter_flags
|
||||
flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
|
||||
formatter_flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
|
||||
formatter='tap'
|
||||
report_formatter=''
|
||||
recursive=
|
||||
setup_suite_file=''
|
||||
export BATS_TEMPDIR_CLEANUP=1
|
||||
output=
|
||||
if [[ -z "${CI:-}" && -t 0 && -t 1 ]] && command -v tput >/dev/null; then
|
||||
formatter='pretty'
|
||||
fi
|
||||
|
||||
while [[ "$#" -ne 0 ]]; do
|
||||
case "$1" in
|
||||
-h | --help)
|
||||
version
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
-v | --version)
|
||||
version
|
||||
exit 0
|
||||
;;
|
||||
-c | --count)
|
||||
flags+=('-c')
|
||||
;;
|
||||
-f | --filter)
|
||||
shift
|
||||
flags+=('-f' "$1")
|
||||
;;
|
||||
-F | --formatter)
|
||||
shift
|
||||
# allow cat formatter to see extended output but don't advertise to users
|
||||
if [[ $1 =~ ^(pretty|junit|tap|tap13|cat|/.*)$ ]]; then
|
||||
formatter="$1"
|
||||
else
|
||||
printf "Unknown formatter '%s', valid options are %s\n" "$1" "${VALID_FORMATTERS}"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
--report-formatter)
|
||||
shift
|
||||
if [[ $1 =~ ^(pretty|junit|tap|tap13)$ ]]; then
|
||||
report_formatter="$1"
|
||||
else
|
||||
printf "Unknown report formatter '%s', valid options are %s\n" "$1" "${VALID_FORMATTERS}"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
-o | --output)
|
||||
shift
|
||||
output="$1"
|
||||
;;
|
||||
-p | --pretty)
|
||||
formatter='pretty'
|
||||
;;
|
||||
-j | --jobs)
|
||||
shift
|
||||
flags+=('-j' "$1")
|
||||
;;
|
||||
-r | --recursive)
|
||||
recursive=1
|
||||
;;
|
||||
-t | --tap)
|
||||
formatter='tap'
|
||||
;;
|
||||
-T | --timing)
|
||||
flags+=('-T')
|
||||
formatter_flags+=('-T')
|
||||
;;
|
||||
# this flag is now a no-op, as it is the parallel default
|
||||
--parallel-preserve-environment)
|
||||
;;
|
||||
--no-parallelize-across-files)
|
||||
flags+=("--no-parallelize-across-files")
|
||||
;;
|
||||
--no-parallelize-within-files)
|
||||
flags+=("--no-parallelize-within-files")
|
||||
;;
|
||||
--no-tempdir-cleanup)
|
||||
BATS_TEMPDIR_CLEANUP=''
|
||||
;;
|
||||
--tempdir) # for internal test consumption only!
|
||||
BATS_RUN_TMPDIR="$2"
|
||||
shift
|
||||
;;
|
||||
-x | --trace)
|
||||
flags+=(--trace)
|
||||
;;
|
||||
--print-output-on-failure)
|
||||
flags+=(--print-output-on-failure)
|
||||
;;
|
||||
--show-output-of-passing-tests)
|
||||
flags+=(--show-output-of-passing-tests)
|
||||
;;
|
||||
--verbose-run)
|
||||
flags+=(--verbose-run)
|
||||
;;
|
||||
--gather-test-outputs-in)
|
||||
shift
|
||||
output_dir="$1"
|
||||
if [ -d "$output_dir" ]; then
|
||||
if ! find "$output_dir" -mindepth 1 -exec false {} + 2>/dev/null; then
|
||||
abort --no-print-usage "Directory '$output_dir' must be empty for --gather-test-outputs-in"
|
||||
fi
|
||||
elif ! mkdir "$output_dir" 2>/dev/null; then
|
||||
abort --no-print-usage "Could not create '$output_dir' for --gather-test-outputs-in"
|
||||
fi
|
||||
flags+=(--gather-test-outputs-in "$output_dir")
|
||||
;;
|
||||
--setup-suite-file)
|
||||
shift
|
||||
setup_suite_file="$1"
|
||||
;;
|
||||
--code-quote-style)
|
||||
shift
|
||||
BATS_CODE_QUOTE_STYLE="$1"
|
||||
;;
|
||||
--filter-status)
|
||||
shift
|
||||
flags+=('--filter-status' "$1")
|
||||
;;
|
||||
-*)
|
||||
abort "Bad command line option '$1'"
|
||||
;;
|
||||
*)
|
||||
arguments+=("$1")
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ -n "${BATS_RUN_TMPDIR:-}" ]];then
|
||||
if [[ -d "$BATS_RUN_TMPDIR" ]]; then
|
||||
printf "Error: BATS_RUN_TMPDIR (%s) already exists\n" "$BATS_RUN_TMPDIR" >&2
|
||||
printf "Reusing old run directories can lead to unexpected results ... aborting!\n" >&2
|
||||
exit 1
|
||||
elif ! mkdir -p "$BATS_RUN_TMPDIR" ;then
|
||||
printf "Error: Failed to create BATS_RUN_TMPDIR (%s)\n" "$BATS_RUN_TMPDIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
elif ! BATS_RUN_TMPDIR=$(mktemp -d "${BATS_TMPDIR}/bats-run-XXXXXX");then
|
||||
printf "Error: Failed to create BATS_RUN_TMPDIR (%s) with mktemp\n" "${BATS_TMPDIR}/bats-run-XXXXXX" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export BATS_WARNING_FILE="${BATS_RUN_TMPDIR}/warnings.log"
|
||||
|
||||
bats_exit_trap() {
|
||||
if [[ -s "$BATS_WARNING_FILE" ]]; then
|
||||
local pre_cat='' post_cat=''
|
||||
if [[ $formatter == pretty ]]; then
|
||||
pre_cat=$'\x1B[31m'
|
||||
post_cat=$'\x1B[0m'
|
||||
fi
|
||||
printf "\nThe following warnings were encountered during tests:\n%s" "$pre_cat"
|
||||
cat "$BATS_WARNING_FILE"
|
||||
printf "%s" "$post_cat"
|
||||
fi >&2
|
||||
|
||||
if [[ -n "$BATS_TEMPDIR_CLEANUP" ]]; then
|
||||
rm -rf "$BATS_RUN_TMPDIR"
|
||||
else
|
||||
printf "BATS_RUN_TMPDIR: %s\n" "$BATS_RUN_TMPDIR" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
trap bats_exit_trap EXIT
|
||||
|
||||
if [[ "$formatter" != "tap" ]]; then
|
||||
flags+=('-x')
|
||||
fi
|
||||
|
||||
if [[ -n "$report_formatter" && "$report_formatter" != "tap" ]]; then
|
||||
flags+=('-x')
|
||||
fi
|
||||
|
||||
if [[ "$formatter" == "junit" ]]; then
|
||||
flags+=('-T')
|
||||
formatter_flags+=('--base-path' "${arguments[0]}")
|
||||
fi
|
||||
if [[ "$report_formatter" == "junit" ]]; then
|
||||
flags+=('-T')
|
||||
report_formatter_flags+=('--base-path' "${arguments[0]}")
|
||||
fi
|
||||
|
||||
if [[ "$formatter" == "pretty" ]]; then
|
||||
formatter_flags+=('--base-path' "${arguments[0]}")
|
||||
fi
|
||||
|
||||
# if we don't need to filter extended syntax, use the faster formatter
|
||||
if [[ "$formatter" == tap && -z "$report_formatter" ]]; then
|
||||
formatter="cat"
|
||||
fi
|
||||
|
||||
bats_check_formatter() { # <formatter-path>
|
||||
local -r formatter="$1"
|
||||
if [[ ! -f "$formatter" ]]; then
|
||||
printf "ERROR: Formatter '%s' is not readable!\n" "$formatter"
|
||||
exit 1
|
||||
elif [[ ! -x "$formatter" ]]; then
|
||||
printf "ERROR: Formatter '%s' is not executable!\n" "$formatter"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ $formatter == /* ]]; then # absolute paths are direct references to formatters
|
||||
bats_check_formatter "$formatter"
|
||||
interpolated_formatter="$formatter"
|
||||
else
|
||||
interpolated_formatter="bats-format-${formatter}"
|
||||
fi
|
||||
|
||||
if [[ "${#arguments[@]}" -eq 0 ]]; then
|
||||
abort 'Must specify at least one <test>'
|
||||
fi
|
||||
|
||||
if [[ -n "$report_formatter" ]]; then
|
||||
# default to the current directory for output
|
||||
if [[ -z "$output" ]]; then
|
||||
output=.
|
||||
fi
|
||||
# only set BATS_REPORT_FILENAME if none was given
|
||||
if [[ -z "${BATS_REPORT_FILENAME:-}" ]]; then
|
||||
case "$report_formatter" in
|
||||
tap|tap13)
|
||||
BATS_REPORT_FILE_NAME="report.tap"
|
||||
;;
|
||||
junit)
|
||||
BATS_REPORT_FILE_NAME="report.xml"
|
||||
;;
|
||||
*)
|
||||
BATS_REPORT_FILE_NAME="report.log"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $report_formatter == /* ]]; then # absolute paths are direct references to formatters
|
||||
bats_check_formatter "$report_formatter"
|
||||
interpolated_report_formatter="${report_formatter}"
|
||||
else
|
||||
interpolated_report_formatter="bats-format-${report_formatter}"
|
||||
fi
|
||||
|
||||
if [[ "${BATS_CODE_QUOTE_STYLE-BATS_CODE_QUOTE_STYLE_UNSET}" == BATS_CODE_QUOTE_STYLE_UNSET ]]; then
|
||||
BATS_CODE_QUOTE_STYLE="\`'"
|
||||
fi
|
||||
|
||||
case "${BATS_CODE_QUOTE_STYLE}" in
|
||||
??)
|
||||
BATS_BEGIN_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE::1}"
|
||||
BATS_END_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE:1:1}"
|
||||
export BATS_BEGIN_CODE_QUOTE BATS_END_CODE_QUOTE
|
||||
;;
|
||||
custom)
|
||||
if [[ ${BATS_BEGIN_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET
|
||||
|| ${BATS_END_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET ]]; then
|
||||
printf "ERROR: BATS_CODE_QUOTE_STYLE=custom requires BATS_BEGIN_CODE_QUOTE and BATS_END_CODE_QUOTE to be set\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
printf "ERROR: Unknown BATS_CODE_QUOTE_STYLE: %s\n" "$BATS_CODE_QUOTE_STYLE" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -n "$output" ]]; then
|
||||
if [[ ! -w "${output}" ]]; then
|
||||
abort "Output path ${output} is not writeable"
|
||||
fi
|
||||
export BATS_REPORT_OUTPUT_PATH="$output"
|
||||
fi
|
||||
|
||||
if [[ -n "$setup_suite_file" && ! -f "$setup_suite_file" ]]; then
|
||||
abort "--setup-suite-file $setup_suite_file does not exist!"
|
||||
fi
|
||||
|
||||
filenames=()
|
||||
for filename in "${arguments[@]}"; do
|
||||
expand_path "$filename" 'filename'
|
||||
|
||||
if [[ -z "$setup_suite_file" ]]; then
|
||||
if [[ -d "$filename" ]]; then
|
||||
dirname="$filename"
|
||||
else
|
||||
dirname="${filename%/*}"
|
||||
fi
|
||||
potential_setup_suite_file="$dirname/setup_suite.bash"
|
||||
if [[ -e "$potential_setup_suite_file" ]]; then
|
||||
setup_suite_file="$potential_setup_suite_file"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -d "$filename" ]]; then
|
||||
shopt -s nullglob
|
||||
if [[ "$recursive" -eq 1 ]]; then
|
||||
while IFS= read -r -d $'\0' file; do
|
||||
filenames+=("$file")
|
||||
done < <(find -L "$filename" -type f -name "*.${BATS_FILE_EXTENSION:-bats}" -print0 | sort -z)
|
||||
else
|
||||
for suite_filename in "$filename"/*."${BATS_FILE_EXTENSION:-bats}"; do
|
||||
filenames+=("$suite_filename")
|
||||
done
|
||||
fi
|
||||
shopt -u nullglob
|
||||
else
|
||||
filenames+=("$filename")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "$setup_suite_file" ]]; then
|
||||
flags+=("--setup-suite-file" "$setup_suite_file")
|
||||
fi
|
||||
|
||||
# shellcheck source=lib/bats-core/validator.bash
|
||||
source "$BATS_ROOT/lib/bats-core/validator.bash"
|
||||
|
||||
trap 'BATS_INTERRUPTED=true' INT # let the lower levels handle the interruption
|
||||
|
||||
set -o pipefail execfail
|
||||
|
||||
if [[ -n "$report_formatter" ]]; then
|
||||
exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | \
|
||||
tee >("$interpolated_report_formatter" "${report_formatter_flags[@]}" >"${BATS_REPORT_OUTPUT_PATH}/${BATS_REPORT_FILE_NAME}") | \
|
||||
bats_test_count_validator | \
|
||||
"$interpolated_formatter" "${formatter_flags[@]}"
|
||||
else
|
||||
exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | \
|
||||
bats_test_count_validator | \
|
||||
"$interpolated_formatter" "${formatter_flags[@]}"
|
||||
fi
|
338
extras/portable/test/bats/libexec/bats-core/bats-exec-file
Executable file
338
extras/portable/test/bats/libexec/bats-core/bats-exec-file
Executable file
|
@ -0,0 +1,338 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eET
|
||||
|
||||
flags=('--dummy-flag')
|
||||
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
|
||||
extended_syntax=''
|
||||
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
|
||||
declare -r BATS_RETRY_RETURN_CODE=126
|
||||
export BATS_TEST_RETRIES=0 # no retries by default
|
||||
|
||||
while [[ "$#" -ne 0 ]]; do
|
||||
case "$1" in
|
||||
-j)
|
||||
shift
|
||||
num_jobs="$1"
|
||||
;;
|
||||
-T)
|
||||
flags+=('-T')
|
||||
;;
|
||||
-x)
|
||||
flags+=('-x')
|
||||
extended_syntax=1
|
||||
;;
|
||||
--no-parallelize-within-files)
|
||||
# use singular to allow for users to override in file
|
||||
BATS_NO_PARALLELIZE_WITHIN_FILE=1
|
||||
;;
|
||||
--dummy-flag)
|
||||
;;
|
||||
--trace)
|
||||
flags+=('--trace')
|
||||
;;
|
||||
--print-output-on-failure)
|
||||
flags+=(--print-output-on-failure)
|
||||
;;
|
||||
--show-output-of-passing-tests)
|
||||
flags+=(--show-output-of-passing-tests)
|
||||
;;
|
||||
--verbose-run)
|
||||
flags+=(--verbose-run)
|
||||
;;
|
||||
--gather-test-outputs-in)
|
||||
shift
|
||||
flags+=(--gather-test-outputs-in "$1")
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
filename="$1"
|
||||
TESTS_FILE="$2"
|
||||
|
||||
if [[ ! -f "$filename" ]]; then
|
||||
printf 'Testfile "%s" not found\n' "$filename" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export BATS_TEST_FILENAME="$filename"
|
||||
|
||||
# shellcheck source=lib/bats-core/preprocessing.bash
|
||||
# shellcheck disable=SC2153
|
||||
source "$BATS_ROOT/lib/bats-core/preprocessing.bash"
|
||||
|
||||
bats_run_setup_file() {
|
||||
# shellcheck source=lib/bats-core/tracing.bash
|
||||
# shellcheck disable=SC2153
|
||||
source "$BATS_ROOT/lib/bats-core/tracing.bash"
|
||||
# shellcheck source=lib/bats-core/test_functions.bash
|
||||
# shellcheck disable=SC2153
|
||||
source "$BATS_ROOT/lib/bats-core/test_functions.bash"
|
||||
|
||||
exec 3<&1
|
||||
|
||||
# these are defined only to avoid errors when referencing undefined variables down the line
|
||||
# shellcheck disable=2034
|
||||
BATS_TEST_NAME= # used in tracing.bash
|
||||
# shellcheck disable=2034
|
||||
BATS_TEST_COMPLETED= # used in tracing.bash
|
||||
|
||||
BATS_SOURCE_FILE_COMPLETED=
|
||||
BATS_SETUP_FILE_COMPLETED=
|
||||
BATS_TEARDOWN_FILE_COMPLETED=
|
||||
# shellcheck disable=2034
|
||||
BATS_ERROR_STATUS= # used in tracing.bash
|
||||
touch "$BATS_OUT"
|
||||
bats_setup_tracing
|
||||
trap 'bats_file_teardown_trap' EXIT
|
||||
|
||||
local status=0
|
||||
# get the setup_file/teardown_file functions for this file (if it has them)
|
||||
# shellcheck disable=SC1090
|
||||
source "$BATS_TEST_SOURCE" >>"$BATS_OUT" 2>&1
|
||||
|
||||
BATS_SOURCE_FILE_COMPLETED=1
|
||||
|
||||
setup_file >>"$BATS_OUT" 2>&1
|
||||
|
||||
BATS_SETUP_FILE_COMPLETED=1
|
||||
}
|
||||
|
||||
bats_run_teardown_file() {
|
||||
# avoid running the therdown trap due to errors in teardown_file
|
||||
trap 'bats_file_exit_trap' EXIT
|
||||
# rely on bats_error_trap to catch failures
|
||||
teardown_file >>"$BATS_OUT" 2>&1
|
||||
|
||||
BATS_TEARDOWN_FILE_COMPLETED=1
|
||||
}
|
||||
|
||||
bats_file_teardown_trap() {
|
||||
bats_run_teardown_file
|
||||
bats_file_exit_trap
|
||||
}
|
||||
|
||||
# shellcheck source=lib/bats-core/common.bash
|
||||
source "$BATS_ROOT/lib/bats-core/common.bash"
|
||||
|
||||
bats_file_exit_trap() {
|
||||
trap - ERR EXIT
|
||||
local failure_reason
|
||||
local -i failure_test_index=$(( BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE + 1 ))
|
||||
if [[ -z "$BATS_SETUP_FILE_COMPLETED" || -z "$BATS_TEARDOWN_FILE_COMPLETED" ]]; then
|
||||
if [[ -z "$BATS_SETUP_FILE_COMPLETED" ]]; then
|
||||
failure_reason='setup_file'
|
||||
elif [[ -z "$BATS_TEARDOWN_FILE_COMPLETED" ]]; then
|
||||
failure_reason='teardown_file'
|
||||
failure_test_index=$(( BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE + ${#tests_to_run[@]} + 1 ))
|
||||
elif [[ -z "$BATS_SOURCE_FILE_COMPLETED" ]]; then
|
||||
failure_reason='source'
|
||||
else
|
||||
failure_reason='unknown internal'
|
||||
fi
|
||||
printf "not ok %d %s\n" "$failure_test_index" "$failure_reason failed" >&3
|
||||
local stack_trace
|
||||
bats_get_failure_stack_trace stack_trace
|
||||
bats_print_stack_trace "${stack_trace[@]}" >&3
|
||||
bats_print_failed_command "${stack_trace[@]}" >&3
|
||||
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
|
||||
rm -rf "$BATS_OUT"
|
||||
bats_exec_file_status=1
|
||||
fi
|
||||
exit $bats_exec_file_status
|
||||
}
|
||||
|
||||
function setup_file() {
|
||||
return 0
|
||||
}
|
||||
|
||||
function teardown_file() {
|
||||
return 0
|
||||
}
|
||||
|
||||
bats_forward_output_of_parallel_test() {
|
||||
local test_number_in_suite=$1
|
||||
local status=0
|
||||
wait "$(cat "$output_folder/$test_number_in_suite/pid")" || status=1
|
||||
cat "$output_folder/$test_number_in_suite/stdout"
|
||||
cat "$output_folder/$test_number_in_suite/stderr" >&2
|
||||
return $status
|
||||
}
|
||||
|
||||
bats_is_next_parallel_test_finished() {
|
||||
local PID
|
||||
# get the pid of the next potentially finished test
|
||||
PID=$(cat "$output_folder/$(( test_number_in_suite_of_last_finished_test + 1 ))/pid")
|
||||
# try to send a signal to this process
|
||||
# if it fails, the process exited,
|
||||
# if it succeeds, the process is still running
|
||||
if kill -0 "$PID" 2>/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# prints output from all tests in the order they were started
|
||||
# $1 == "blocking": wait for a test to finish before printing
|
||||
# != "blocking": abort printing, when a test has not finished
|
||||
bats_forward_output_for_parallel_tests() {
|
||||
local status=0
|
||||
# was the next test already started?
|
||||
while (( test_number_in_suite_of_last_finished_test + 1 <= test_number_in_suite )); do
|
||||
# if we are okay with waiting or if the test has already been finished
|
||||
if [[ "$1" == "blocking" ]] || bats_is_next_parallel_test_finished ; then
|
||||
(( ++test_number_in_suite_of_last_finished_test ))
|
||||
bats_forward_output_of_parallel_test "$test_number_in_suite_of_last_finished_test" || status=$?
|
||||
else
|
||||
# non-blocking and the process has not finished -> abort the printing
|
||||
break
|
||||
fi
|
||||
done
|
||||
return $status
|
||||
}
|
||||
|
||||
bats_run_test_with_retries() { # <args>
|
||||
local status=0
|
||||
local should_try_again=1 try_number
|
||||
for ((try_number=1; should_try_again; ++try_number)); do
|
||||
if "$BATS_LIBEXEC/bats-exec-test" "$@" "$try_number"; then
|
||||
should_try_again=0
|
||||
else
|
||||
status=$?
|
||||
if ((status == BATS_RETRY_RETURN_CODE)); then
|
||||
should_try_again=1
|
||||
else
|
||||
should_try_again=0
|
||||
bats_exec_file_status=$status
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return $status
|
||||
}
|
||||
|
||||
bats_run_tests_in_parallel() {
|
||||
local output_folder="$BATS_RUN_TMPDIR/parallel_output"
|
||||
local status=0
|
||||
mkdir -p "$output_folder"
|
||||
# shellcheck source=lib/bats-core/semaphore.bash
|
||||
source "$BATS_ROOT/lib/bats-core/semaphore.bash"
|
||||
bats_semaphore_setup
|
||||
# the test_number_in_file is not yet incremented -> one before the next test to run
|
||||
local test_number_in_suite_of_last_finished_test="$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE" # stores which test was printed last
|
||||
local test_number_in_file=0 test_number_in_suite=$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE
|
||||
for test_name in "${tests_to_run[@]}"; do
|
||||
# Only handle non-empty lines
|
||||
if [[ $test_name ]]; then
|
||||
((++test_number_in_suite))
|
||||
((++test_number_in_file))
|
||||
mkdir -p "$output_folder/$test_number_in_suite"
|
||||
bats_semaphore_run "$output_folder/$test_number_in_suite" \
|
||||
bats_run_test_with_retries "${flags[@]}" "$filename" "$test_name" "$test_number_in_suite" "$test_number_in_file" \
|
||||
> "$output_folder/$test_number_in_suite/pid"
|
||||
fi
|
||||
# print results early to get interactive feedback
|
||||
bats_forward_output_for_parallel_tests non-blocking || status=1 # ignore if we did not finish yet
|
||||
done
|
||||
bats_forward_output_for_parallel_tests blocking || status=1
|
||||
return $status
|
||||
}
|
||||
|
||||
bats_read_tests_list_file() {
|
||||
local line_number=0
|
||||
tests_to_run=()
|
||||
# the global test number must be visible to traps -> not local
|
||||
local test_number_in_suite=''
|
||||
while read -r test_line; do
|
||||
# check if the line begins with filename
|
||||
# filename might contain some hard to parse characters,
|
||||
# use simple string operations to work around that issue
|
||||
if [[ "$filename" == "${test_line::${#filename}}" ]]; then
|
||||
# get the rest of the line without the separator \t
|
||||
test_name=${test_line:$((1 + ${#filename} ))}
|
||||
tests_to_run+=("$test_name")
|
||||
# save the first test's number for later iteration
|
||||
# this assumes that tests for a file are stored consecutive in the file!
|
||||
if [[ -z "$test_number_in_suite" ]]; then
|
||||
test_number_in_suite=$line_number
|
||||
fi
|
||||
fi
|
||||
((++line_number))
|
||||
done <"$TESTS_FILE"
|
||||
BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE="$test_number_in_suite"
|
||||
declare -ri BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE # mark readonly (cannot merge assignment, because value would be lost)
|
||||
}
|
||||
|
||||
bats_run_tests() {
|
||||
bats_exec_file_status=0
|
||||
|
||||
if [[ "$num_jobs" != 1 && "${BATS_NO_PARALLELIZE_WITHIN_FILE-False}" == False ]]; then
|
||||
export BATS_SEMAPHORE_NUMBER_OF_SLOTS="$num_jobs"
|
||||
bats_run_tests_in_parallel "$BATS_RUN_TMPDIR/parallel_output" || bats_exec_file_status=1
|
||||
else
|
||||
local test_number_in_suite=$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE \
|
||||
test_number_in_file=0
|
||||
for test_name in "${tests_to_run[@]}"; do
|
||||
if [[ "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
|
||||
bats_exec_file_status=130 # bash's code for SIGINT exits
|
||||
break
|
||||
fi
|
||||
# Only handle non-empty lines
|
||||
if [[ $test_name ]]; then
|
||||
((++test_number_in_suite))
|
||||
((++test_number_in_file))
|
||||
bats_run_test_with_retries "${flags[@]}" "$filename" "$test_name" \
|
||||
"$test_number_in_suite" "$test_number_in_file" || bats_exec_file_status=$?
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
bats_create_file_tempdirs() {
|
||||
local bats_files_tmpdir="${BATS_RUN_TMPDIR}/file"
|
||||
if ! mkdir -p "$bats_files_tmpdir"; then
|
||||
printf 'Failed to create %s\n' "$bats_files_tmpdir" >&2
|
||||
exit 1
|
||||
fi
|
||||
BATS_FILE_TMPDIR="$bats_files_tmpdir/${BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE?}"
|
||||
if ! mkdir "$BATS_FILE_TMPDIR"; then
|
||||
printf 'Failed to create BATS_FILE_TMPDIR=%s\n' "$BATS_FILE_TMPDIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
ln -s "$BATS_TEST_FILENAME" "$BATS_FILE_TMPDIR-$(basename "$BATS_TEST_FILENAME").source_file"
|
||||
export BATS_FILE_TMPDIR
|
||||
}
|
||||
|
||||
trap 'BATS_INTERRUPTED=true' INT
|
||||
|
||||
if [[ -n "$extended_syntax" ]]; then
|
||||
printf "suite %s\n" "$filename"
|
||||
fi
|
||||
|
||||
BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE=0 # predeclare as Bash 3.2 does not support declare -g
|
||||
bats_read_tests_list_file
|
||||
|
||||
# don't run potentially expensive setup/teardown_file
|
||||
# when there are no tests to run
|
||||
if [[ ${#tests_to_run[@]} -eq 0 ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# requires the test list to be read but not empty
|
||||
bats_create_file_tempdirs
|
||||
|
||||
bats_preprocess_source "$filename"
|
||||
|
||||
trap bats_interrupt_trap INT
|
||||
bats_run_setup_file
|
||||
|
||||
# during tests, we don't want to get backtraces from this level
|
||||
# just wait for the test to be interrupted and display their trace
|
||||
trap 'BATS_INTERRUPTED=true' INT
|
||||
bats_run_tests
|
||||
|
||||
trap bats_interrupt_trap INT
|
||||
bats_run_teardown_file
|
||||
|
||||
exit $bats_exec_file_status
|
359
extras/portable/test/bats/libexec/bats-core/bats-exec-suite
Executable file
359
extras/portable/test/bats/libexec/bats-core/bats-exec-suite
Executable file
|
@ -0,0 +1,359 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
count_only_flag=''
|
||||
filter=''
|
||||
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
|
||||
bats_no_parallelize_across_files=${BATS_NO_PARALLELIZE_ACROSS_FILES-}
|
||||
bats_no_parallelize_within_files=
|
||||
filter_status=''
|
||||
flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
|
||||
setup_suite_file=''
|
||||
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
|
||||
|
||||
abort() {
|
||||
printf 'Error: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
while [[ "$#" -ne 0 ]]; do
|
||||
case "$1" in
|
||||
-c)
|
||||
count_only_flag=1
|
||||
;;
|
||||
-f)
|
||||
shift
|
||||
filter="$1"
|
||||
;;
|
||||
-j)
|
||||
shift
|
||||
num_jobs="$1"
|
||||
flags+=('-j' "$num_jobs")
|
||||
;;
|
||||
-T)
|
||||
flags+=('-T')
|
||||
;;
|
||||
-x)
|
||||
flags+=('-x')
|
||||
;;
|
||||
--no-parallelize-across-files)
|
||||
bats_no_parallelize_across_files=1
|
||||
;;
|
||||
--no-parallelize-within-files)
|
||||
bats_no_parallelize_within_files=1
|
||||
flags+=("--no-parallelize-within-files")
|
||||
;;
|
||||
--filter-status)
|
||||
shift
|
||||
filter_status="$1"
|
||||
;;
|
||||
--dummy-flag)
|
||||
;;
|
||||
--trace)
|
||||
flags+=('--trace')
|
||||
(( ++BATS_TRACE_LEVEL )) # avoid returning 0
|
||||
;;
|
||||
--print-output-on-failure)
|
||||
flags+=(--print-output-on-failure)
|
||||
;;
|
||||
--show-output-of-passing-tests)
|
||||
flags+=(--show-output-of-passing-tests)
|
||||
;;
|
||||
--verbose-run)
|
||||
flags+=(--verbose-run)
|
||||
;;
|
||||
--gather-test-outputs-in)
|
||||
shift
|
||||
flags+=(--gather-test-outputs-in "$1")
|
||||
;;
|
||||
--setup-suite-file)
|
||||
shift
|
||||
setup_suite_file="$1"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ "$num_jobs" != 1 ]]; then
|
||||
if ! type -p parallel >/dev/null && [[ -z "$bats_no_parallelize_across_files" ]]; then
|
||||
abort "Cannot execute \"${num_jobs}\" jobs without GNU parallel"
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck source=lib/bats-core/semaphore.bash
|
||||
source "${BATS_ROOT}/lib/bats-core/semaphore.bash"
|
||||
bats_semaphore_setup
|
||||
fi
|
||||
|
||||
# create a file that contains all (filtered) tests to run from all files
|
||||
TESTS_LIST_FILE="${BATS_RUN_TMPDIR}/test_list_file.txt"
|
||||
|
||||
bats_gather_tests() {
|
||||
all_tests=()
|
||||
for filename in "$@"; do
|
||||
if [[ ! -f "$filename" ]]; then
|
||||
abort "Test file \"${filename}\" does not exist"
|
||||
fi
|
||||
|
||||
test_names=()
|
||||
test_dupes=()
|
||||
while read -r line; do
|
||||
if [[ ! "$line" =~ ^bats_test_function\ ]]; then
|
||||
continue
|
||||
fi
|
||||
line="${line%$'\r'}"
|
||||
line="${line#* }"
|
||||
test_line=$(printf "%s\t%s" "$filename" "$line")
|
||||
all_tests+=("$test_line")
|
||||
printf "%s\n" "$test_line" >>"$TESTS_LIST_FILE"
|
||||
# avoid unbound variable errors on empty array expansion with old bash versions
|
||||
if [[ ${#test_names[@]} -gt 0 && " ${test_names[*]} " == *" $line "* ]]; then
|
||||
test_dupes+=("$line")
|
||||
continue
|
||||
fi
|
||||
test_names+=("$line")
|
||||
done < <(BATS_TEST_FILTER="$filter" bats-preprocess "$filename")
|
||||
|
||||
if [[ "${#test_dupes[@]}" -ne 0 ]]; then
|
||||
abort "Duplicate test name(s) in file \"${filename}\": ${test_dupes[*]}"
|
||||
fi
|
||||
done
|
||||
|
||||
test_count="${#all_tests[@]}"
|
||||
}
|
||||
|
||||
TEST_ROOT=${1-}
|
||||
TEST_ROOT=${TEST_ROOT%/*}
|
||||
BATS_RUN_LOGS_DIRECTORY="$TEST_ROOT/.bats/run-logs"
|
||||
if [[ ! -d "$BATS_RUN_LOGS_DIRECTORY" ]]; then
|
||||
if [[ -n "$filter_status" ]]; then
|
||||
printf "Error: --filter-status needs '%s/' to save failed tests. Please create this folder, add it to .gitignore and try again.\n" "$BATS_RUN_LOGS_DIRECTORY"
|
||||
exit 1
|
||||
else
|
||||
BATS_RUN_LOGS_DIRECTORY=
|
||||
fi
|
||||
# discard via sink instead of having a conditional later
|
||||
export BATS_RUNLOG_FILE='/dev/null'
|
||||
else
|
||||
# use UTC (-u) to avoid problems with TZ changes
|
||||
BATS_RUNLOG_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||
export BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}.log"
|
||||
fi
|
||||
|
||||
bats_gather_tests "$@"
|
||||
|
||||
if [[ -n "$filter_status" ]]; then
|
||||
# shellcheck source=lib/bats-core/common.bash
|
||||
source "$BATS_ROOT/lib/bats-core/common.bash"
|
||||
case "$filter_status" in
|
||||
failed)
|
||||
bats_filter_test_by_status() { # <line>
|
||||
! bats_binary_search "$1" "passed_tests"
|
||||
}
|
||||
;;
|
||||
passed)
|
||||
bats_filter_test_by_status() {
|
||||
! bats_binary_search "$1" "failed_tests"
|
||||
}
|
||||
;;
|
||||
missed)
|
||||
bats_filter_test_by_status() {
|
||||
! bats_binary_search "$1" "failed_tests" && ! bats_binary_search "$1" "passed_tests"
|
||||
}
|
||||
;;
|
||||
*)
|
||||
printf "Error: Unknown value '%s' for --filter-status. Valid values are 'failed' and 'missed'.\n" "$filter_status">&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if IFS='' read -d $'\n' -r BATS_PREVIOUS_RUNLOG_FILE < <(ls -1r "$BATS_RUN_LOGS_DIRECTORY"); then
|
||||
BATS_PREVIOUS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/$BATS_PREVIOUS_RUNLOG_FILE"
|
||||
if [[ $BATS_PREVIOUS_RUNLOG_FILE == "$BATS_RUNLOG_FILE" ]]; then
|
||||
count=$(find "$BATS_RUN_LOGS_DIRECTORY" -name "$BATS_RUNLOG_DATE*" | wc -l)
|
||||
BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}-$count.log"
|
||||
fi
|
||||
failed_tests=()
|
||||
passed_tests=()
|
||||
# store tests that were already filtered out in the last run for the same filter reason
|
||||
last_filtered_tests=()
|
||||
i=0
|
||||
while read -rd $'\n' line; do
|
||||
((++i))
|
||||
case "$line" in
|
||||
"passed "*)
|
||||
passed_tests+=("${line#passed }")
|
||||
;;
|
||||
"failed "*)
|
||||
failed_tests+=("${line#failed }")
|
||||
;;
|
||||
"status-filtered $filter_status"*) # pick up tests that were filtered in the last round for the same status
|
||||
last_filtered_tests+=("${line#status-filtered "$filter_status" }")
|
||||
;;
|
||||
"status-filtered "*) # ignore other status-filtered lines
|
||||
;;
|
||||
"#"*) # allow for comments
|
||||
;;
|
||||
*)
|
||||
printf "Error: %s:%d: Invalid format: %s\n" "$BATS_PREVIOUS_RUNLOG_FILE" "$i" "$line" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done < <(sort "$BATS_PREVIOUS_RUNLOG_FILE")
|
||||
|
||||
filtered_tests=()
|
||||
for line in "${all_tests[@]}"; do
|
||||
if bats_filter_test_by_status "$line" && ! bats_binary_search "$line" last_filtered_tests; then
|
||||
printf "%s\n" "$line"
|
||||
filtered_tests+=("$line")
|
||||
else
|
||||
printf "status-filtered %s %s\n" "$filter_status" "$line" >> "$BATS_RUNLOG_FILE"
|
||||
fi
|
||||
done > "$TESTS_LIST_FILE"
|
||||
|
||||
# save filtered tests to exclude them again in next round
|
||||
for test_line in "${last_filtered_tests[@]}"; do
|
||||
printf "status-filtered %s %s\n" "$filter_status" "$test_line"
|
||||
done >> "$BATS_RUNLOG_FILE"
|
||||
|
||||
test_count="${#filtered_tests[@]}"
|
||||
if [[ ${#failed_tests[@]} -eq 0 && ${#filtered_tests[@]} -eq 0 ]]; then
|
||||
printf "There where no failed tests in the last recorded run.\n" >&2
|
||||
fi
|
||||
else
|
||||
printf "No recording of previous runs found. Running all tests!\n" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$count_only_flag" ]]; then
|
||||
printf '%d\n' "${test_count}"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ -n "$bats_no_parallelize_across_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
|
||||
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$bats_no_parallelize_within_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
|
||||
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# only abort on the lowest levels
|
||||
trap 'BATS_INTERRUPTED=true' INT
|
||||
|
||||
bats_exec_suite_status=0
|
||||
printf '1..%d\n' "${test_count}"
|
||||
|
||||
# No point on continuing if there's no tests.
|
||||
if [[ "${test_count}" == 0 ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
export BATS_SUITE_TMPDIR="${BATS_RUN_TMPDIR}/suite"
|
||||
if ! mkdir "$BATS_SUITE_TMPDIR"; then
|
||||
printf '%s\n' "Failed to create BATS_SUITE_TMPDIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Deduplicate filenames (without reordering) to avoid running duplicate tests n by n times.
|
||||
# (see https://github.com/bats-core/bats-core/issues/329)
|
||||
# If a file was specified multiple times, we already got it repeatedly in our TESTS_LIST_FILE.
|
||||
# Thus, it suffices to bats-exec-file it once to run all repeated tests on it.
|
||||
IFS=$'\n' read -d '' -r -a BATS_UNIQUE_TEST_FILENAMES < <(printf "%s\n" "$@"| nl | sort -k 2 | uniq -f 1 | sort -n | cut -f 2-) || true
|
||||
|
||||
# shellcheck source=lib/bats-core/tracing.bash
|
||||
source "$BATS_ROOT/lib/bats-core/tracing.bash"
|
||||
bats_setup_tracing
|
||||
|
||||
trap bats_suite_exit_trap EXIT
|
||||
|
||||
bats_suite_exit_trap() {
|
||||
if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" || -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
|
||||
if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" ]]; then
|
||||
printf "not ok 1 setup_suite\n"
|
||||
elif [[ -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
|
||||
printf "not ok %d teardown_suite\n" $((test_count+1))
|
||||
fi
|
||||
local stack_trace
|
||||
bats_get_failure_stack_trace stack_trace
|
||||
bats_print_stack_trace "${stack_trace[@]}"
|
||||
bats_print_failed_command "${stack_trace[@]}"
|
||||
bats_exec_suite_status=1
|
||||
fi
|
||||
if [[ ${BATS_INTERRUPTED-NOTSET} != NOTSET ]]; then
|
||||
printf "\n# Received SIGINT, aborting ...\n\n"
|
||||
fi
|
||||
|
||||
if [[ -d "$BATS_RUN_LOGS_DIRECTORY" && -n "${BATS_INTERRUPTED:-}" ]]; then
|
||||
# aborting a test run with CTRL+C does not save the runlog file
|
||||
rm "$BATS_RUNLOG_FILE"
|
||||
fi
|
||||
exit "$bats_exec_suite_status"
|
||||
}
|
||||
|
||||
bats_run_teardown_suite() {
|
||||
# avoid being called twice, in case this is not called through bats_teardown_suite_trap
|
||||
# but from the end of file
|
||||
trap bats_suite_exit_trap EXIT
|
||||
set -eET
|
||||
BATS_TEARDOWN_SUITE_COMPLETED=
|
||||
teardown_suite 2>&1
|
||||
BATS_TEARDOWN_SUITE_COMPLETED=1
|
||||
set +ET
|
||||
}
|
||||
|
||||
bats_teardown_suite_trap() {
|
||||
bats_run_teardown_suite
|
||||
bats_suite_exit_trap
|
||||
}
|
||||
|
||||
setup_suite() {
|
||||
:
|
||||
}
|
||||
|
||||
teardown_suite() {
|
||||
:
|
||||
}
|
||||
|
||||
trap bats_teardown_suite_trap EXIT
|
||||
|
||||
if [[ -n "$setup_suite_file" ]]; then
|
||||
setup_suite() {
|
||||
printf "%s does not define \`setup_suite()\`\n" "$setup_suite_file" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
source "$setup_suite_file"
|
||||
fi
|
||||
|
||||
set -eET
|
||||
BATS_SETUP_SUITE_COMPLETED=
|
||||
setup_suite 2>&1
|
||||
BATS_SETUP_SUITE_COMPLETED=1
|
||||
set +ET
|
||||
|
||||
if [[ "$num_jobs" -gt 1 ]] && [[ -z "$bats_no_parallelize_across_files" ]]; then
|
||||
# run files in parallel to get the maximum pool of parallel tasks
|
||||
# shellcheck disable=SC2086,SC2068
|
||||
# we need to handle the quoting of ${flags[@]} ourselves,
|
||||
# because parallel can only quote it as one
|
||||
parallel --keep-order --jobs "$num_jobs" bats-exec-file "$(printf "%q " "${flags[@]}")" "{}" "$TESTS_LIST_FILE" ::: "${BATS_UNIQUE_TEST_FILENAMES[@]}" 2>&1 || bats_exec_suite_status=1
|
||||
else
|
||||
for filename in "${BATS_UNIQUE_TEST_FILENAMES[@]}"; do
|
||||
if [[ "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
|
||||
bats_exec_suite_status=130 # bash's code for SIGINT exits
|
||||
break
|
||||
fi
|
||||
bats-exec-file "${flags[@]}" "$filename" "${TESTS_LIST_FILE}" || bats_exec_suite_status=1
|
||||
done
|
||||
fi
|
||||
|
||||
set -eET
|
||||
bats_run_teardown_suite
|
||||
|
||||
exit "$bats_exec_suite_status"
|
231
extras/portable/test/bats/libexec/bats-core/bats-exec-test
Executable file
231
extras/portable/test/bats/libexec/bats-core/bats-exec-test
Executable file
|
@ -0,0 +1,231 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eET
|
||||
|
||||
# Variables used in other scripts.
|
||||
BATS_ENABLE_TIMING=''
|
||||
BATS_EXTENDED_SYNTAX=''
|
||||
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
|
||||
BATS_PRINT_OUTPUT_ON_FAILURE="${BATS_PRINT_OUTPUT_ON_FAILURE:-}"
|
||||
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS:-}"
|
||||
BATS_VERBOSE_RUN="${BATS_VERBOSE_RUN:-}"
|
||||
BATS_GATHER_TEST_OUTPUTS_IN="${BATS_GATHER_TEST_OUTPUTS_IN:-}"
|
||||
BATS_TEST_NAME_PREFIX="${BATS_TEST_NAME_PREFIX:-}"
|
||||
|
||||
while [[ "$#" -ne 0 ]]; do
|
||||
case "$1" in
|
||||
-T)
|
||||
BATS_ENABLE_TIMING='-T'
|
||||
;;
|
||||
-x)
|
||||
# shellcheck disable=SC2034
|
||||
BATS_EXTENDED_SYNTAX='-x'
|
||||
;;
|
||||
--dummy-flag)
|
||||
;;
|
||||
--trace)
|
||||
(( ++BATS_TRACE_LEVEL )) # avoid returning 0
|
||||
;;
|
||||
--print-output-on-failure)
|
||||
BATS_PRINT_OUTPUT_ON_FAILURE=1
|
||||
;;
|
||||
--show-output-of-passing-tests)
|
||||
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS=1
|
||||
;;
|
||||
--verbose-run)
|
||||
BATS_VERBOSE_RUN=1
|
||||
;;
|
||||
--gather-test-outputs-in)
|
||||
shift
|
||||
BATS_GATHER_TEST_OUTPUTS_IN="$1"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
export BATS_TEST_FILENAME="$1"
|
||||
export BATS_TEST_NAME="$2"
|
||||
export BATS_SUITE_TEST_NUMBER="$3"
|
||||
export BATS_TEST_NUMBER="$4"
|
||||
BATS_TEST_TRY_NUMBER="$5"
|
||||
|
||||
if [[ -z "$BATS_TEST_FILENAME" ]]; then
|
||||
printf 'usage: bats-exec-test <filename>\n' >&2
|
||||
exit 1
|
||||
elif [[ ! -f "$BATS_TEST_FILENAME" ]]; then
|
||||
printf 'bats: %s does not exist\n' "$BATS_TEST_FILENAME" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bats_create_test_tmpdirs() {
|
||||
local tests_tmpdir="${BATS_RUN_TMPDIR}/test"
|
||||
if ! mkdir -p "$tests_tmpdir"; then
|
||||
printf 'Failed to create: %s\n' "$tests_tmpdir" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BATS_TEST_TMPDIR="$tests_tmpdir/$BATS_SUITE_TEST_NUMBER"
|
||||
if ! mkdir "$BATS_TEST_TMPDIR"; then
|
||||
printf 'Failed to create BATS_TEST_TMPDIR%d: %s\n' "$BATS_TEST_TRY_NUMBER" "$BATS_TEST_TMPDIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "%s\n" "$BATS_TEST_NAME" > "$BATS_TEST_TMPDIR.name"
|
||||
|
||||
export BATS_TEST_TMPDIR
|
||||
}
|
||||
|
||||
# load the test helper functions like `load` or `run` that are needed to run a (preprocessed) .bats file without bash errors
|
||||
# shellcheck source=lib/bats-core/test_functions.bash disable=SC2153
|
||||
source "$BATS_ROOT/lib/bats-core/test_functions.bash"
|
||||
# shellcheck source=lib/bats-core/tracing.bash disable=SC2153
|
||||
source "$BATS_ROOT/lib/bats-core/tracing.bash"
|
||||
|
||||
bats_teardown_trap() {
|
||||
bats_check_status_from_trap
|
||||
local bats_teardown_trap_status=0
|
||||
# mark the start of this function to distinguish where skip is called
|
||||
# parameter 1 will signify the reason why this function was called
|
||||
# this is used to identify when this is called as exit trap function
|
||||
BATS_TEARDOWN_STARTED=${1:-1}
|
||||
teardown >>"$BATS_OUT" 2>&1 || bats_teardown_trap_status="$?"
|
||||
|
||||
if [[ $bats_teardown_trap_status -eq 0 ]]; then
|
||||
BATS_TEARDOWN_COMPLETED=1
|
||||
elif [[ -n "$BATS_TEST_COMPLETED" ]]; then
|
||||
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
|
||||
BATS_ERROR_STATUS="$bats_teardown_trap_status"
|
||||
fi
|
||||
|
||||
bats_exit_trap
|
||||
}
|
||||
|
||||
# shellcheck source=lib/bats-core/common.bash
|
||||
source "$BATS_ROOT/lib/bats-core/common.bash"
|
||||
|
||||
bats_exit_trap() {
|
||||
local status
|
||||
local skipped=''
|
||||
trap - ERR EXIT
|
||||
|
||||
if [[ -n "$BATS_TEST_SKIPPED" ]]; then
|
||||
skipped=' # skip'
|
||||
if [[ "$BATS_TEST_SKIPPED" != '1' ]]; then
|
||||
skipped+=" $BATS_TEST_SKIPPED"
|
||||
fi
|
||||
fi
|
||||
|
||||
BATS_TEST_TIME=''
|
||||
if [[ -z "${skipped}" && -n "$BATS_ENABLE_TIMING" ]]; then
|
||||
BATS_TEST_TIME=" in "$(( $(get_mills_since_epoch) - BATS_TEST_START_TIME ))"ms"
|
||||
fi
|
||||
|
||||
local print_bats_out="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS}"
|
||||
|
||||
local should_retry=''
|
||||
if [[ -z "$BATS_TEST_COMPLETED" || -z "$BATS_TEARDOWN_COMPLETED" || "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
|
||||
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
|
||||
# For some versions of bash, `$?` may not be set properly for some error
|
||||
# conditions before triggering the EXIT trap directly (see #72 and #81).
|
||||
# Thanks to the `BATS_TEARDOWN_COMPLETED` signal, this will pinpoint such
|
||||
# errors if they happen during `teardown()` when `bats_perform_test` calls
|
||||
# `bats_teardown_trap` directly after the test itself passes.
|
||||
#
|
||||
# If instead the test fails, and the `teardown()` error happens while
|
||||
# `bats_teardown_trap` runs as the EXIT trap, the test will fail with no
|
||||
# output, since there's no way to reach the `bats_exit_trap` call.
|
||||
BATS_ERROR_STATUS=1
|
||||
fi
|
||||
if bats_should_retry_test; then
|
||||
should_retry=1
|
||||
status=126 # signify retry
|
||||
rm -r "$BATS_TEST_TMPDIR" # clean up for retry
|
||||
else
|
||||
printf 'not ok %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" >&3
|
||||
local stack_trace
|
||||
bats_get_failure_stack_trace stack_trace
|
||||
bats_print_stack_trace "${stack_trace[@]}" >&3
|
||||
bats_print_failed_command "${stack_trace[@]}" >&3
|
||||
|
||||
if [[ $BATS_PRINT_OUTPUT_ON_FAILURE && -n "${output:-}" ]]; then
|
||||
printf "Last output:\n%s\n" "$output" >> "$BATS_OUT"
|
||||
fi
|
||||
|
||||
print_bats_out=1
|
||||
status=1
|
||||
local state=failed
|
||||
fi
|
||||
else
|
||||
printf 'ok %d %s%s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" \
|
||||
"$skipped" >&3
|
||||
status=0
|
||||
local state=passed
|
||||
fi
|
||||
|
||||
if [[ -z "$should_retry" ]]; then
|
||||
printf "%s %s\t%s\n" "$state" "$BATS_TEST_FILENAME" "$BATS_TEST_NAME" >> "$BATS_RUNLOG_FILE"
|
||||
|
||||
if [[ $print_bats_out ]]; then
|
||||
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
|
||||
fi
|
||||
fi
|
||||
if [[ $BATS_GATHER_TEST_OUTPUTS_IN ]]; then
|
||||
local try_suffix=
|
||||
if [[ -n "$should_retry" ]]; then
|
||||
try_suffix="-try$BATS_TEST_TRY_NUMBER"
|
||||
fi
|
||||
cp "$BATS_OUT" "$BATS_GATHER_TEST_OUTPUTS_IN/$BATS_SUITE_TEST_NUMBER$try_suffix-$BATS_TEST_DESCRIPTION.log"
|
||||
fi
|
||||
rm -f "$BATS_OUT"
|
||||
exit "$status"
|
||||
}
|
||||
|
||||
get_mills_since_epoch() {
|
||||
local ms_since_epoch
|
||||
ms_since_epoch=$(date +%s%N)
|
||||
if [[ "$ms_since_epoch" == *N || "${#ms_since_epoch}" -lt 19 ]]; then
|
||||
ms_since_epoch=$(( $(date +%s) * 1000 ))
|
||||
else
|
||||
ms_since_epoch=$(( ms_since_epoch / 1000000 ))
|
||||
fi
|
||||
|
||||
printf "%d\n" "$ms_since_epoch"
|
||||
}
|
||||
|
||||
bats_perform_test() {
|
||||
if ! declare -F "$BATS_TEST_NAME" &>/dev/null; then
|
||||
local quoted_test_name
|
||||
bats_quote_code quoted_test_name "$BATS_TEST_NAME"
|
||||
printf "bats: unknown test name %s\n" "$quoted_test_name" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BATS_TEST_COMPLETED=
|
||||
BATS_TEST_SKIPPED=
|
||||
BATS_TEARDOWN_COMPLETED=
|
||||
BATS_ERROR_STATUS=
|
||||
bats_setup_tracing
|
||||
# mark this call as trap call
|
||||
trap 'bats_teardown_trap as-exit-trap' EXIT
|
||||
|
||||
BATS_TEST_START_TIME=$(get_mills_since_epoch)
|
||||
"$BATS_TEST_NAME" >>"$BATS_OUT" 2>&1 4>&1
|
||||
|
||||
BATS_TEST_COMPLETED=1
|
||||
trap 'bats_exit_trap' EXIT
|
||||
bats_teardown_trap "" # pass empty parameter to signify call outside trap
|
||||
}
|
||||
|
||||
trap bats_interrupt_trap INT
|
||||
|
||||
# shellcheck source=lib/bats-core/preprocessing.bash
|
||||
source "$BATS_ROOT/lib/bats-core/preprocessing.bash"
|
||||
|
||||
exec 3<&1
|
||||
|
||||
bats_create_test_tmpdirs
|
||||
# Run the given test.
|
||||
bats_evaluate_preprocessed_source
|
||||
bats_perform_test
|
6
extras/portable/test/bats/libexec/bats-core/bats-format-cat
Executable file
6
extras/portable/test/bats/libexec/bats-core/bats-format-cat
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
trap '' INT
|
||||
|
||||
cat
|
251
extras/portable/test/bats/libexec/bats-core/bats-format-junit
Executable file
251
extras/portable/test/bats/libexec/bats-core/bats-format-junit
Executable file
|
@ -0,0 +1,251 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# shellcheck source=lib/bats-core/formatter.bash
|
||||
source "$BATS_ROOT/lib/bats-core/formatter.bash"
|
||||
|
||||
BASE_PATH=.
|
||||
|
||||
|
||||
while [[ "$#" -ne 0 ]]; do
|
||||
case "$1" in
|
||||
--base-path)
|
||||
shift
|
||||
normalize_base_path BASE_PATH "$1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
init_suite() {
|
||||
suite_test_exec_time=0
|
||||
# since we have to print the suite header before its contents but we don't know the contents before the header,
|
||||
# we have to buffer the contents
|
||||
_suite_buffer=""
|
||||
test_result_state="" # declare for the first flush, when no test has been encountered
|
||||
}
|
||||
|
||||
_buffer_log=
|
||||
init_file() {
|
||||
file_count=0
|
||||
file_failures=0
|
||||
file_skipped=0
|
||||
file_exec_time=0
|
||||
test_exec_time=0
|
||||
_buffer=""
|
||||
_buffer_log=""
|
||||
_system_out_log=""
|
||||
test_result_state="" # mark that no test has run in this file so far
|
||||
}
|
||||
|
||||
host() {
|
||||
local hostname="${HOST:-}"
|
||||
[[ -z "$hostname" ]] && hostname="${HOSTNAME:-}"
|
||||
[[ -z "$hostname" ]] && hostname="$(uname -n)"
|
||||
[[ -z "$hostname" ]] && hostname="$(hostname -f)"
|
||||
|
||||
echo "$hostname"
|
||||
}
|
||||
|
||||
# convert $1 (time in milliseconds) to seconds
|
||||
milliseconds_to_seconds() {
|
||||
# we cannot rely on having bc for this calculation
|
||||
full_seconds=$(($1 / 1000))
|
||||
remaining_milliseconds=$(($1 % 1000))
|
||||
if [[ $remaining_milliseconds -eq 0 ]]; then
|
||||
printf "%d" "$full_seconds"
|
||||
else
|
||||
printf "%d.%03d" "$full_seconds" "$remaining_milliseconds"
|
||||
fi
|
||||
}
|
||||
|
||||
suite_header() {
|
||||
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||
<testsuites time=\"%s\">\n" "$(milliseconds_to_seconds "${suite_test_exec_time}")"
|
||||
}
|
||||
|
||||
file_header() {
|
||||
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S")
|
||||
printf "<testsuite name=\"%s\" tests=\"%s\" failures=\"%s\" errors=\"0\" skipped=\"%s\" time=\"%s\" timestamp=\"%s\" hostname=\"%s\">\n" \
|
||||
"$(xml_escape "${class}")" "${file_count}" "${file_failures}" "${file_skipped}" "$(milliseconds_to_seconds "${file_exec_time}")" "${timestamp}" "$(host)"
|
||||
}
|
||||
|
||||
file_footer() {
|
||||
printf "</testsuite>\n"
|
||||
}
|
||||
|
||||
suite_footer() {
|
||||
printf "</testsuites>\n"
|
||||
}
|
||||
|
||||
print_test_case() {
|
||||
if [[ "$test_result_state" == ok && -z "$_system_out_log" && -z "$_buffer_log" ]]; then
|
||||
# pass and no output can be shortened
|
||||
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\" />\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
|
||||
else
|
||||
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\">\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
|
||||
if [[ -n "$_system_out_log" ]]; then
|
||||
printf " <system-out>%s</system-out>\n" "$(xml_escape "${_system_out_log}")"
|
||||
fi
|
||||
if [[ -n "$_buffer_log" || "$test_result_state" == not_ok ]]; then
|
||||
printf " <failure type=\"failure\">%s</failure>\n" "$(xml_escape "${_buffer_log}")"
|
||||
fi
|
||||
if [[ "$test_result_state" == skipped ]]; then
|
||||
printf " <skipped>%s</skipped>\n" "$(xml_escape "$test_skip_message")"
|
||||
fi
|
||||
printf " </testcase>\n"
|
||||
fi
|
||||
}
|
||||
|
||||
xml_escape() {
|
||||
output=${1//&/&}
|
||||
output=${output//</<}
|
||||
output=${output//>/>}
|
||||
output=${output//'"'/"}
|
||||
output=${output//\'/'}
|
||||
local CONTROL_CHAR=$'\033'
|
||||
output="${output//$CONTROL_CHAR/}"
|
||||
printf "%s" "$output"
|
||||
}
|
||||
|
||||
suite_buffer() {
|
||||
local output
|
||||
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
|
||||
_suite_buffer="${_suite_buffer}${output%x}"
|
||||
}
|
||||
|
||||
suite_flush() {
|
||||
echo -n "${_suite_buffer}"
|
||||
_suite_buffer=""
|
||||
}
|
||||
|
||||
buffer() {
|
||||
local output
|
||||
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
|
||||
_buffer="${_buffer}${output%x}"
|
||||
}
|
||||
|
||||
flush() {
|
||||
echo -n "${_buffer}"
|
||||
_buffer=""
|
||||
}
|
||||
|
||||
log() {
|
||||
if [[ -n "$_buffer_log" ]]; then
|
||||
_buffer_log="${_buffer_log}
|
||||
$1"
|
||||
else
|
||||
_buffer_log="$1"
|
||||
fi
|
||||
}
|
||||
|
||||
flush_log() {
|
||||
if [[ -n "$test_result_state" ]]; then
|
||||
buffer print_test_case
|
||||
fi
|
||||
_buffer_log=""
|
||||
_system_out_log=""
|
||||
}
|
||||
|
||||
log_system_out() {
|
||||
if [[ -n "$_system_out_log" ]]; then
|
||||
_system_out_log="${_system_out_log}
|
||||
$1"
|
||||
else
|
||||
_system_out_log="$1"
|
||||
fi
|
||||
}
|
||||
|
||||
finish_file() {
|
||||
if [[ "${class-JUNIT_FORMATTER_NO_FILE_ENCOUNTERED}" != JUNIT_FORMATTER_NO_FILE_ENCOUNTERED ]]; then
|
||||
file_header
|
||||
printf "%s\n" "${_buffer}"
|
||||
file_footer
|
||||
fi
|
||||
}
|
||||
|
||||
finish_suite() {
|
||||
flush_log
|
||||
suite_header
|
||||
suite_flush
|
||||
finish_file # must come after suite flush to not print the last file before the others
|
||||
suite_footer
|
||||
}
|
||||
|
||||
bats_tap_stream_plan() { # <number of tests>
|
||||
:
|
||||
}
|
||||
|
||||
init_suite
|
||||
trap finish_suite EXIT
|
||||
trap '' INT
|
||||
|
||||
bats_tap_stream_begin() { # <test index> <test name>
|
||||
flush_log
|
||||
# set after flushing to avoid overriding name of test
|
||||
name="$2"
|
||||
}
|
||||
|
||||
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
|
||||
if [[ "$1" == "--duration" ]]; then
|
||||
test_exec_time="${BASH_REMATCH[1]}"
|
||||
else
|
||||
test_exec_time=0
|
||||
fi
|
||||
((file_count += 1))
|
||||
test_result_state='ok'
|
||||
file_exec_time="$((file_exec_time + test_exec_time))"
|
||||
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
|
||||
}
|
||||
|
||||
bats_tap_stream_skipped() { # <test index> <test name> <skip reason>
|
||||
((file_count += 1))
|
||||
((file_skipped += 1))
|
||||
test_result_state='skipped'
|
||||
test_exec_time=0
|
||||
test_skip_message="$3"
|
||||
}
|
||||
|
||||
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
|
||||
((file_count += 1))
|
||||
((file_failures += 1))
|
||||
if [[ "$1" == "--duration" ]]; then
|
||||
test_exec_time="${BASH_REMATCH[1]}"
|
||||
else
|
||||
test_exec_time=0
|
||||
fi
|
||||
test_result_state=not_ok
|
||||
file_exec_time="$((file_exec_time + test_exec_time))"
|
||||
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
|
||||
}
|
||||
|
||||
bats_tap_stream_comment() { # <comment text without leading '# '> <scope>
|
||||
local comment="$1" scope="$2"
|
||||
case "$scope" in
|
||||
begin)
|
||||
# everything that happens between begin and [not] ok is FD3 output from the test
|
||||
log_system_out "$comment"
|
||||
;;
|
||||
ok)
|
||||
# non failed tests can produce FD3 output
|
||||
log_system_out "$comment"
|
||||
;;
|
||||
*)
|
||||
# everything else is considered error output
|
||||
log "$1"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
bats_tap_stream_suite() { # <file name>
|
||||
flush_log
|
||||
suite_buffer finish_file
|
||||
init_file
|
||||
class="${1/$BASE_PATH}"
|
||||
}
|
||||
|
||||
bats_tap_stream_unknown() { # <full line>
|
||||
:
|
||||
}
|
||||
|
||||
bats_parse_internal_extended_tap
|
328
extras/portable/test/bats/libexec/bats-core/bats-format-pretty
Executable file
328
extras/portable/test/bats/libexec/bats-core/bats-format-pretty
Executable file
|
@ -0,0 +1,328 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# shellcheck source=lib/bats-core/formatter.bash
|
||||
source "$BATS_ROOT/lib/bats-core/formatter.bash"
|
||||
|
||||
BASE_PATH=.
|
||||
BATS_ENABLE_TIMING=
|
||||
|
||||
while [[ "$#" -ne 0 ]]; do
|
||||
case "$1" in
|
||||
-T)
|
||||
BATS_ENABLE_TIMING="-T"
|
||||
;;
|
||||
--base-path)
|
||||
shift
|
||||
normalize_base_path BASE_PATH "$1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
update_count_column_width() {
|
||||
count_column_width=$((${#count} * 2 + 2))
|
||||
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||
# additional space for ' in %s sec'
|
||||
count_column_width=$((count_column_width + ${#SECONDS} + 8))
|
||||
fi
|
||||
# also update dependent value
|
||||
update_count_column_left
|
||||
}
|
||||
|
||||
update_screen_width() {
|
||||
screen_width="$(tput cols)"
|
||||
# also update dependent value
|
||||
update_count_column_left
|
||||
}
|
||||
|
||||
update_count_column_left() {
|
||||
count_column_left=$((screen_width - count_column_width))
|
||||
}
|
||||
|
||||
# avoid unset variables
|
||||
count=0
|
||||
screen_width=80
|
||||
update_count_column_width
|
||||
update_screen_width
|
||||
test_result=
|
||||
|
||||
trap update_screen_width WINCH
|
||||
|
||||
begin() {
|
||||
test_result= # reset to avoid carrying over result state from previous test
|
||||
line_backoff_count=0
|
||||
go_to_column 0
|
||||
update_count_column_width
|
||||
buffer_with_truncation $((count_column_left - 1)) ' %s' "$name"
|
||||
clear_to_end_of_line
|
||||
go_to_column $count_column_left
|
||||
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||
buffer "%${#count}s/${count} in %s sec" "$index" "$SECONDS"
|
||||
else
|
||||
buffer "%${#count}s/${count}" "$index"
|
||||
fi
|
||||
go_to_column 1
|
||||
}
|
||||
|
||||
finish_test() {
|
||||
move_up $line_backoff_count
|
||||
go_to_column 0
|
||||
buffer "$@"
|
||||
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||
set_color 2
|
||||
buffer ' [%s]' "$TIMING"
|
||||
fi
|
||||
advance
|
||||
move_down $(( line_backoff_count - 1 ))
|
||||
}
|
||||
|
||||
pass() {
|
||||
TIMING="${1:-}"
|
||||
finish_test ' ✓ %s' "$name"
|
||||
test_result=pass
|
||||
}
|
||||
|
||||
skip() {
|
||||
local reason="$1"
|
||||
if [[ -n "$reason" ]]; then
|
||||
reason=": $reason"
|
||||
fi
|
||||
BATS_ENABLE_TIMING='' finish_test ' - %s (skipped%s)' "$name" "$reason"
|
||||
test_result=skip
|
||||
}
|
||||
|
||||
fail() {
|
||||
set_color 1 bold
|
||||
TIMING="${1:-}"
|
||||
finish_test ' ✗ %s' "$name"
|
||||
test_result=fail
|
||||
}
|
||||
|
||||
log() {
|
||||
case ${test_result} in
|
||||
pass)
|
||||
clear_color
|
||||
;;
|
||||
fail)
|
||||
set_color 1
|
||||
;;
|
||||
esac
|
||||
buffer ' %s\n' "$1"
|
||||
clear_color
|
||||
}
|
||||
|
||||
summary() {
|
||||
if [ "$failures" -eq 0 ] ; then
|
||||
set_color 2 bold
|
||||
else
|
||||
set_color 1 bold
|
||||
fi
|
||||
|
||||
buffer '\n%d test' "$count"
|
||||
if [[ "$count" -ne 1 ]]; then
|
||||
buffer 's'
|
||||
fi
|
||||
|
||||
buffer ', %d failure' "$failures"
|
||||
if [[ "$failures" -ne 1 ]]; then
|
||||
buffer 's'
|
||||
fi
|
||||
|
||||
if [[ "$skipped" -gt 0 ]]; then
|
||||
buffer ', %d skipped' "$skipped"
|
||||
fi
|
||||
|
||||
not_run=$((count - passed - failures - skipped))
|
||||
if [[ "$not_run" -gt 0 ]]; then
|
||||
buffer ', %d not run' "$not_run"
|
||||
fi
|
||||
|
||||
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||
buffer " in $SECONDS seconds"
|
||||
fi
|
||||
|
||||
buffer '\n'
|
||||
clear_color
|
||||
}
|
||||
|
||||
buffer_with_truncation() {
|
||||
local width="$1"
|
||||
shift
|
||||
local string
|
||||
|
||||
# shellcheck disable=SC2059
|
||||
printf -v 'string' -- "$@"
|
||||
|
||||
if [[ "${#string}" -gt "$width" ]]; then
|
||||
buffer '%s...' "${string:0:$((width - 4))}"
|
||||
else
|
||||
buffer '%s' "$string"
|
||||
fi
|
||||
}
|
||||
|
||||
move_up() {
|
||||
if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
|
||||
buffer '\x1B[%dA' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
move_down() {
|
||||
if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
|
||||
buffer '\x1B[%dB' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
go_to_column() {
|
||||
local column="$1"
|
||||
buffer '\x1B[%dG' $((column + 1))
|
||||
}
|
||||
|
||||
clear_to_end_of_line() {
|
||||
buffer '\x1B[K'
|
||||
}
|
||||
|
||||
advance() {
|
||||
clear_to_end_of_line
|
||||
buffer '\n'
|
||||
clear_color
|
||||
}
|
||||
|
||||
set_color() {
|
||||
local color="$1"
|
||||
local weight=22
|
||||
|
||||
if [[ "${2:-}" == 'bold' ]]; then
|
||||
weight=1
|
||||
fi
|
||||
buffer '\x1B[%d;%dm' "$((30 + color))" "$weight"
|
||||
}
|
||||
|
||||
clear_color() {
|
||||
buffer '\x1B[0m'
|
||||
}
|
||||
|
||||
_buffer=
|
||||
|
||||
buffer() {
|
||||
local content
|
||||
# shellcheck disable=SC2059
|
||||
printf -v content -- "$@"
|
||||
_buffer+="$content"
|
||||
}
|
||||
|
||||
prefix_buffer_with() {
|
||||
local old_buffer="$_buffer"
|
||||
_buffer=''
|
||||
"$@"
|
||||
_buffer="$_buffer$old_buffer"
|
||||
}
|
||||
|
||||
flush() {
|
||||
printf '%s' "$_buffer"
|
||||
_buffer=
|
||||
}
|
||||
|
||||
finish() {
|
||||
flush
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
trap finish EXIT
|
||||
trap '' INT
|
||||
|
||||
bats_tap_stream_plan() {
|
||||
count="$1"
|
||||
index=0
|
||||
passed=0
|
||||
failures=0
|
||||
skipped=0
|
||||
name=
|
||||
update_count_column_width
|
||||
}
|
||||
|
||||
bats_tap_stream_begin() {
|
||||
index="$1"
|
||||
name="$2"
|
||||
begin
|
||||
flush
|
||||
}
|
||||
|
||||
bats_tap_stream_ok() {
|
||||
local duration=
|
||||
if [[ "$1" == "--duration" ]]; then
|
||||
duration="$2"
|
||||
shift 2
|
||||
fi
|
||||
index="$1"
|
||||
name="$2"
|
||||
((++passed))
|
||||
|
||||
pass "$duration"
|
||||
}
|
||||
|
||||
bats_tap_stream_skipped() {
|
||||
index="$1"
|
||||
name="$2"
|
||||
((++skipped))
|
||||
skip "$3"
|
||||
}
|
||||
|
||||
bats_tap_stream_not_ok() {
|
||||
local duration=
|
||||
if [[ "$1" == "--duration" ]]; then
|
||||
duration="$2"
|
||||
shift 2
|
||||
fi
|
||||
index="$1"
|
||||
name="$2"
|
||||
((++failures))
|
||||
|
||||
fail "$duration"
|
||||
}
|
||||
|
||||
bats_tap_stream_comment() { # <comment> <scope>
|
||||
local scope=$2
|
||||
# count the lines we printed after the begin text,
|
||||
if [[ $line_backoff_count -eq 0 && $scope == begin ]]; then
|
||||
# if this is the first line after begin, go down one line
|
||||
buffer "\n"
|
||||
(( ++line_backoff_count )) # prefix-increment to avoid "error" due to returning 0
|
||||
fi
|
||||
|
||||
(( ++line_backoff_count ))
|
||||
(( line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
|
||||
log "$1"
|
||||
}
|
||||
|
||||
bats_tap_stream_suite() {
|
||||
#test_file="$1"
|
||||
line_backoff_count=0
|
||||
index=
|
||||
# indicate filename for failures
|
||||
local file_name="${1/$BASE_PATH}"
|
||||
name="File $file_name"
|
||||
set_color 4 bold
|
||||
buffer "%s\n" "$file_name"
|
||||
clear_color
|
||||
}
|
||||
|
||||
line_backoff_count=0
|
||||
bats_tap_stream_unknown() { # <full line> <scope>
|
||||
local scope=$2
|
||||
# count the lines we printed after the begin text, (or after suite, in case of syntax errors)
|
||||
if [[ $line_backoff_count -eq 0 && ( $scope == begin || $scope == suite )]]; then
|
||||
# if this is the first line after begin, go down one line
|
||||
buffer "\n"
|
||||
(( ++line_backoff_count )) # prefix-increment to avoid "error" due to returning 0
|
||||
fi
|
||||
|
||||
(( ++line_backoff_count ))
|
||||
(( line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
|
||||
buffer "%s\n" "$1"
|
||||
flush
|
||||
}
|
||||
|
||||
bats_parse_internal_extended_tap
|
||||
|
||||
summary
|
52
extras/portable/test/bats/libexec/bats-core/bats-format-tap
Executable file
52
extras/portable/test/bats/libexec/bats-core/bats-format-tap
Executable file
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
trap '' INT
|
||||
|
||||
# shellcheck source=lib/bats-core/formatter.bash
|
||||
source "$BATS_ROOT/lib/bats-core/formatter.bash"
|
||||
|
||||
bats_tap_stream_plan() {
|
||||
printf "1..%d\n" "$1"
|
||||
}
|
||||
|
||||
bats_tap_stream_begin() { #<test index> <test name>
|
||||
:
|
||||
}
|
||||
|
||||
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
|
||||
if [[ "$1" == "--duration" ]]; then
|
||||
printf "ok %d %s # in %d ms\n" "$3" "$4" "$2"
|
||||
else
|
||||
printf "ok %d %s\n" "$1" "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
|
||||
if [[ "$1" == "--duration" ]]; then
|
||||
printf "not ok %d %s # in %d ms\n" "$3" "$4" "$2"
|
||||
else
|
||||
printf "not ok %d %s\n" "$1" "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
bats_tap_stream_skipped() { # <test index> <test name> <reason>
|
||||
if [[ -n "$3" ]]; then
|
||||
printf "ok %d %s # skip %s\n" "$1" "$2" "$3"
|
||||
else
|
||||
printf "ok %d %s # skip\n" "$1" "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
bats_tap_stream_comment() { # <comment text without leading '# '>
|
||||
printf "# %s\n" "$1"
|
||||
}
|
||||
|
||||
bats_tap_stream_suite() { # <file name>
|
||||
:
|
||||
}
|
||||
|
||||
bats_tap_stream_unknown() { # <full line>
|
||||
printf "%s\n" "$1"
|
||||
}
|
||||
|
||||
bats_parse_internal_extended_tap
|
91
extras/portable/test/bats/libexec/bats-core/bats-format-tap13
Executable file
91
extras/portable/test/bats/libexec/bats-core/bats-format-tap13
Executable file
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
while [[ "$#" -ne 0 ]]; do
|
||||
case "$1" in
|
||||
-T)
|
||||
BATS_ENABLE_TIMING="-T"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
header_pattern='[0-9]+\.\.[0-9]+'
|
||||
IFS= read -r header
|
||||
|
||||
if [[ "$header" =~ $header_pattern ]]; then
|
||||
printf "TAP version 13\n"
|
||||
printf "%s\n" "$header"
|
||||
else
|
||||
# If the first line isn't a TAP plan, print it and pass the rest through
|
||||
printf '%s\n' "$header"
|
||||
exec cat
|
||||
fi
|
||||
|
||||
yaml_block_open=''
|
||||
add_yaml_entry() {
|
||||
if [[ -z "$yaml_block_open" ]]; then
|
||||
printf " ---\n"
|
||||
fi
|
||||
printf " %s: %s\n" "$1" "$2"
|
||||
yaml_block_open=1
|
||||
}
|
||||
|
||||
close_previous_yaml_block() {
|
||||
if [[ -n "$yaml_block_open" ]]; then
|
||||
printf " ...\n"
|
||||
yaml_block_open=''
|
||||
fi
|
||||
}
|
||||
|
||||
trap '' INT
|
||||
|
||||
number_of_printed_log_lines_for_this_test_so_far=0
|
||||
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
'begin '*) ;;
|
||||
'ok '*)
|
||||
close_previous_yaml_block
|
||||
number_of_printed_log_lines_for_this_test_so_far=0
|
||||
if [[ -n "${BATS_ENABLE_TIMING-}" ]]; then
|
||||
timing_expr="(ok [0-9]+ .+) in ([0-9]+)ms$"
|
||||
if [[ "$line" =~ $timing_expr ]]; then
|
||||
printf "%s\n" "${BASH_REMATCH[1]}"
|
||||
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
|
||||
else
|
||||
echo "Could not match output line to timing regex: $line" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
printf "%s\n" "${line}"
|
||||
fi
|
||||
;;
|
||||
'not ok '*)
|
||||
close_previous_yaml_block
|
||||
number_of_printed_log_lines_for_this_test_so_far=0
|
||||
timing_expr="(not ok [0-9]+ .+) in ([0-9])+ms$"
|
||||
if [[ -n "${BATS_ENABLE_TIMING-}" ]]; then
|
||||
if [[ "$line" =~ $timing_expr ]]; then
|
||||
printf "%s\n" "${BASH_REMATCH[1]}"
|
||||
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
|
||||
else
|
||||
echo "Could not match failure line to timing regex: $line" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
printf "%s\n" "${line}"
|
||||
fi
|
||||
;;
|
||||
'# '*)
|
||||
if [[ $number_of_printed_log_lines_for_this_test_so_far -eq 0 ]]; then
|
||||
add_yaml_entry "message" "|" # use a multiline string for this entry
|
||||
fi
|
||||
((++number_of_printed_log_lines_for_this_test_so_far))
|
||||
printf " %s\n" "${line:2}"
|
||||
;;
|
||||
'suite '*) ;;
|
||||
esac
|
||||
done
|
||||
# close the final block if there was one
|
||||
close_previous_yaml_block
|
60
extras/portable/test/bats/libexec/bats-core/bats-preprocess
Executable file
60
extras/portable/test/bats/libexec/bats-core/bats-preprocess
Executable file
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
bats_encode_test_name() {
|
||||
local name="$1"
|
||||
local result='test_'
|
||||
local hex_code
|
||||
|
||||
if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then
|
||||
name="${name//_/-5f}"
|
||||
name="${name//-/-2d}"
|
||||
name="${name// /_}"
|
||||
result+="$name"
|
||||
else
|
||||
local length="${#name}"
|
||||
local char i
|
||||
|
||||
for ((i = 0; i < length; i++)); do
|
||||
char="${name:$i:1}"
|
||||
if [[ "$char" == ' ' ]]; then
|
||||
result+='_'
|
||||
elif [[ "$char" =~ [[:alnum:]] ]]; then
|
||||
result+="$char"
|
||||
else
|
||||
printf -v 'hex_code' -- '-%02x' \'"$char"
|
||||
result+="$hex_code"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
printf -v "$2" '%s' "$result"
|
||||
}
|
||||
|
||||
BATS_TEST_PATTERN="^[[:blank:]]*@test[[:blank:]]+(.*[^[:blank:]])[[:blank:]]+\{(.*)\$"
|
||||
BATS_TEST_PATTERN_COMMENT="[[:blank:]]*([^[:blank:]()]+)[[:blank:]]*\(?\)?[[:blank:]]+\{[[:blank:]]+#[[:blank:]]*@test[[:blank:]]*\$"
|
||||
|
||||
test_file="$1"
|
||||
tests=()
|
||||
{
|
||||
while IFS= read -r line; do
|
||||
line="${line//$'\r'/}"
|
||||
if [[ "$line" =~ $BATS_TEST_PATTERN ]] || [[ "$line" =~ $BATS_TEST_PATTERN_COMMENT ]]; then
|
||||
name="${BASH_REMATCH[1]#[\'\"]}"
|
||||
name="${name%[\'\"]}"
|
||||
body="${BASH_REMATCH[2]:-}"
|
||||
bats_encode_test_name "$name" 'encoded_name'
|
||||
printf '%s() { bats_test_begin "%s"; %s\n' "${encoded_name:?}" "$name" "$body" || :
|
||||
|
||||
if [[ -z "$BATS_TEST_FILTER" || "$name" =~ $BATS_TEST_FILTER ]]; then
|
||||
tests+=("$encoded_name")
|
||||
fi
|
||||
else
|
||||
printf '%s\n' "$line"
|
||||
fi
|
||||
done
|
||||
} <<<"$(<"$test_file")"$'\n'
|
||||
|
||||
for test_name in "${tests[@]}"; do
|
||||
printf 'bats_test_function %s\n' "$test_name"
|
||||
done
|
197
extras/portable/test/bats_setup
Normal file
197
extras/portable/test/bats_setup
Normal file
|
@ -0,0 +1,197 @@
|
|||
# setup paths for BATS test units
|
||||
setup() {
|
||||
[ ! -f ${BATS_PARENT_TMPNAME}.skip ] || skip "skip remaining tests"
|
||||
bats_require_minimum_version 1.5.0
|
||||
R="$BATS_TEST_DIRNAME"
|
||||
# R=`cd "$T"/.. && pwd`
|
||||
TMP="$BATS_FILE_TMPDIR"
|
||||
load test_helper/bats-support/load
|
||||
load test_helper/bats-assert/load
|
||||
load test_helper/bats-file/load
|
||||
ZTMP="$BATS_FILE_TMPDIR"
|
||||
if [ "$ZENROOM_EXECUTABLE" == "" ]; then
|
||||
ZENROOM_EXECUTABLE="/usr/local/bin/zenroom"
|
||||
fi
|
||||
|
||||
PATH="$PATH:/usr/local/bin:/usr/local/sbin"
|
||||
MNT="/media/`basename $BATS_TEST_FILENAME`"
|
||||
PIM=42
|
||||
# max password stdin size to veracrypt is 128 bytes
|
||||
PW="91cd69b2caab05cb64f8e1c8ae22a7c5a25564e7fc5116d0303dce1ffd26861ea0483c25bdb85adb216718c19815eb59ac14ec9783bfbbb57786ca7d9038c845"
|
||||
# `openssl rand -hex 64`"
|
||||
FS='ext4'
|
||||
SIZE='20M'
|
||||
[ "$TOMB" = "" ] && TOMB=test.tomb
|
||||
|
||||
# default on freebsd is 1001
|
||||
user_uid=1000
|
||||
user_gid=1000
|
||||
|
||||
system="`uname -s`"
|
||||
f_create=""
|
||||
f_format=""
|
||||
f_map=""
|
||||
f_mount=""
|
||||
f_close=""
|
||||
|
||||
case "$system" in
|
||||
FreeBSD)
|
||||
f_create=portable_create
|
||||
f_format=freebsd_format
|
||||
f_map=portable_map
|
||||
f_mount=freebsd_mount
|
||||
f_close=freebsd_close
|
||||
user_uid=1001
|
||||
user_gid=1001
|
||||
;;
|
||||
Linux)
|
||||
f_create=portable_create
|
||||
f_format=linux_format
|
||||
f_map=portable_map
|
||||
f_mount=linux_mount
|
||||
f_close=portable_close
|
||||
user_uid=1000
|
||||
user_gid=1000
|
||||
;;
|
||||
*)
|
||||
>&2 echo "Unsupported system: $system"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
# cd $ZTMP
|
||||
}
|
||||
|
||||
teardown() {
|
||||
[ -n "$BATS_TEST_COMPLETED" ] || touch ${BATS_PARENT_TMPNAME}.skip
|
||||
>&3 echo
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# usage: echo PASSWORD | portable_create file size pim
|
||||
portable_create() {
|
||||
local file="$1" # must not exist
|
||||
local size="$2" # veracrypt format (accepts M or G)
|
||||
local pim="$3" # any number
|
||||
# >&2 echo "portable_create $file $size $pim"
|
||||
veracrypt --non-interactive --text --stdin \
|
||||
-m nokernelcrypto \
|
||||
-c ${file} --volume-type normal \
|
||||
--hash sha512 --encryption serpent-aes \
|
||||
--filesystem none --size ${size} --pim ${pim} \
|
||||
--random-source /dev/urandom -k ''
|
||||
return $?
|
||||
}
|
||||
|
||||
# usage: echo PASSWORD | portable_map file pim
|
||||
portable_map() {
|
||||
local file="$1"
|
||||
local pim="$2"
|
||||
# >&2 echo "portable_map $file $pim"
|
||||
veracrypt --non-interactive --text --stdin \
|
||||
--protect-hidden no -m nokernelcrypto \
|
||||
-k '' --pim ${pim} --filesystem none \
|
||||
${file}
|
||||
local loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||
return $?
|
||||
}
|
||||
|
||||
portable_close() {
|
||||
local file="$1"
|
||||
veracrypt -d ${file}
|
||||
return $?
|
||||
}
|
||||
|
||||
linux_format() {
|
||||
local file="$1"
|
||||
# loop="`losetup -j ${vera}/volume | cut -d: -f1`"
|
||||
local loop=`veracrypt -l "$FILE" | awk '{print $3}'`
|
||||
# losetup -l
|
||||
>&2 echo "veramap format: ${loop}"
|
||||
sync
|
||||
sudo mkfs.ext4 -E root_owner="${user_uid}:${user_gid}" "$loop"
|
||||
return $?
|
||||
}
|
||||
|
||||
# usage: _mount /tmp/.veracrypt_ mountpoint
|
||||
linux_mount() {
|
||||
local file="$1"
|
||||
local mnt="$2"
|
||||
# local loop="`losetup -j ${vera}/volume | cut -d: -f1`"
|
||||
local loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||
>&3 echo "fsck $loop"
|
||||
fsck.ext4 -p -C0 "$loop"
|
||||
sudo mount ${loop} "$mnt"
|
||||
return $?
|
||||
}
|
||||
|
||||
freebsd_format() {
|
||||
file="$1"
|
||||
local loop=`veracrypt -l "$FILE" | awk '{print $3}'`
|
||||
mkfs.ext4 -E root_owner="${user_uid}:${user_gid}" "${loop}"
|
||||
return $?
|
||||
}
|
||||
|
||||
freebsd_mount() {
|
||||
local file="$1"
|
||||
local mnt="$2"
|
||||
>&2 echo `veracrypt -l "$file"`
|
||||
local loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||
>&3 echo "fsck $loop"
|
||||
fsck.ext4 -p -C0 "$loop"
|
||||
lklfuse -o type=ext4 "${loop}" "$mnt"
|
||||
return $?
|
||||
}
|
||||
|
||||
freebsd_close() {
|
||||
local file="$1"
|
||||
local md=`veracrypt -l "$file" | awk '{print $3}'`
|
||||
# umount "$mnt"
|
||||
>&2 echo "md: $md"
|
||||
local mnt=`pgrep -lf "lklfuse.*$md" | awk '{print $6}'`
|
||||
>&2 echo "mnt: $mnt"
|
||||
lkl=`pgrep -f "lklfuse.*$md" | awk '{print $1}'`
|
||||
>&3 echo "kill $lkl (lklfuse on $md)"
|
||||
# trying to deail with lklfuse bug
|
||||
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=239831
|
||||
renice -20 $lkl
|
||||
kill $lkl
|
||||
sync
|
||||
sleep 1
|
||||
kill -9 $lkl
|
||||
# lkl should have really exited now
|
||||
>&3 echo "veracrypt -d $file"
|
||||
veracrypt -d "$file"
|
||||
return $?
|
||||
}
|
||||
|
||||
Z() {
|
||||
tmptmp=0
|
||||
if ! [ "$TMP" ]; then
|
||||
TMP=`mktemp -d`; tmptmp=1
|
||||
fi
|
||||
script=$1
|
||||
if [ "$script" == "-" ]; then
|
||||
cat > $TMP/script_stdin
|
||||
script=$TMP/script_stdin
|
||||
else
|
||||
if ! [ -r "$script" ]; then
|
||||
script=$T/$script
|
||||
fi
|
||||
fi
|
||||
if ! [ -r $script ]; then
|
||||
>&2 echo "Error - script not found: $script"
|
||||
return 1
|
||||
fi
|
||||
shift 1
|
||||
if [ "${1##*.}" == "zen" ]; then
|
||||
$ZENROOM_EXECUTABLE $@ -z $script
|
||||
else
|
||||
$ZENROOM_EXECUTABLE $@ $script
|
||||
fi
|
||||
if [ $tmptmp = 1 ]; then
|
||||
rm -f $TMP/*
|
||||
rmdir $TMP
|
||||
fi
|
||||
}
|
26
extras/portable/test/check-random-data.bats
Normal file
26
extras/portable/test/check-random-data.bats
Normal file
|
@ -0,0 +1,26 @@
|
|||
load bats_setup
|
||||
|
||||
@test "Find the tomb" {
|
||||
assert_file_exists "$TOMB"
|
||||
>&3 echo "Found: $TOMB"
|
||||
}
|
||||
|
||||
@test "Open tomb with key" {
|
||||
mkdir -p "$TOMB".mnt
|
||||
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
|
||||
}
|
||||
|
||||
@test "Check integrity of random data" {
|
||||
sha512sum "$TOMB".mnt/random.data | awk '{print $1}'
|
||||
cat "$TOMB".hash
|
||||
>&2 echo $newhash
|
||||
>&2 echo $oldhash
|
||||
assert_equal "$newhash" "$oldhash"
|
||||
if [ -r "$TOMB".uname ]; then
|
||||
>&3 cat "$TOMB".uname
|
||||
fi
|
||||
}
|
||||
|
||||
@test "Close tomb" {
|
||||
./tomb close "$TOMB"
|
||||
}
|
79
extras/portable/test/create-fillrandom.bats
Normal file
79
extras/portable/test/create-fillrandom.bats
Normal file
|
@ -0,0 +1,79 @@
|
|||
load bats_setup
|
||||
|
||||
# f_create=""
|
||||
# f_format=""
|
||||
# f_map=""
|
||||
# f_mount=""
|
||||
# f_close=""
|
||||
|
||||
@test "Create a new tomb" {
|
||||
# >&3 echo $PW
|
||||
>&3 echo $TOMB
|
||||
echo -n "$PW" | $f_create "$TOMB" 20M ${PIM}
|
||||
}
|
||||
|
||||
@test "Map the tomb" {
|
||||
echo -n "$PW" | $f_map "$TOMB" ${PIM}
|
||||
}
|
||||
|
||||
@test "Format the tomb" {
|
||||
$f_format "/tmp/.veracrypt_aux_mnt1" || {
|
||||
$f_close "$TOMB"
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@test "Mount the tomb" {
|
||||
mkdir -p "$MNT"
|
||||
$f_mount "$TOMB" "$MNT" || {
|
||||
$f_close "$TOMB"
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@test "Create random.data inside the tomb" {
|
||||
dd if=/dev/urandom of="$MNT"/random.data bs=1024 count=10000 || {
|
||||
# sudo umount testmnt
|
||||
$f_close "$TOMB"
|
||||
return false
|
||||
}
|
||||
>&3 ls -l "$MNT"/random.data
|
||||
sha512sum "$MNT"/random.data | awk '{print $1}' | >&3 tee "$TOMB".hash
|
||||
uname -a > "$TOMB".uname
|
||||
}
|
||||
|
||||
@test "Close the test.tomb" {
|
||||
>&3 veracrypt -l
|
||||
# sudo umount "$TMP"/testmnt
|
||||
$f_close "$TOMB"
|
||||
}
|
||||
|
||||
|
||||
@test "Re-Map the test.tomb" {
|
||||
echo -n "$PW" | $f_map "$TOMB" ${PIM}
|
||||
}
|
||||
|
||||
@test "Re-Mount the test.tomb" {
|
||||
mkdir -p "$MNT"
|
||||
$f_mount "$TOMB" "$MNT" || {
|
||||
$f_close "$TOMB"
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@test "Re-Check integrity of random data" {
|
||||
newhash=`sha512sum "$MNT"/random.data | awk '{print $1}'`
|
||||
oldhash=`cat "$TOMB".hash`
|
||||
>&2 echo $newhash
|
||||
>&2 echo $oldhash
|
||||
assert_equal "$newhash" "$oldhash"
|
||||
if [ -r "$TOMB".uname ]; then
|
||||
>&3 cat "$TOMB".uname
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@test "Re-Close the test.tomb" {
|
||||
>&3 veracrypt -l
|
||||
$f_close "$TOMB"
|
||||
}
|
51
extras/portable/test/create_open_close.bats
Normal file
51
extras/portable/test/create_open_close.bats
Normal file
|
@ -0,0 +1,51 @@
|
|||
load bats_setup
|
||||
|
||||
@test "Dig tomb" {
|
||||
rm -f "$TOMB"
|
||||
>&3 echo "$TOMB"
|
||||
./tomb dig -s 20 "$TOMB"
|
||||
}
|
||||
|
||||
@test "Forge key" {
|
||||
rm -f "$TOMB".key
|
||||
./tomb forge "$TOMB".key
|
||||
}
|
||||
|
||||
@test "Lock tomb with key" {
|
||||
./tomb lock -k "$TOMB".key "$TOMB"
|
||||
}
|
||||
|
||||
@test "Open tomb with key" {
|
||||
mkdir -p "$TOMB".mnt
|
||||
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
|
||||
}
|
||||
|
||||
@test "Create random.data inside the tomb" {
|
||||
dd if=/dev/urandom of="$TOMB".mnt/random.data bs=1024 count=10000
|
||||
>&3 ls -l "$TOMB".mnt/random.data
|
||||
sha512sum "$TOMB".mnt/random.data | awk '{print $1}' | >&3 tee "$TOMB".hash
|
||||
uname -a > "$TOMB".uname
|
||||
}
|
||||
|
||||
@test "Close tomb" {
|
||||
./tomb close "$TOMB"
|
||||
}
|
||||
|
||||
@test "Re-open tomb with key" {
|
||||
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
|
||||
}
|
||||
|
||||
@test "Check integrity of random data" {
|
||||
newhash=`sha512sum "$TOMB".mnt/random.data | awk '{print $1}'`
|
||||
oldhash=`cat "$TOMB".hash`
|
||||
>&2 echo $newhash
|
||||
>&2 echo $oldhash
|
||||
assert_equal "$newhash" "$oldhash"
|
||||
if [ -r "$TOMB".uname ]; then
|
||||
>&3 cat "$TOMB".uname
|
||||
fi
|
||||
}
|
||||
|
||||
@test "Close the tomb again" {
|
||||
./tomb close "$TOMB"
|
||||
}
|
33
extras/portable/test/test_helper/bats-assert/load.bash
Normal file
33
extras/portable/test/test_helper/bats-assert/load.bash
Normal file
|
@ -0,0 +1,33 @@
|
|||
# bats-assert - Common assertions for Bats
|
||||
#
|
||||
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||
#
|
||||
# To the extent possible under law, the author(s) have dedicated all
|
||||
# copyright and related and neighboring rights to this software to the
|
||||
# public domain worldwide. This software is distributed without any
|
||||
# warranty.
|
||||
#
|
||||
# You should have received a copy of the CC0 Public Domain Dedication
|
||||
# along with this software. If not, see
|
||||
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
#
|
||||
# Assertions are functions that perform a test and output relevant
|
||||
# information on failure to help debugging. They return 1 on failure
|
||||
# and 0 otherwise.
|
||||
#
|
||||
# All output is formatted for readability using the functions of
|
||||
# `output.bash' and sent to the standard error.
|
||||
|
||||
# shellcheck disable=1090
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/refute.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_equal.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_not_equal.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_success.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_failure.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_output.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_output.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_line.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_line.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_regex.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_regex.bash"
|
42
extras/portable/test/test_helper/bats-assert/src/assert.bash
Normal file
42
extras/portable/test/test_helper/bats-assert/src/assert.bash
Normal file
|
@ -0,0 +1,42 @@
|
|||
# assert
|
||||
# ======
|
||||
#
|
||||
# Summary: Fail if the given expression evaluates to false.
|
||||
#
|
||||
# Usage: assert <expression>
|
||||
|
||||
# Options:
|
||||
# <expression> The expression to evaluate for truthiness.
|
||||
# *__Note:__ The expression must be a simple command.
|
||||
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
|
||||
# such as `[[`, can be used only when executed with `bash -c`.*
|
||||
#
|
||||
# IO:
|
||||
# STDERR - the failed expression, on failure
|
||||
# Globals:
|
||||
# none
|
||||
# Returns:
|
||||
# 0 - if expression evaluates to true
|
||||
# 1 - otherwise
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert()' {
|
||||
# touch '/var/log/test.log'
|
||||
# assert [ -e '/var/log/test.log' ]
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the failed expression is displayed.
|
||||
#
|
||||
# ```
|
||||
# -- assertion failed --
|
||||
# expression : [ -e /var/log/test.log ]
|
||||
# --
|
||||
# ```
|
||||
assert() {
|
||||
if ! "$@"; then
|
||||
batslib_print_kv_single 10 'expression' "$*" \
|
||||
| batslib_decorate 'assertion failed' \
|
||||
| fail
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
# assert_equal
|
||||
# ============
|
||||
#
|
||||
# Summary: Fail if the actual and expected values are not equal.
|
||||
#
|
||||
# Usage: assert_equal <actual> <expected>
|
||||
#
|
||||
# Options:
|
||||
# <actual> The value being compared.
|
||||
# <expected> The value to compare against.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_equal()' {
|
||||
# assert_equal 'have' 'want'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# IO:
|
||||
# STDERR - expected and actual values, on failure
|
||||
# Globals:
|
||||
# none
|
||||
# Returns:
|
||||
# 0 - if values equal
|
||||
# 1 - otherwise
|
||||
#
|
||||
# On failure, the expected and actual values are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- values do not equal --
|
||||
# expected : want
|
||||
# actual : have
|
||||
# --
|
||||
# ```
|
||||
assert_equal() {
|
||||
if [[ $1 != "$2" ]]; then
|
||||
batslib_print_kv_single_or_multi 8 \
|
||||
'expected' "$2" \
|
||||
'actual' "$1" \
|
||||
| batslib_decorate 'values do not equal' \
|
||||
| fail
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
# assert_failure
|
||||
# ==============
|
||||
#
|
||||
# Summary: Fail if `$status` is 0; or is not equal to the optionally provided status.
|
||||
#
|
||||
# Usage: assert_failure [<expected_status>]
|
||||
#
|
||||
# Options:
|
||||
# <expected_status> The specific status code to check against.
|
||||
# If not provided, simply asserts status is != 0.
|
||||
#
|
||||
# IO:
|
||||
# STDERR - `$output`, on failure;
|
||||
# - also, `$status` and `expected_status`, if provided
|
||||
# Globals:
|
||||
# status
|
||||
# output
|
||||
# Returns:
|
||||
# 0 - if `$status' is 0,
|
||||
# or if expected_status is provided but does not equal `$status'
|
||||
# 1 - otherwise
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_failure() status only' {
|
||||
# run echo 'Success!'
|
||||
# assert_failure
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, `$output` is displayed.
|
||||
#
|
||||
# ```
|
||||
# -- command succeeded, but it was expected to fail --
|
||||
# output : Success!
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Expected status
|
||||
#
|
||||
# When `expected_status` is provided, fail if `$status` does not equal the `expected_status`.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_failure() with expected status' {
|
||||
# run bash -c "echo 'Error!'; exit 1"
|
||||
# assert_failure 2
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, both the expected and actual statuses, and `$output` are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- command failed as expected, but status differs --
|
||||
# expected : 2
|
||||
# actual : 1
|
||||
# output : Error!
|
||||
# --
|
||||
# ```
|
||||
assert_failure() {
|
||||
: "${output?}"
|
||||
: "${status?}"
|
||||
|
||||
(( $# > 0 )) && local -r expected="$1"
|
||||
if (( status == 0 )); then
|
||||
batslib_print_kv_single_or_multi 6 'output' "$output" \
|
||||
| batslib_decorate 'command succeeded, but it was expected to fail' \
|
||||
| fail
|
||||
elif (( $# > 0 )) && (( status != expected )); then
|
||||
{ local -ir width=8
|
||||
batslib_print_kv_single "$width" \
|
||||
'expected' "$expected" \
|
||||
'actual' "$status"
|
||||
batslib_print_kv_single_or_multi "$width" \
|
||||
'output' "$output"
|
||||
} \
|
||||
| batslib_decorate 'command failed as expected, but status differs' \
|
||||
| fail
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
# assert_line
|
||||
# ===========
|
||||
#
|
||||
# Summary: Fail if the expected line is not found in the output (default) or at a specific line number.
|
||||
#
|
||||
# Usage: assert_line [-n index] [-p | -e] [--] <expected>
|
||||
#
|
||||
# Options:
|
||||
# -n, --index <idx> Match the <idx>th line
|
||||
# -p, --partial Match if `expected` is a substring of `$output` or line <idx>
|
||||
# -e, --regexp Treat `expected` as an extended regular expression
|
||||
# <expected> The expected line string, substring, or regular expression
|
||||
#
|
||||
# IO:
|
||||
# STDERR - details, on failure
|
||||
# error message, on error
|
||||
# Globals:
|
||||
# output
|
||||
# lines
|
||||
# Returns:
|
||||
# 0 - if matching line found
|
||||
# 1 - otherwise
|
||||
#
|
||||
# Similarly to `assert_output`, this function verifies that a command or function produces the expected output.
|
||||
# (It is the logical complement of `refute_line`.)
|
||||
# It checks that the expected line appears in the output (default) or at a specific line number.
|
||||
# Matching can be literal (default), partial or regular expression.
|
||||
#
|
||||
# *__Warning:__
|
||||
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
|
||||
# causing line indices to change and preventing testing for empty lines.*
|
||||
#
|
||||
# [bats-93]: https://github.com/sstephenson/bats/pull/93
|
||||
#
|
||||
# ## Looking for a line in the output
|
||||
#
|
||||
# By default, the entire output is searched for the expected line.
|
||||
# The assertion fails if the expected line is not found in `${lines[@]}`.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_line() looking for line' {
|
||||
# run echo $'have-0\nhave-1\nhave-2'
|
||||
# assert_line 'want'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the expected line and the output are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- output does not contain line --
|
||||
# line : want
|
||||
# output (3 lines):
|
||||
# have-0
|
||||
# have-1
|
||||
# have-2
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Matching a specific line
|
||||
#
|
||||
# When the `--index <idx>` option is used (`-n <idx>` for short), the expected line is matched only against the line identified by the given index.
|
||||
# The assertion fails if the expected line does not equal `${lines[<idx>]}`.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_line() specific line' {
|
||||
# run echo $'have-0\nhave-1\nhave-2'
|
||||
# assert_line --index 1 'want-1'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the index and the compared lines are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- line differs --
|
||||
# index : 1
|
||||
# expected : want-1
|
||||
# actual : have-1
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Partial matching
|
||||
#
|
||||
# Partial matching can be enabled with the `--partial` option (`-p` for short).
|
||||
# When used, a match fails if the expected *substring* is not found in the matched line.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_line() partial matching' {
|
||||
# run echo $'have 1\nhave 2\nhave 3'
|
||||
# assert_line --partial 'want'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line.
|
||||
#
|
||||
# ```
|
||||
# -- no output line contains substring --
|
||||
# substring : want
|
||||
# output (3 lines):
|
||||
# have 1
|
||||
# have 2
|
||||
# have 3
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Regular expression matching
|
||||
#
|
||||
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
|
||||
# When used, a match fails if the *extended regular expression* does not match the line being tested.
|
||||
#
|
||||
# *__Note__:
|
||||
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_line() regular expression matching' {
|
||||
# run echo $'have-0\nhave-1\nhave-2'
|
||||
# assert_line --index 1 --regexp '^want-[0-9]$'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the same details are displayed as for literal matching, except that the regular expression replaces the expected line.
|
||||
#
|
||||
# ```
|
||||
# -- regular expression does not match line --
|
||||
# index : 1
|
||||
# regexp : ^want-[0-9]$
|
||||
# line : have-1
|
||||
# --
|
||||
# ```
|
||||
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
|
||||
assert_line() {
|
||||
local -i is_match_line=0
|
||||
local -i is_mode_partial=0
|
||||
local -i is_mode_regexp=0
|
||||
: "${lines?}"
|
||||
|
||||
# Handle options.
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-n|--index)
|
||||
if (( $# < 2 )) || ! [[ $2 =~ ^-?([0-9]|[1-9][0-9]+)$ ]]; then
|
||||
echo "\`--index' requires an integer argument: \`$2'" \
|
||||
| batslib_decorate 'ERROR: assert_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
is_match_line=1
|
||||
local -ri idx="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--partial) is_mode_partial=1; shift ;;
|
||||
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||
| batslib_decorate 'ERROR: assert_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Arguments.
|
||||
local -r expected="$1"
|
||||
|
||||
if (( is_mode_regexp == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then
|
||||
echo "Invalid extended regular expression: \`$expected'" \
|
||||
| batslib_decorate 'ERROR: assert_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Matching.
|
||||
if (( is_match_line )); then
|
||||
# Specific line.
|
||||
if (( is_mode_regexp )); then
|
||||
if ! [[ ${lines[$idx]} =~ $expected ]]; then
|
||||
batslib_print_kv_single 6 \
|
||||
'index' "$idx" \
|
||||
'regexp' "$expected" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'regular expression does not match line' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_partial )); then
|
||||
if [[ ${lines[$idx]} != *"$expected"* ]]; then
|
||||
batslib_print_kv_single 9 \
|
||||
'index' "$idx" \
|
||||
'substring' "$expected" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'line does not contain substring' \
|
||||
| fail
|
||||
fi
|
||||
else
|
||||
if [[ ${lines[$idx]} != "$expected" ]]; then
|
||||
batslib_print_kv_single 8 \
|
||||
'index' "$idx" \
|
||||
'expected' "$expected" \
|
||||
'actual' "${lines[$idx]}" \
|
||||
| batslib_decorate 'line differs' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Contained in output.
|
||||
if (( is_mode_regexp )); then
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
[[ ${lines[$idx]} =~ $expected ]] && return 0
|
||||
done
|
||||
{ local -ar single=( 'regexp' "$expected" )
|
||||
local -ar may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
|
||||
} \
|
||||
| batslib_decorate 'no output line matches regular expression' \
|
||||
| fail
|
||||
elif (( is_mode_partial )); then
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
[[ ${lines[$idx]} == *"$expected"* ]] && return 0
|
||||
done
|
||||
{ local -ar single=( 'substring' "$expected" )
|
||||
local -ar may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
|
||||
} \
|
||||
| batslib_decorate 'no output line contains substring' \
|
||||
| fail
|
||||
else
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
[[ ${lines[$idx]} == "$expected" ]] && return 0
|
||||
done
|
||||
{ local -ar single=( 'line' "$expected" )
|
||||
local -ar may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
|
||||
} \
|
||||
| batslib_decorate 'output does not contain line' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
# assert_not_equal
|
||||
# ============
|
||||
#
|
||||
# Summary: Fail if the actual and unexpected values are equal.
|
||||
#
|
||||
# Usage: assert_not_equal <actual> <unexpected>
|
||||
#
|
||||
# Options:
|
||||
# <actual> The value being compared.
|
||||
# <unexpected> The value to compare against.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_not_equal()' {
|
||||
# assert_not_equal 'foo' 'foo'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# IO:
|
||||
# STDERR - expected and actual values, on failure
|
||||
# Globals:
|
||||
# none
|
||||
# Returns:
|
||||
# 0 - if actual does not equal unexpected
|
||||
# 1 - otherwise
|
||||
#
|
||||
# On failure, the unexpected and actual values are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- values should not be equal --
|
||||
# unexpected : foo
|
||||
# actual : foo
|
||||
# --
|
||||
# ```
|
||||
assert_not_equal() {
|
||||
if [[ "$1" == "$2" ]]; then
|
||||
batslib_print_kv_single_or_multi 10 \
|
||||
'unexpected' "$2" \
|
||||
'actual' "$1" \
|
||||
| batslib_decorate 'values should not be equal' \
|
||||
| fail
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
# assert_output
|
||||
# =============
|
||||
#
|
||||
# Summary: Fail if `$output' does not match the expected output.
|
||||
#
|
||||
# Usage: assert_output [-p | -e] [- | [--] <expected>]
|
||||
#
|
||||
# Options:
|
||||
# -p, --partial Match if `expected` is a substring of `$output`
|
||||
# -e, --regexp Treat `expected` as an extended regular expression
|
||||
# -, --stdin Read `expected` value from STDIN
|
||||
# <expected> The expected value, substring or regular expression
|
||||
#
|
||||
# IO:
|
||||
# STDIN - [=$1] expected output
|
||||
# STDERR - details, on failure
|
||||
# error message, on error
|
||||
# Globals:
|
||||
# output
|
||||
# Returns:
|
||||
# 0 - if output matches the expected value/partial/regexp
|
||||
# 1 - otherwise
|
||||
#
|
||||
# This function verifies that a command or function produces the expected output.
|
||||
# (It is the logical complement of `refute_output`.)
|
||||
# Output matching can be literal (the default), partial or by regular expression.
|
||||
# The expected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
|
||||
#
|
||||
# ## Literal matching
|
||||
#
|
||||
# By default, literal matching is performed.
|
||||
# The assertion fails if `$output` does not equal the expected output.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_output()' {
|
||||
# run echo 'have'
|
||||
# assert_output 'want'
|
||||
# }
|
||||
#
|
||||
# @test 'assert_output() with pipe' {
|
||||
# run echo 'hello'
|
||||
# echo 'hello' | assert_output -
|
||||
# }
|
||||
#
|
||||
# @test 'assert_output() with herestring' {
|
||||
# run echo 'hello'
|
||||
# assert_output - <<< hello
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the expected and actual output are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- output differs --
|
||||
# expected : want
|
||||
# actual : have
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Existence
|
||||
#
|
||||
# To assert that any output exists at all, omit the `expected` argument.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_output()' {
|
||||
# run echo 'have'
|
||||
# assert_output
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, an error message is displayed.
|
||||
#
|
||||
# ```
|
||||
# -- no output --
|
||||
# expected non-empty output, but output was empty
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Partial matching
|
||||
#
|
||||
# Partial matching can be enabled with the `--partial` option (`-p` for short).
|
||||
# When used, the assertion fails if the expected _substring_ is not found in `$output`.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_output() partial matching' {
|
||||
# run echo 'ERROR: no such file or directory'
|
||||
# assert_output --partial 'SUCCESS'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the substring and the output are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- output does not contain substring --
|
||||
# substring : SUCCESS
|
||||
# output : ERROR: no such file or directory
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Regular expression matching
|
||||
#
|
||||
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
|
||||
# When used, the assertion fails if the *extended regular expression* does not match `$output`.
|
||||
#
|
||||
# *__Note__:
|
||||
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
|
||||
# not individual lines.*
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_output() regular expression matching' {
|
||||
# run echo 'Foobar 0.1.0'
|
||||
# assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the regular expression and the output are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- regular expression does not match output --
|
||||
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
|
||||
# output : Foobar 0.1.0
|
||||
# --
|
||||
# ```
|
||||
assert_output() {
|
||||
local -i is_mode_partial=0
|
||||
local -i is_mode_regexp=0
|
||||
local -i is_mode_nonempty=0
|
||||
local -i use_stdin=0
|
||||
: "${output?}"
|
||||
|
||||
# Handle options.
|
||||
if (( $# == 0 )); then
|
||||
is_mode_nonempty=1
|
||||
fi
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-p|--partial) is_mode_partial=1; shift ;;
|
||||
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||
-|--stdin) use_stdin=1; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||
| batslib_decorate 'ERROR: assert_output' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Arguments.
|
||||
local expected
|
||||
if (( use_stdin )); then
|
||||
expected="$(cat -)"
|
||||
else
|
||||
expected="${1-}"
|
||||
fi
|
||||
|
||||
# Matching.
|
||||
if (( is_mode_nonempty )); then
|
||||
if [ -z "$output" ]; then
|
||||
echo 'expected non-empty output, but output was empty' \
|
||||
| batslib_decorate 'no output' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_regexp )); then
|
||||
if [[ '' =~ $expected ]] || (( $? == 2 )); then
|
||||
echo "Invalid extended regular expression: \`$expected'" \
|
||||
| batslib_decorate 'ERROR: assert_output' \
|
||||
| fail
|
||||
elif ! [[ $output =~ $expected ]]; then
|
||||
batslib_print_kv_single_or_multi 6 \
|
||||
'regexp' "$expected" \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'regular expression does not match output' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_partial )); then
|
||||
if [[ $output != *"$expected"* ]]; then
|
||||
batslib_print_kv_single_or_multi 9 \
|
||||
'substring' "$expected" \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'output does not contain substring' \
|
||||
| fail
|
||||
fi
|
||||
else
|
||||
if [[ $output != "$expected" ]]; then
|
||||
batslib_print_kv_single_or_multi 8 \
|
||||
'expected' "$expected" \
|
||||
'actual' "$output" \
|
||||
| batslib_decorate 'output differs' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
# `assert_regex`
|
||||
#
|
||||
# This function is similar to `assert_equal` but uses pattern matching instead
|
||||
# of equality, by wrapping `[[ value =~ pattern ]]`.
|
||||
#
|
||||
# Fail if the value (first parameter) does not match the pattern (second
|
||||
# parameter).
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_regex()' {
|
||||
# assert_regex 'what' 'x$'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the value and the pattern are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- values does not match regular expression --
|
||||
# value : what
|
||||
# pattern : x$
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# If the value is longer than one line then it is displayed in *multi-line*
|
||||
# format.
|
||||
#
|
||||
# An error is displayed if the specified extended regular expression is invalid.
|
||||
#
|
||||
# For description of the matching behavior, refer to the documentation of the
|
||||
# `=~` operator in the
|
||||
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
|
||||
# Note that the `BASH_REMATCH` array is available immediately after the
|
||||
# assertion succeeds but is fragile, i.e. prone to being overwritten as a side
|
||||
# effect of other actions.
|
||||
assert_regex() {
|
||||
local -r value="${1}"
|
||||
local -r pattern="${2}"
|
||||
|
||||
if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then
|
||||
echo "Invalid extended regular expression: \`${pattern}'" \
|
||||
| batslib_decorate 'ERROR: assert_regex' \
|
||||
| fail
|
||||
elif ! [[ "${value}" =~ ${pattern} ]]; then
|
||||
if shopt -p nocasematch &>/dev/null; then
|
||||
local case_sensitive=insensitive
|
||||
else
|
||||
local case_sensitive=sensitive
|
||||
fi
|
||||
batslib_print_kv_single_or_multi 8 \
|
||||
'value' "${value}" \
|
||||
'pattern' "${pattern}" \
|
||||
'case' "${case_sensitive}" \
|
||||
| batslib_decorate 'value does not match regular expression' \
|
||||
| fail
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
# assert_success
|
||||
# ==============
|
||||
#
|
||||
# Summary: Fail if `$status` is not 0.
|
||||
#
|
||||
# Usage: assert_success
|
||||
#
|
||||
# IO:
|
||||
# STDERR - `$status` and `$output`, on failure
|
||||
# Globals:
|
||||
# status
|
||||
# output
|
||||
# Returns:
|
||||
# 0 - if `$status' is 0
|
||||
# 1 - otherwise
|
||||
#
|
||||
# ```bash
|
||||
# @test 'assert_success() status only' {
|
||||
# run bash -c "echo 'Error!'; exit 1"
|
||||
# assert_success
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, `$status` and `$output` are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- command failed --
|
||||
# status : 1
|
||||
# output : Error!
|
||||
# --
|
||||
# ```
|
||||
assert_success() {
|
||||
: "${output?}"
|
||||
: "${status?}"
|
||||
|
||||
if (( status != 0 )); then
|
||||
{ local -ir width=6
|
||||
batslib_print_kv_single "$width" 'status' "$status"
|
||||
batslib_print_kv_single_or_multi "$width" 'output' "$output"
|
||||
} \
|
||||
| batslib_decorate 'command failed' \
|
||||
| fail
|
||||
fi
|
||||
}
|
42
extras/portable/test/test_helper/bats-assert/src/refute.bash
Normal file
42
extras/portable/test/test_helper/bats-assert/src/refute.bash
Normal file
|
@ -0,0 +1,42 @@
|
|||
# refute
|
||||
# ======
|
||||
#
|
||||
# Summary: Fail if the given expression evaluates to true.
|
||||
#
|
||||
# Usage: refute <expression>
|
||||
#
|
||||
# Options:
|
||||
# <expression> The expression to evaluate for falsiness.
|
||||
# *__Note:__ The expression must be a simple command.
|
||||
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
|
||||
# such as `[[`, can be used only when executed with `bash -c`.*
|
||||
#
|
||||
# IO:
|
||||
# STDERR - the successful expression, on failure
|
||||
# Globals:
|
||||
# none
|
||||
# Returns:
|
||||
# 0 - if expression evaluates to false
|
||||
# 1 - otherwise
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute()' {
|
||||
# rm -f '/var/log/test.log'
|
||||
# refute [ -e '/var/log/test.log' ]
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the successful expression is displayed.
|
||||
#
|
||||
# ```
|
||||
# -- assertion succeeded, but it was expected to fail --
|
||||
# expression : [ -e /var/log/test.log ]
|
||||
# --
|
||||
# ```
|
||||
refute() {
|
||||
if "$@"; then
|
||||
batslib_print_kv_single 10 'expression' "$*" \
|
||||
| batslib_decorate 'assertion succeeded, but it was expected to fail' \
|
||||
| fail
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
# refute_line
|
||||
# ===========
|
||||
#
|
||||
# Summary: Fail if the unexpected line is found in the output (default) or at a specific line number.
|
||||
#
|
||||
# Usage: refute_line [-n index] [-p | -e] [--] <unexpected>
|
||||
#
|
||||
# Options:
|
||||
# -n, --index <idx> Match the <idx>th line
|
||||
# -p, --partial Match if `unexpected` is a substring of `$output` or line <idx>
|
||||
# -e, --regexp Treat `unexpected` as an extended regular expression
|
||||
# <unexpected> The unexpected line string, substring, or regular expression.
|
||||
#
|
||||
# IO:
|
||||
# STDERR - details, on failure
|
||||
# error message, on error
|
||||
# Globals:
|
||||
# output
|
||||
# lines
|
||||
# Returns:
|
||||
# 0 - if match not found
|
||||
# 1 - otherwise
|
||||
#
|
||||
# Similarly to `refute_output`, this function verifies that a command or function does not produce the unexpected output.
|
||||
# (It is the logical complement of `assert_line`.)
|
||||
# It checks that the unexpected line does not appear in the output (default) or at a specific line number.
|
||||
# Matching can be literal (default), partial or regular expression.
|
||||
#
|
||||
# *__Warning:__
|
||||
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
|
||||
# causing line indices to change and preventing testing for empty lines.*
|
||||
#
|
||||
# [bats-93]: https://github.com/sstephenson/bats/pull/93
|
||||
#
|
||||
# ## Looking for a line in the output
|
||||
#
|
||||
# By default, the entire output is searched for the unexpected line.
|
||||
# The assertion fails if the unexpected line is found in `${lines[@]}`.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute_line() looking for line' {
|
||||
# run echo $'have-0\nwant\nhave-2'
|
||||
# refute_line 'want'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the unexpected line, the index of its first match and the output with the matching line highlighted are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- line should not be in output --
|
||||
# line : want
|
||||
# index : 1
|
||||
# output (3 lines):
|
||||
# have-0
|
||||
# > want
|
||||
# have-2
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Matching a specific line
|
||||
#
|
||||
# When the `--index <idx>` option is used (`-n <idx>` for short), the unexpected line is matched only against the line identified by the given index.
|
||||
# The assertion fails if the unexpected line equals `${lines[<idx>]}`.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute_line() specific line' {
|
||||
# run echo $'have-0\nwant-1\nhave-2'
|
||||
# refute_line --index 1 'want-1'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the index and the unexpected line are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- line should differ --
|
||||
# index : 1
|
||||
# line : want-1
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Partial matching
|
||||
#
|
||||
# Partial matching can be enabled with the `--partial` option (`-p` for short).
|
||||
# When used, a match fails if the unexpected *substring* is found in the matched line.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute_line() partial matching' {
|
||||
# run echo $'have 1\nwant 2\nhave 3'
|
||||
# refute_line --partial 'want'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, in addition to the details of literal matching, the substring is also displayed.
|
||||
# When used with `--index <idx>` the substring replaces the unexpected line.
|
||||
#
|
||||
# ```
|
||||
# -- no line should contain substring --
|
||||
# substring : want
|
||||
# index : 1
|
||||
# output (3 lines):
|
||||
# have 1
|
||||
# > want 2
|
||||
# have 3
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Regular expression matching
|
||||
#
|
||||
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
|
||||
# When used, a match fails if the *extended regular expression* matches the line being tested.
|
||||
#
|
||||
# *__Note__:
|
||||
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute_line() regular expression matching' {
|
||||
# run echo $'Foobar v0.1.0\nRelease date: 2015-11-29'
|
||||
# refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, in addition to the details of literal matching, the regular expression is also displayed.
|
||||
# When used with `--index <idx>` the regular expression replaces the unexpected line.
|
||||
#
|
||||
# ```
|
||||
# -- regular expression should not match line --
|
||||
# index : 0
|
||||
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
|
||||
# line : Foobar v0.1.0
|
||||
# --
|
||||
# ```
|
||||
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
|
||||
refute_line() {
|
||||
local -i is_match_line=0
|
||||
local -i is_mode_partial=0
|
||||
local -i is_mode_regexp=0
|
||||
: "${lines?}"
|
||||
|
||||
# Handle options.
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-n|--index)
|
||||
if (( $# < 2 )) || ! [[ $2 =~ ^-?([0-9]|[1-9][0-9]+)$ ]]; then
|
||||
echo "\`--index' requires an integer argument: \`$2'" \
|
||||
| batslib_decorate 'ERROR: refute_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
is_match_line=1
|
||||
local -ri idx="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--partial) is_mode_partial=1; shift ;;
|
||||
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||
| batslib_decorate 'ERROR: refute_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Arguments.
|
||||
local -r unexpected="$1"
|
||||
|
||||
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
|
||||
echo "Invalid extended regular expression: \`$unexpected'" \
|
||||
| batslib_decorate 'ERROR: refute_line' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Matching.
|
||||
if (( is_match_line )); then
|
||||
# Specific line.
|
||||
if (( is_mode_regexp )); then
|
||||
if [[ ${lines[$idx]} =~ $unexpected ]]; then
|
||||
batslib_print_kv_single 6 \
|
||||
'index' "$idx" \
|
||||
'regexp' "$unexpected" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'regular expression should not match line' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_partial )); then
|
||||
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
|
||||
batslib_print_kv_single 9 \
|
||||
'index' "$idx" \
|
||||
'substring' "$unexpected" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'line should not contain substring' \
|
||||
| fail
|
||||
fi
|
||||
else
|
||||
if [[ ${lines[$idx]} == "$unexpected" ]]; then
|
||||
batslib_print_kv_single 5 \
|
||||
'index' "$idx" \
|
||||
'line' "${lines[$idx]}" \
|
||||
| batslib_decorate 'line should differ' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Line contained in output.
|
||||
if (( is_mode_regexp )); then
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
if [[ ${lines[$idx]} =~ $unexpected ]]; then
|
||||
{ local -ar single=( 'regexp' "$unexpected" 'index' "$idx" )
|
||||
local -a may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
if batslib_is_single_line "${may_be_multi[1]}"; then
|
||||
batslib_print_kv_single "$width" "${may_be_multi[@]}"
|
||||
else
|
||||
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
|
||||
batslib_print_kv_multi "${may_be_multi[@]}"
|
||||
fi
|
||||
} \
|
||||
| batslib_decorate 'no line should match the regular expression' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
done
|
||||
elif (( is_mode_partial )); then
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
|
||||
{ local -ar single=( 'substring' "$unexpected" 'index' "$idx" )
|
||||
local -a may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
if batslib_is_single_line "${may_be_multi[1]}"; then
|
||||
batslib_print_kv_single "$width" "${may_be_multi[@]}"
|
||||
else
|
||||
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
|
||||
batslib_print_kv_multi "${may_be_multi[@]}"
|
||||
fi
|
||||
} \
|
||||
| batslib_decorate 'no line should contain substring' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
done
|
||||
else
|
||||
local -i idx
|
||||
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||
if [[ ${lines[$idx]} == "$unexpected" ]]; then
|
||||
{ local -ar single=( 'line' "$unexpected" 'index' "$idx" )
|
||||
local -a may_be_multi=( 'output' "$output" )
|
||||
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||
batslib_print_kv_single "$width" "${single[@]}"
|
||||
if batslib_is_single_line "${may_be_multi[1]}"; then
|
||||
batslib_print_kv_single "$width" "${may_be_multi[@]}"
|
||||
else
|
||||
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
|
||||
batslib_print_kv_multi "${may_be_multi[@]}"
|
||||
fi
|
||||
} \
|
||||
| batslib_decorate 'line should not be in output' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
# refute_output
|
||||
# =============
|
||||
#
|
||||
# Summary: Fail if `$output' matches the unexpected output.
|
||||
#
|
||||
# Usage: refute_output [-p | -e] [- | [--] <unexpected>]
|
||||
#
|
||||
# Options:
|
||||
# -p, --partial Match if `unexpected` is a substring of `$output`
|
||||
# -e, --regexp Treat `unexpected` as an extended regular expression
|
||||
# -, --stdin Read `unexpected` value from STDIN
|
||||
# <unexpected> The unexpected value, substring, or regular expression
|
||||
#
|
||||
# IO:
|
||||
# STDIN - [=$1] unexpected output
|
||||
# STDERR - details, on failure
|
||||
# error message, on error
|
||||
# Globals:
|
||||
# output
|
||||
# Returns:
|
||||
# 0 - if output matches the unexpected value/partial/regexp
|
||||
# 1 - otherwise
|
||||
#
|
||||
# This function verifies that a command or function does not produce the unexpected output.
|
||||
# (It is the logical complement of `assert_output`.)
|
||||
# Output matching can be literal (the default), partial or by regular expression.
|
||||
# The unexpected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
|
||||
#
|
||||
# ## Literal matching
|
||||
#
|
||||
# By default, literal matching is performed.
|
||||
# The assertion fails if `$output` equals the unexpected output.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute_output()' {
|
||||
# run echo 'want'
|
||||
# refute_output 'want'
|
||||
# }
|
||||
#
|
||||
# @test 'refute_output() with pipe' {
|
||||
# run echo 'hello'
|
||||
# echo 'world' | refute_output -
|
||||
# }
|
||||
#
|
||||
# @test 'refute_output() with herestring' {
|
||||
# run echo 'hello'
|
||||
# refute_output - <<< world
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the output is displayed.
|
||||
#
|
||||
# ```
|
||||
# -- output equals, but it was expected to differ --
|
||||
# output : want
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Existence
|
||||
#
|
||||
# To assert that there is no output at all, omit the matching argument.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute_output()' {
|
||||
# run foo --silent
|
||||
# refute_output
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, an error message is displayed.
|
||||
#
|
||||
# ```
|
||||
# -- unexpected output --
|
||||
# expected no output, but output was non-empty
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Partial matching
|
||||
#
|
||||
# Partial matching can be enabled with the `--partial` option (`-p` for short).
|
||||
# When used, the assertion fails if the unexpected _substring_ is found in `$output`.
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute_output() partial matching' {
|
||||
# run echo 'ERROR: no such file or directory'
|
||||
# refute_output --partial 'ERROR'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the substring and the output are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- output should not contain substring --
|
||||
# substring : ERROR
|
||||
# output : ERROR: no such file or directory
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# ## Regular expression matching
|
||||
#
|
||||
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
|
||||
# When used, the assertion fails if the *extended regular expression* matches `$output`.
|
||||
#
|
||||
# *__Note__:
|
||||
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
|
||||
# not individual lines.*
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute_output() regular expression matching' {
|
||||
# run echo 'Foobar v0.1.0'
|
||||
# refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the regular expression and the output are displayed.
|
||||
#
|
||||
# ```
|
||||
# -- regular expression should not match output --
|
||||
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
|
||||
# output : Foobar v0.1.0
|
||||
# --
|
||||
# ```
|
||||
refute_output() {
|
||||
local -i is_mode_partial=0
|
||||
local -i is_mode_regexp=0
|
||||
local -i is_mode_empty=0
|
||||
local -i use_stdin=0
|
||||
: "${output?}"
|
||||
|
||||
# Handle options.
|
||||
if (( $# == 0 )); then
|
||||
is_mode_empty=1
|
||||
fi
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-p|--partial) is_mode_partial=1; shift ;;
|
||||
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||
-|--stdin) use_stdin=1; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||
| batslib_decorate 'ERROR: refute_output' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Arguments.
|
||||
local unexpected
|
||||
if (( use_stdin )); then
|
||||
unexpected="$(cat -)"
|
||||
else
|
||||
unexpected="${1-}"
|
||||
fi
|
||||
|
||||
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
|
||||
echo "Invalid extended regular expression: \`$unexpected'" \
|
||||
| batslib_decorate 'ERROR: refute_output' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Matching.
|
||||
if (( is_mode_empty )); then
|
||||
if [ -n "$output" ]; then
|
||||
batslib_print_kv_single_or_multi 6 \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'output non-empty, but expected no output' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_regexp )); then
|
||||
if [[ $output =~ $unexpected ]]; then
|
||||
batslib_print_kv_single_or_multi 6 \
|
||||
'regexp' "$unexpected" \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'regular expression should not match output' \
|
||||
| fail
|
||||
fi
|
||||
elif (( is_mode_partial )); then
|
||||
if [[ $output == *"$unexpected"* ]]; then
|
||||
batslib_print_kv_single_or_multi 9 \
|
||||
'substring' "$unexpected" \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'output should not contain substring' \
|
||||
| fail
|
||||
fi
|
||||
else
|
||||
if [[ $output == "$unexpected" ]]; then
|
||||
batslib_print_kv_single_or_multi 6 \
|
||||
'output' "$output" \
|
||||
| batslib_decorate 'output equals, but it was expected to differ' \
|
||||
| fail
|
||||
fi
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
# `refute_regex`
|
||||
#
|
||||
# This function is similar to `refute_equal` but uses pattern matching instead
|
||||
# of equality, by wrapping `! [[ value =~ pattern ]]`.
|
||||
#
|
||||
# Fail if the value (first parameter) matches the pattern (second parameter).
|
||||
#
|
||||
# ```bash
|
||||
# @test 'refute_regex()' {
|
||||
# refute_regex 'WhatsApp' 'Threema'
|
||||
# }
|
||||
# ```
|
||||
#
|
||||
# On failure, the value, the pattern and the match are displayed.
|
||||
#
|
||||
# ```
|
||||
# @test 'refute_regex()' {
|
||||
# refute_regex 'WhatsApp' 'What.'
|
||||
# }
|
||||
#
|
||||
# -- value matches regular expression --
|
||||
# value : WhatsApp
|
||||
# pattern : What.
|
||||
# match : Whats
|
||||
# case : sensitive
|
||||
# --
|
||||
# ```
|
||||
#
|
||||
# If the value or pattern is longer than one line then it is displayed in
|
||||
# *multi-line* format.
|
||||
#
|
||||
# An error is displayed if the specified extended regular expression is invalid.
|
||||
#
|
||||
# For description of the matching behavior, refer to the documentation of the
|
||||
# `=~` operator in the
|
||||
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
|
||||
#
|
||||
# Note that the `BASH_REMATCH` array is available immediately after the
|
||||
# assertion fails but is fragile, i.e. prone to being overwritten as a side
|
||||
# effect of other actions like calling `run`. Thus, it's good practice to avoid
|
||||
# using `BASH_REMATCH` in conjunction with `refute_regex()`. The valuable
|
||||
# information the array contains is the matching part of the value which is
|
||||
# printed in the failing test log, as mentioned above.
|
||||
refute_regex() {
|
||||
local -r value="${1}"
|
||||
local -r pattern="${2}"
|
||||
|
||||
if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then
|
||||
echo "Invalid extended regular expression: \`${pattern}'" \
|
||||
| batslib_decorate 'ERROR: refute_regex' \
|
||||
| fail
|
||||
elif [[ "${value}" =~ ${pattern} ]]; then
|
||||
if shopt -p nocasematch &>/dev/null; then
|
||||
local case_sensitive=insensitive
|
||||
else
|
||||
local case_sensitive=sensitive
|
||||
fi
|
||||
batslib_print_kv_single_or_multi 8 \
|
||||
'value' "${value}" \
|
||||
'pattern' "${pattern}" \
|
||||
'match' "${BASH_REMATCH[0]}" \
|
||||
'case' "${case_sensitive}" \
|
||||
| batslib_decorate 'value matches regular expression' \
|
||||
| fail
|
||||
fi
|
||||
}
|
2
extras/portable/test/test_helper/bats-file/load.bash
Normal file
2
extras/portable/test/test_helper/bats-file/load.bash
Normal file
|
@ -0,0 +1,2 @@
|
|||
source "$(dirname "${BASH_SOURCE[0]}")/src/file.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/temp.bash"
|
1111
extras/portable/test/test_helper/bats-file/src/file.bash
Normal file
1111
extras/portable/test/test_helper/bats-file/src/file.bash
Normal file
File diff suppressed because it is too large
Load Diff
182
extras/portable/test/test_helper/bats-file/src/temp.bash
Executable file
182
extras/portable/test/test_helper/bats-file/src/temp.bash
Executable file
|
@ -0,0 +1,182 @@
|
|||
#
|
||||
# bats-file - Common filesystem assertions and helpers for Bats
|
||||
#
|
||||
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||
#
|
||||
# To the extent possible under law, the author(s) have dedicated all
|
||||
# copyright and related and neighboring rights to this software to the
|
||||
# public domain worldwide. This software is distributed without any
|
||||
# warranty.
|
||||
#
|
||||
# You should have received a copy of the CC0 Public Domain Dedication
|
||||
# along with this software. If not, see
|
||||
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
#
|
||||
|
||||
#
|
||||
# temp.bash
|
||||
# ---------
|
||||
#
|
||||
# Functions for handling temporary directories.
|
||||
#
|
||||
|
||||
# Create a temporary directory for the current test in `BATS_TMPDIR`,
|
||||
# and display its path on the standard output.
|
||||
#
|
||||
# The directory name is derived from the test's filename and number, and
|
||||
# a random string for uniqueness.
|
||||
#
|
||||
# <test-filename>-<test-number>-<random-string>
|
||||
#
|
||||
# When `--prefix <prefix>' is specified, `<prefix>' is prepended to the
|
||||
# directory name.
|
||||
#
|
||||
# <prefix><test-filename>-<test-number>-<random-string>
|
||||
#
|
||||
# Must be called from `setup', `@test' or `teardown'.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# setup() {
|
||||
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
|
||||
# }
|
||||
#
|
||||
# teardown() {
|
||||
# temp_del "$TEST_TEMP_DIR"
|
||||
# }
|
||||
#
|
||||
# Globals:
|
||||
# BATS_TEST_NAME
|
||||
# BATS_TEST_FILENAME
|
||||
# BATS_TEST_NUMBER
|
||||
# BATS_TMPDIR
|
||||
# Arguments:
|
||||
# none
|
||||
# Options:
|
||||
# -p, --prefix <prefix> - prefix the directory name with `<prefix>'
|
||||
# Returns:
|
||||
# 0 - on success
|
||||
# 1 - otherwise
|
||||
# Outputs:
|
||||
# STDOUT - path of temporary directory
|
||||
# STDERR - error messages
|
||||
temp_make() {
|
||||
# Check caller.
|
||||
if ! ( batslib_is_caller --indirect 'setup' \
|
||||
|| batslib_is_caller --indirect 'setup_file' \
|
||||
|| batslib_is_caller --indirect "$BATS_TEST_NAME" \
|
||||
|| batslib_is_caller --indirect 'teardown' \
|
||||
|| batslib_is_caller --indirect 'teardown_file' )
|
||||
then
|
||||
echo "Must be called from \`setup', \`@test' or \`teardown'" \
|
||||
| batslib_decorate 'ERROR: temp_make' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Handle options.
|
||||
local prefix=''
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-p|--prefix)
|
||||
if (( $# < 2 )); then
|
||||
echo "\`--prefix' requires an argument" \
|
||||
| batslib_decorate 'ERROR: temp_make' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
prefix="$2"
|
||||
shift 2
|
||||
;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Create directory.
|
||||
local template="$prefix"
|
||||
template+="${BATS_TEST_FILENAME##*/}"
|
||||
template+="-${BATS_TEST_NUMBER}"
|
||||
template+='-XXXXXXXXXX'
|
||||
|
||||
local path
|
||||
path="$(mktemp -d -- "${BATS_TMPDIR}/${template}" 2>&1)"
|
||||
if (( $? )); then
|
||||
echo "$path" \
|
||||
| batslib_decorate 'ERROR: temp_make' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
echo "$path"
|
||||
}
|
||||
|
||||
# Delete a temporary directory, typically created with `temp_make', and
|
||||
# its contents.
|
||||
#
|
||||
# Note: Actually, this function can be used to delete any file or
|
||||
# directory. However, it is most useful in deleting temporary
|
||||
# directories created with `temp_make', hence the naming.
|
||||
#
|
||||
# For development and debugging, deletion can be prevented using
|
||||
# environment variables.
|
||||
#
|
||||
# When `BATSLIB_TEMP_PRESERVE' is set to 1, the function succeeds but
|
||||
# the directory is not deleted.
|
||||
#
|
||||
# When `BATSLIB_TEMP_PRESERVE_ON_FAILURE' is set to 1 and `temp_del' is
|
||||
# called, directly or indirectly, from `teardown', the function succeeds
|
||||
# but the directory is not deleted if the test has failed.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# setup() {
|
||||
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
|
||||
# }
|
||||
#
|
||||
# teardown() {
|
||||
# temp_del "$TEST_TEMP_DIR"
|
||||
# }
|
||||
#
|
||||
# Globals:
|
||||
# BATSLIB_TEMP_PRESERVE
|
||||
# BATSLIB_TEMP_PRESERVE_ON_FAILURE
|
||||
# BATS_TEST_COMPLETED
|
||||
# Arguments:
|
||||
# $1 - path of directory
|
||||
# Returns:
|
||||
# 0 - on success
|
||||
# 1 - otherwise
|
||||
# Outputs:
|
||||
# STDERR - error messages
|
||||
temp_del() {
|
||||
local -r path="$1"
|
||||
|
||||
# Environment variables.
|
||||
if [[ ${BATSLIB_TEMP_PRESERVE-} == '1' ]]; then
|
||||
return 0
|
||||
elif [[ ${BATSLIB_TEMP_PRESERVE_ON_FAILURE-} == '1' ]]; then
|
||||
# Check caller.
|
||||
if ! ( batslib_is_caller --indirect 'teardown' \
|
||||
|| batslib_is_caller --indirect 'teardown_file' )
|
||||
then
|
||||
echo "Must be called from \`teardown' or \`teardown_file' when using \`BATSLIB_TEMP_PRESERVE_ON_FAILURE'" \
|
||||
| batslib_decorate 'ERROR: temp_del' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
|
||||
(( ${BATS_TEST_COMPLETED:-0} != 1 )) && return 0
|
||||
fi
|
||||
|
||||
# Delete directory.
|
||||
local result
|
||||
result="$(rm -r -- "$path" 2>&1 </dev/null)"
|
||||
if (( $? )); then
|
||||
echo "$result" \
|
||||
| batslib_decorate 'ERROR: temp_del' \
|
||||
| fail
|
||||
return $?
|
||||
fi
|
||||
}
|
3
extras/portable/test/test_helper/bats-support/load.bash
Normal file
3
extras/portable/test/test_helper/bats-support/load.bash
Normal file
|
@ -0,0 +1,3 @@
|
|||
source "$(dirname "${BASH_SOURCE[0]}")/src/output.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/error.bash"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/src/lang.bash"
|
41
extras/portable/test/test_helper/bats-support/src/error.bash
Normal file
41
extras/portable/test/test_helper/bats-support/src/error.bash
Normal file
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
# bats-support - Supporting library for Bats test helpers
|
||||
#
|
||||
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||
#
|
||||
# To the extent possible under law, the author(s) have dedicated all
|
||||
# copyright and related and neighboring rights to this software to the
|
||||
# public domain worldwide. This software is distributed without any
|
||||
# warranty.
|
||||
#
|
||||
# You should have received a copy of the CC0 Public Domain Dedication
|
||||
# along with this software. If not, see
|
||||
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
#
|
||||
|
||||
#
|
||||
# error.bash
|
||||
# ----------
|
||||
#
|
||||
# Functions implementing error reporting. Used by public helper
|
||||
# functions or test suits directly.
|
||||
#
|
||||
|
||||
# Fail and display a message. When no parameters are specified, the
|
||||
# message is read from the standard input. Other functions use this to
|
||||
# report failure.
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $@ - [=STDIN] message
|
||||
# Returns:
|
||||
# 1 - always
|
||||
# Inputs:
|
||||
# STDIN - [=$@] message
|
||||
# Outputs:
|
||||
# STDERR - message
|
||||
fail() {
|
||||
(( $# == 0 )) && batslib_err || batslib_err "$@"
|
||||
return 1
|
||||
}
|
73
extras/portable/test/test_helper/bats-support/src/lang.bash
Normal file
73
extras/portable/test/test_helper/bats-support/src/lang.bash
Normal file
|
@ -0,0 +1,73 @@
|
|||
#
|
||||
# bats-util - Various auxiliary functions for Bats
|
||||
#
|
||||
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||
#
|
||||
# To the extent possible under law, the author(s) have dedicated all
|
||||
# copyright and related and neighboring rights to this software to the
|
||||
# public domain worldwide. This software is distributed without any
|
||||
# warranty.
|
||||
#
|
||||
# You should have received a copy of the CC0 Public Domain Dedication
|
||||
# along with this software. If not, see
|
||||
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
#
|
||||
|
||||
#
|
||||
# lang.bash
|
||||
# ---------
|
||||
#
|
||||
# Bash language and execution related functions. Used by public helper
|
||||
# functions.
|
||||
#
|
||||
|
||||
# Check whether the calling function was called from a given function.
|
||||
#
|
||||
# By default, direct invocation is checked. The function succeeds if the
|
||||
# calling function was called directly from the given function. In other
|
||||
# words, if the given function is the next element on the call stack.
|
||||
#
|
||||
# When `--indirect' is specified, indirect invocation is checked. The
|
||||
# function succeeds if the calling function was called from the given
|
||||
# function with any number of intermediate calls. In other words, if the
|
||||
# given function can be found somewhere on the call stack.
|
||||
#
|
||||
# Direct invocation is a form of indirect invocation with zero
|
||||
# intermediate calls.
|
||||
#
|
||||
# Globals:
|
||||
# FUNCNAME
|
||||
# Options:
|
||||
# -i, --indirect - check indirect invocation
|
||||
# Arguments:
|
||||
# $1 - calling function's name
|
||||
# Returns:
|
||||
# 0 - current function was called from the given function
|
||||
# 1 - otherwise
|
||||
batslib_is_caller() {
|
||||
local -i is_mode_direct=1
|
||||
|
||||
# Handle options.
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-i|--indirect) is_mode_direct=0; shift ;;
|
||||
--) shift; break ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Arguments.
|
||||
local -r func="$1"
|
||||
|
||||
# Check call stack.
|
||||
if (( is_mode_direct )); then
|
||||
[[ $func == "${FUNCNAME[2]}" ]] && return 0
|
||||
else
|
||||
local -i depth
|
||||
for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do
|
||||
[[ $func == "${FUNCNAME[$depth]}" ]] && return 0
|
||||
done
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
279
extras/portable/test/test_helper/bats-support/src/output.bash
Normal file
279
extras/portable/test/test_helper/bats-support/src/output.bash
Normal file
|
@ -0,0 +1,279 @@
|
|||
#
|
||||
# bats-support - Supporting library for Bats test helpers
|
||||
#
|
||||
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||
#
|
||||
# To the extent possible under law, the author(s) have dedicated all
|
||||
# copyright and related and neighboring rights to this software to the
|
||||
# public domain worldwide. This software is distributed without any
|
||||
# warranty.
|
||||
#
|
||||
# You should have received a copy of the CC0 Public Domain Dedication
|
||||
# along with this software. If not, see
|
||||
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
#
|
||||
|
||||
#
|
||||
# output.bash
|
||||
# -----------
|
||||
#
|
||||
# Private functions implementing output formatting. Used by public
|
||||
# helper functions.
|
||||
#
|
||||
|
||||
# Print a message to the standard error. When no parameters are
|
||||
# specified, the message is read from the standard input.
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $@ - [=STDIN] message
|
||||
# Returns:
|
||||
# none
|
||||
# Inputs:
|
||||
# STDIN - [=$@] message
|
||||
# Outputs:
|
||||
# STDERR - message
|
||||
batslib_err() {
|
||||
{ if (( $# > 0 )); then
|
||||
echo "$@"
|
||||
else
|
||||
cat -
|
||||
fi
|
||||
} >&2
|
||||
}
|
||||
|
||||
# Count the number of lines in the given string.
|
||||
#
|
||||
# TODO(ztombol): Fix tests and remove this note after #93 is resolved!
|
||||
# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not
|
||||
# give the same result as `${#lines[@]}' when the output contains
|
||||
# empty lines.
|
||||
# See PR #93 (https://github.com/sstephenson/bats/pull/93).
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $1 - string
|
||||
# Returns:
|
||||
# none
|
||||
# Outputs:
|
||||
# STDOUT - number of lines
|
||||
batslib_count_lines() {
|
||||
local -i n_lines=0
|
||||
local line
|
||||
while IFS='' read -r line || [[ -n $line ]]; do
|
||||
(( ++n_lines ))
|
||||
done < <(printf '%s' "$1")
|
||||
echo "$n_lines"
|
||||
}
|
||||
|
||||
# Determine whether all strings are single-line.
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $@ - strings
|
||||
# Returns:
|
||||
# 0 - all strings are single-line
|
||||
# 1 - otherwise
|
||||
batslib_is_single_line() {
|
||||
for string in "$@"; do
|
||||
(( $(batslib_count_lines "$string") > 1 )) && return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Determine the length of the longest key that has a single-line value.
|
||||
#
|
||||
# This function is useful in determining the correct width of the key
|
||||
# column in two-column format when some keys may have multi-line values
|
||||
# and thus should be excluded.
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $odd - key
|
||||
# $even - value of the previous key
|
||||
# Returns:
|
||||
# none
|
||||
# Outputs:
|
||||
# STDOUT - length of longest key
|
||||
batslib_get_max_single_line_key_width() {
|
||||
local -i max_len=-1
|
||||
while (( $# != 0 )); do
|
||||
local -i key_len="${#1}"
|
||||
batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len"
|
||||
shift 2
|
||||
done
|
||||
echo "$max_len"
|
||||
}
|
||||
|
||||
# Print key-value pairs in two-column format.
|
||||
#
|
||||
# Keys are displayed in the first column, and their corresponding values
|
||||
# in the second. To evenly line up values, the key column is fixed-width
|
||||
# and its width is specified with the first parameter (possibly computed
|
||||
# using `batslib_get_max_single_line_key_width').
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $1 - width of key column
|
||||
# $even - key
|
||||
# $odd - value of the previous key
|
||||
# Returns:
|
||||
# none
|
||||
# Outputs:
|
||||
# STDOUT - formatted key-value pairs
|
||||
batslib_print_kv_single() {
|
||||
local -ir col_width="$1"; shift
|
||||
while (( $# != 0 )); do
|
||||
printf '%-*s : %s\n' "$col_width" "$1" "$2"
|
||||
shift 2
|
||||
done
|
||||
}
|
||||
|
||||
# Print key-value pairs in multi-line format.
|
||||
#
|
||||
# The key is displayed first with the number of lines of its
|
||||
# corresponding value in parenthesis. Next, starting on the next line,
|
||||
# the value is displayed. For better readability, it is recommended to
|
||||
# indent values using `batslib_prefix'.
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $odd - key
|
||||
# $even - value of the previous key
|
||||
# Returns:
|
||||
# none
|
||||
# Outputs:
|
||||
# STDOUT - formatted key-value pairs
|
||||
batslib_print_kv_multi() {
|
||||
while (( $# != 0 )); do
|
||||
printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )"
|
||||
printf '%s\n' "$2"
|
||||
shift 2
|
||||
done
|
||||
}
|
||||
|
||||
# Print all key-value pairs in either two-column or multi-line format
|
||||
# depending on whether all values are single-line.
|
||||
#
|
||||
# If all values are single-line, print all pairs in two-column format
|
||||
# with the specified key column width (identical to using
|
||||
# `batslib_print_kv_single').
|
||||
#
|
||||
# Otherwise, print all pairs in multi-line format after indenting values
|
||||
# with two spaces for readability (identical to using `batslib_prefix'
|
||||
# and `batslib_print_kv_multi')
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $1 - width of key column (for two-column format)
|
||||
# $even - key
|
||||
# $odd - value of the previous key
|
||||
# Returns:
|
||||
# none
|
||||
# Outputs:
|
||||
# STDOUT - formatted key-value pairs
|
||||
batslib_print_kv_single_or_multi() {
|
||||
local -ir width="$1"; shift
|
||||
local -a pairs=( "$@" )
|
||||
|
||||
local -a values=()
|
||||
local -i i
|
||||
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
|
||||
values+=( "${pairs[$i]}" )
|
||||
done
|
||||
|
||||
if batslib_is_single_line "${values[@]}"; then
|
||||
batslib_print_kv_single "$width" "${pairs[@]}"
|
||||
else
|
||||
local -i i
|
||||
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
|
||||
pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
|
||||
done
|
||||
batslib_print_kv_multi "${pairs[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Prefix each line read from the standard input with the given string.
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $1 - [= ] prefix string
|
||||
# Returns:
|
||||
# none
|
||||
# Inputs:
|
||||
# STDIN - lines
|
||||
# Outputs:
|
||||
# STDOUT - prefixed lines
|
||||
batslib_prefix() {
|
||||
local -r prefix="${1:- }"
|
||||
local line
|
||||
while IFS='' read -r line || [[ -n $line ]]; do
|
||||
printf '%s%s\n' "$prefix" "$line"
|
||||
done
|
||||
}
|
||||
|
||||
# Mark select lines of the text read from the standard input by
|
||||
# overwriting their beginning with the given string.
|
||||
#
|
||||
# Usually the input is indented by a few spaces using `batslib_prefix'
|
||||
# first.
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $1 - marking string
|
||||
# $@ - indices (zero-based) of lines to mark
|
||||
# Returns:
|
||||
# none
|
||||
# Inputs:
|
||||
# STDIN - lines
|
||||
# Outputs:
|
||||
# STDOUT - lines after marking
|
||||
batslib_mark() {
|
||||
local -r symbol="$1"; shift
|
||||
# Sort line numbers.
|
||||
set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" )
|
||||
|
||||
local line
|
||||
local -i idx=0
|
||||
while IFS='' read -r line || [[ -n $line ]]; do
|
||||
if (( ${1:--1} == idx )); then
|
||||
printf '%s\n' "${symbol}${line:${#symbol}}"
|
||||
shift
|
||||
else
|
||||
printf '%s\n' "$line"
|
||||
fi
|
||||
(( ++idx ))
|
||||
done
|
||||
}
|
||||
|
||||
# Enclose the input text in header and footer lines.
|
||||
#
|
||||
# The header contains the given string as title. The output is preceded
|
||||
# and followed by an additional newline to make it stand out more.
|
||||
#
|
||||
# Globals:
|
||||
# none
|
||||
# Arguments:
|
||||
# $1 - title
|
||||
# Returns:
|
||||
# none
|
||||
# Inputs:
|
||||
# STDIN - text
|
||||
# Outputs:
|
||||
# STDOUT - decorated text
|
||||
batslib_decorate() {
|
||||
echo
|
||||
echo "-- $1 --"
|
||||
cat -
|
||||
echo '--'
|
||||
echo
|
||||
}
|
403
extras/portable/tomb
Executable file
403
extras/portable/tomb
Executable file
|
@ -0,0 +1,403 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Tomb v3, the Crypto Undertaker portable version 3
|
||||
#
|
||||
# A commandline tool to easily operate encryption of secret data
|
||||
#
|
||||
# {{{ License
|
||||
|
||||
# Copyright (C) 2007-2022 Dyne.org Foundation
|
||||
#
|
||||
# Tomb is designed, written and maintained by Denis Roio <jaromil@dyne.org>
|
||||
#
|
||||
# Please refer to the AUTHORS file for more information.
|
||||
#
|
||||
# This source code is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This source code is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
|
||||
# to the GNU Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Public License along with
|
||||
# this source code; if not, , see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# }}} - License
|
||||
|
||||
# {{{ Global variables
|
||||
|
||||
VERSION="3.1.0"
|
||||
DATE="Nov/2022"
|
||||
TOMBEXEC="$0"
|
||||
TMPDIR="${TMP:-/tmp}"
|
||||
TOMBTMPFILES=""
|
||||
PATH="${PATH}:.:/usr/local/bin"
|
||||
exitcode=0
|
||||
# }}}
|
||||
|
||||
# {{{ Logging functions
|
||||
|
||||
_success() {
|
||||
echo "[*] " "$1" "$2" "$3" "$4" 1>&2
|
||||
}
|
||||
_message() {
|
||||
echo " . " "$1" "$2" "$3" "$4" 1>&2
|
||||
}
|
||||
_warning() {
|
||||
echo "[W] " "$1" "$2" "$3" "$4" 1>&2
|
||||
}
|
||||
_verbose() {
|
||||
echo "[D] " "$1" "$2" "$3" "$4" 1>&2
|
||||
}
|
||||
_error() {
|
||||
echo "[!] " "$1" "$2" "$3" "$4" 1>&2
|
||||
exitcode=1
|
||||
}
|
||||
_failure() {
|
||||
echo "[!!] " "$1" "$2" "$3" "$4" 1>&2
|
||||
exitcode=1
|
||||
exit 1
|
||||
}
|
||||
|
||||
# }}}
|
||||
|
||||
_success "Starting Tomb v$VERSION"
|
||||
|
||||
# {{{ Internal functions
|
||||
|
||||
_random_string() {
|
||||
len=${1:-32}
|
||||
[ "$1" != "" ] && {
|
||||
echo "print(O.random($len):base58())" | zenroom 2>/dev/null
|
||||
}
|
||||
return $?
|
||||
}
|
||||
|
||||
_tmp_create() {
|
||||
[ -d "$TMPDIR" ] || {
|
||||
# we create the tempdir with the sticky bit on
|
||||
mkdir -m 1777 "$TMPDIR"
|
||||
[ $? = 0 ] || {
|
||||
_failure "Fatal error creating the temporary directory: %s" "$TMPDIR"
|
||||
}
|
||||
}
|
||||
tfile="${TMPDIR}/`_random_string 64`" # Temporary file
|
||||
umask 066
|
||||
[ $? = 0 ] || {
|
||||
_failure "Fatal error setting the permission umask for temporary files"
|
||||
}
|
||||
[ -r "$tfile" ] && {
|
||||
_failure "Someone is messing up with us trying to hijack temporary files."
|
||||
}
|
||||
touch "$tfile"
|
||||
[ $? = 0 ] || {
|
||||
_failure "Fatal error creating a temporary file: %s" "$tfile"
|
||||
}
|
||||
_verbose "Created tempfile: ::1 temp file::" "$tfile"
|
||||
TOMBTMP="$tfile"
|
||||
TOMBTMPFILES="$TOMBTMPFILES:$tfile"
|
||||
return 0
|
||||
}
|
||||
|
||||
# }}}
|
||||
|
||||
# {{{ FreeBSD
|
||||
freebsd_mount() {
|
||||
file="$1"
|
||||
mnt="$2"
|
||||
_verbose `veracrypt -l "$file"`
|
||||
loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||
_verbose "fsck $loop"
|
||||
fsck.ext4 -p -C0 "$loop"
|
||||
lklfuse -o type=ext4 "${loop}" "$mnt"
|
||||
return $?
|
||||
}
|
||||
|
||||
freebsd_close() {
|
||||
file="$1"
|
||||
md=`veracrypt -l "$file" | awk '{print $3}'`
|
||||
# umount "$mnt"
|
||||
_verbose "md: $md"
|
||||
mnt=`pgrep -lf "lklfuse.*$md" | awk '{print $6}'`
|
||||
_verbose "mnt: $mnt"
|
||||
lkl=`pgrep -f "lklfuse.*$md" | awk '{print $1}'`
|
||||
_verbose "kill $lkl (lklfuse on $md)"
|
||||
# trying to deail with lklfuse bug
|
||||
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=239831
|
||||
renice -20 $lkl
|
||||
kill $lkl
|
||||
sync
|
||||
sleep 1
|
||||
kill -9 $lkl
|
||||
# lkl should have really exited now
|
||||
_verbose "veracrypt -d $file"
|
||||
veracrypt --text --non-interactive -d "$file"
|
||||
return $?
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ Linux
|
||||
# usage: _mount /tmp/.veracrypt_ mountpoint
|
||||
linux_mount() {
|
||||
file="$1"
|
||||
mnt="$2"
|
||||
veralist=`veracrypt -l "$file" | awk '{print($2,":",$3,":",$4)}'`
|
||||
[ "$veralist" = "" ] && {
|
||||
_error "Cannot mount tomb not yet mapped " "$file"
|
||||
return 1
|
||||
}
|
||||
loop=`echo $veralist | cut -d: -f2 | xargs`
|
||||
[ "`echo $veralist | cut -d: -f3 | xargs`" != "-" ] && {
|
||||
_error "Tomb already mounted " "$file on $loop"
|
||||
return 1
|
||||
}
|
||||
_verbose "fsck $loop"
|
||||
fsck.ext4 -p -C0 "$loop" 1>&2
|
||||
_verbose "linux_mount $mnt"
|
||||
mount "$loop" "$mnt"
|
||||
return $?
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ POSIX portable
|
||||
|
||||
# usage: echo PASSWORD | posix_create file size pim
|
||||
posix_create() {
|
||||
file="$1" # must not exist
|
||||
size="$2" # size in bytes
|
||||
pim="$3" # any number
|
||||
_verbose "posix_create $file $size $pim"
|
||||
veracrypt --text --non-interactive --stdin \
|
||||
-m nokernelcrypto \
|
||||
-c "$file" --volume-type normal \
|
||||
--hash sha512 --encryption serpent-aes \
|
||||
--filesystem none --size "${size}" --pim "$pim" \
|
||||
--random-source /dev/urandom -k ''
|
||||
return $?
|
||||
}
|
||||
|
||||
posix_format() {
|
||||
file="$1"
|
||||
loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||
_verbose "posix_format: ${loop}"
|
||||
mkfs.ext4 -L "`basename $file`" "$loop" # -E root_owner="${user_uid}:${user_gid}" "$loop"
|
||||
return $?
|
||||
}
|
||||
|
||||
# usage: echo PASSWORD | posix_map file pim
|
||||
posix_map() {
|
||||
file="$1"
|
||||
pim="$2"
|
||||
_verbose "posix_map $file $pim"
|
||||
veracrypt --text --non-interactive --stdin \
|
||||
--protect-hidden no -m nokernelcrypto \
|
||||
-k '' --pim "$pim" --filesystem none \
|
||||
"$file"
|
||||
return $?
|
||||
}
|
||||
|
||||
posix_close() {
|
||||
file="$1"
|
||||
_verbose "posix_close $file"
|
||||
veracrypt --text --non-interactive -d "$file"
|
||||
return $?
|
||||
}
|
||||
# }}}
|
||||
|
||||
# {{{ Initialization
|
||||
|
||||
system="unknown"
|
||||
create=""
|
||||
format=""
|
||||
map=""
|
||||
mount=""
|
||||
close=""
|
||||
|
||||
tomb_init() {
|
||||
system="`uname -s`"
|
||||
case "$system" in
|
||||
FreeBSD)
|
||||
cat <<EOF
|
||||
create=posix_create
|
||||
format=posix_format
|
||||
map=posix_map
|
||||
mount=freebsd_mount
|
||||
close=freebsd_close
|
||||
EOF
|
||||
;;
|
||||
Linux)
|
||||
cat <<EOF
|
||||
create=posix_create
|
||||
format=posix_format
|
||||
map=posix_map
|
||||
mount=linux_mount
|
||||
close=posix_close
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
_failure "Unsupported system: %s" "$system"
|
||||
esac
|
||||
echo "system=$system"
|
||||
# _verbose "system detected: %s" $system
|
||||
}
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
# {{{ Main
|
||||
[ "$1" = "source" ] && return 0
|
||||
eval "`tomb_init`"
|
||||
tombfile=""
|
||||
tombsize=""
|
||||
PIM=69
|
||||
|
||||
cmd="$1"
|
||||
shift 1
|
||||
|
||||
case "$cmd" in
|
||||
dig)
|
||||
args=2
|
||||
while [ $args -gt 0 ]; do
|
||||
[ "$1" = '-s' ] && { tombsize="$2"; shift 2; }
|
||||
[ "$1" != "" ] && { tombfile="$1"; shift 1; }
|
||||
args=$(( $args - 1 ))
|
||||
done
|
||||
|
||||
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
|
||||
[ "$tombsize" = "" ] && _failure "Size argument missing, use -s"
|
||||
# TODO: check if size is integer
|
||||
# [ "$tombsize" -ge 10 ] || _failure "Tombs can't be smaller than 10 mebibytes"
|
||||
[ -e "$tombfile" ] && {
|
||||
_error "A tomb exists already. I'm not digging here:"
|
||||
ls -l "$tombfile"; exit 1
|
||||
}
|
||||
_message "Commanded to dig tomb " "$tombfile" " sized $tombsize MiB"
|
||||
touch "$tombfile" || _failure "Error creating the tomb " "$tombfile"
|
||||
# dd if=/dev/urandom bs=1048576 count=$tombsize of="$tombfile" || {
|
||||
# _failure "Error creating the tomb " "$tombfile"
|
||||
# exit 1
|
||||
# }
|
||||
bytesize="$(( $tombsize * 1048576 ))"
|
||||
[ "$bytesize" = "" ] &&
|
||||
_failure "Error reading tomb size " "$tombsize"
|
||||
if [ "$system" = "Linux" ]; then
|
||||
fallocate -x -l "$bytesize" "$tombfile" ||
|
||||
_failure "Error creating the tomb " "$tombfile"
|
||||
else
|
||||
dd if=/dev/zero of="$tombfile" count="$tombsize" bs=1048576 ||
|
||||
_failure "Error creating the tomb " "$tombfile of $tombsize MiB"
|
||||
fi
|
||||
;;
|
||||
|
||||
forge)
|
||||
args=1
|
||||
[ "$1" = "" ] && _failure "Missing argument in command " "forge"
|
||||
while [ $args -gt 0 ]; do
|
||||
case "$1" in
|
||||
'-k') tombkey="$2"; shift 2 ;;
|
||||
*) tombkey="$1"; shift 1 ;;
|
||||
esac
|
||||
args=$(( $args - 1 ))
|
||||
done
|
||||
[ "$tombkey" = "" ] && _failure "Missing path to tomb key"
|
||||
[ -e "$tombkey" ] && {
|
||||
_error "A tomb key exists already. I'm not forging here:"
|
||||
ls -l "$tombkey"; exit 1
|
||||
}
|
||||
_message "Commanded to forge tomb key " "$tombkey"
|
||||
touch "$tombkey" || _failure "Error creating the tomb key " "$tombkey"
|
||||
cat <<EOF | zenroom -z | cut -d\" -f4 > "$tombkey"
|
||||
rule check version 2.0.0
|
||||
Given nothing
|
||||
When I create the random object of '64' bytes
|
||||
Then print the 'random object' as 'hex'
|
||||
EOF
|
||||
;;
|
||||
|
||||
lock)
|
||||
args=2
|
||||
while [ $args -gt 0 ]; do
|
||||
case "$1" in
|
||||
'-k') tombkey="$2"; shift 2 ;;
|
||||
*) tombfile="$1"; shift 1 ;;
|
||||
esac
|
||||
args=$(( $args - 1 ))
|
||||
done
|
||||
case "$system" in
|
||||
Linux)
|
||||
bytesize=`stat "$tombfile" | awk '/Size:/ {print $2; exit}'`
|
||||
;;
|
||||
*)
|
||||
bytesize=`stat -f '%z' "$tombfile"`
|
||||
;;
|
||||
esac
|
||||
_message "Commanded to lock tomb " "$tombfile with key $tombkey"
|
||||
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
|
||||
[ -r "$tombfile" ] || _failure "Tomb file not readable"
|
||||
[ "$tombkey" = "" ] && _failure "Missing path to key"
|
||||
[ -r "$tombkey" ] || _failure "Key file not readable"
|
||||
"$create" "$tombfile" "$bytesize" "$PIM" < "$tombkey" || {
|
||||
_failure "Error creating the tomb " "$tombfile"
|
||||
exit 1
|
||||
}
|
||||
"$map" "$tombfile" "$PIM" < "$tombkey" ||
|
||||
_failure "Error mapping the tomb " "$tombfile with key $tombkey"
|
||||
"$format" "$tombfile" ||
|
||||
_error "Error formatting tomb " "$tombfile"
|
||||
"$close" "$tombfile"
|
||||
[ $exitcode = 0 ] &&
|
||||
_success "Success locking tomb " "$tombfile with key $tombkey"
|
||||
;;
|
||||
|
||||
open)
|
||||
args=3
|
||||
while [ $args -gt 0 ]; do
|
||||
case "$1" in
|
||||
'-k') tombkey="$2"; shift 2 ;;
|
||||
*) if [ "$tombfile" = "" ]; then
|
||||
tombfile="$1"
|
||||
else
|
||||
tombmount="$1"
|
||||
fi
|
||||
shift 1 ;;
|
||||
esac
|
||||
args=$(( $args - 1 ))
|
||||
done
|
||||
_message "Commanded to open tomb " "$tombfile with key $tombkey on $tombmount"
|
||||
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
|
||||
[ -r "$tombfile" ] || _failure "Tomb file not readable"
|
||||
[ "$tombkey" = "" ] && _failure "Missing path to key"
|
||||
[ -r "$tombkey" ] || _failure "Key file not readable"
|
||||
[ "$tombmount" = "" ] && tombmount="`basename $tombfile`"
|
||||
"$map" "$tombfile" "$PIM" < "$tombkey"
|
||||
[ $? != 0 ] &&
|
||||
_failure "Error mapping the tomb " "$tombfile with key $tombkey"
|
||||
"$mount" "$tombfile" "$tombmount" || {
|
||||
"$close" "$tombfile"
|
||||
_failure "Error mounting the tomb " "$tombfile on $tombmount"
|
||||
}
|
||||
;;
|
||||
|
||||
close)
|
||||
# args=1
|
||||
if [ "$1" = "all" ]; then
|
||||
"$close"
|
||||
elif [ -e "$1" ]; then
|
||||
"$close" "`realpath $1`" ||
|
||||
_failure "Error closing " "$1"
|
||||
elif [ "$1" = "" ]; then
|
||||
_failure "Missing argument: tomb file or mountpoint"
|
||||
else
|
||||
_failure "Wrong argument: $1"
|
||||
fi
|
||||
;;
|
||||
list)
|
||||
veracrypt -l
|
||||
;;
|
||||
esac
|
||||
# }}}
|
||||
|
||||
exit 0
|
|
@ -3,3 +3,9 @@ Build with 'qmake' and then 'make'
|
|||
|
||||
Requires QT version 5.4 or above
|
||||
|
||||
To install copy the binary in your PATH
|
||||
then all contents of pixmaps to /usr/share/pixmaps
|
||||
and all contents of i18n to /usr/share/i18n
|
||||
|
||||
The prefix /usr/local may be preferred if ever packaged.
|
||||
|
||||
|
|
|
@ -11,20 +11,32 @@ test_expect_success 'Testing tomb creation: dig, forge and lock' '
|
|||
tt_forge --tomb-pwd $DUMMYPASS &&
|
||||
print $DUMMYPASS \
|
||||
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
|
||||
| hexdump -C &&
|
||||
| xxd &&
|
||||
tt_lock --tomb-pwd $DUMMYPASS
|
||||
'
|
||||
|
||||
if test_have_prereq SPHINX ORACLE; then
|
||||
test_export "sphinx_test"
|
||||
test_expect_success 'Testing tomb creation: dig, forge and lock (sphinx password handling)' '
|
||||
tt_dig -s 20 &&
|
||||
tt_forge --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
|
||||
print $(echo $DUMMYPASS | sphinx get $DUMMYUSER $DUMMYHOST) \
|
||||
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
|
||||
| hexdump -C &&
|
||||
tt_lock --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST
|
||||
'
|
||||
tt_dig -s 20 &&
|
||||
tt_forge --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
|
||||
print $(echo $DUMMYPASS | sphinx get $DUMMYUSER $DUMMYHOST) \
|
||||
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
|
||||
| xxd &&
|
||||
tt_lock --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST
|
||||
'
|
||||
fi
|
||||
|
||||
if test_have_prereq DOAS; then
|
||||
test_export "doas_test"
|
||||
test_expect_success 'Testing tomb creation: dig, forge and lock (using doas instead of sudo)' '
|
||||
tt_dig --sudo doas -s 20 &&
|
||||
tt_forge --sudo doas --tomb-pwd $DUMMYPASS &&
|
||||
print $DUMMYPASS \
|
||||
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
|
||||
| xxd &&
|
||||
tt_lock --sudo doas --tomb-pwd $DUMMYPASS
|
||||
'
|
||||
fi
|
||||
|
||||
test_done
|
||||
|
|
|
@ -52,5 +52,12 @@ if test_have_prereq SPHINX ORACLE; then
|
|||
'
|
||||
fi
|
||||
|
||||
if test_have_prereq DOAS; then
|
||||
test_export "doas_test" # Using already generated tomb
|
||||
test_expect_success 'Testing open with good password (using doas instead of sudo)' '
|
||||
tt_open --sudo doas --tomb-pwd $DUMMYPASS &&
|
||||
tt_close --sudo doas
|
||||
'
|
||||
fi
|
||||
|
||||
test_done
|
||||
|
|
|
@ -4,6 +4,8 @@ export test_description="Testing tomb with GnuPG keys"
|
|||
|
||||
source ./setup
|
||||
|
||||
if test_have_prereq GPGRCPT; then
|
||||
|
||||
test_export "recipient"
|
||||
test_expect_success 'Testing tomb with GnuPG keys: creation' '
|
||||
tt_dig -s 20 &&
|
||||
|
@ -54,4 +56,6 @@ test_expect_success 'Testing tomb creation with untrusted GnuPG keys' '
|
|||
test_must_fail tt_forge -g -r $KEY_UNTRUSTED
|
||||
'
|
||||
|
||||
fi
|
||||
|
||||
test_done
|
||||
|
|
|
@ -11,7 +11,7 @@ if test_have_prereq KDF; then
|
|||
tt_forge --tomb-pwd $DUMMYPASS --kdf 1 &&
|
||||
print $DUMMYPASS \
|
||||
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
|
||||
| hexdump -C &&
|
||||
| xxd &&
|
||||
tt_lock --tomb-pwd $DUMMYPASS --kdf 1
|
||||
'
|
||||
|
||||
|
|
|
@ -9,13 +9,15 @@ if test_have_prereq RESIZER; then
|
|||
test_expect_success 'Testing resize to 30 MB tomb' '
|
||||
tt resize -s 30 $tomb -k $tomb_key --unsafe --tomb-pwd $DUMMYPASS
|
||||
'
|
||||
|
||||
if test_have_prereq GPGRCPT; then
|
||||
test_export "recipient" # Using already generated tomb
|
||||
test_expect_success 'Testing resize to 30 MB tomb with GnuPG keys' '
|
||||
tt resize -s 30 $tomb -k $tomb_key -g -r $KEY2
|
||||
'
|
||||
fi
|
||||
|
||||
fi # RESIZER
|
||||
|
||||
if test_have_prereq RESIZER SPHINX ORACLE; then
|
||||
test_export "sphinx_test" # Using already generated tomb
|
||||
test_expect_success 'Testing resize to 30 MB tomb (sphinx)' '
|
||||
|
|
|
@ -6,24 +6,28 @@ source ./setup
|
|||
|
||||
test_export "test" # Using already generated tomb
|
||||
test_expect_success 'Testing tomb with GnuPG keys: passwd' '
|
||||
tt passwd -k $tomb_key --unsafe \
|
||||
tt passwd -f -k $tomb_key --unsafe \
|
||||
--tomb-old-pwd $DUMMYPASS --tomb-pwd $DUMMYPASSNEW &&
|
||||
tt passwd -k $tomb_key --unsafe \
|
||||
tt passwd -f -k $tomb_key --unsafe \
|
||||
--tomb-old-pwd $DUMMYPASSNEW --tomb-pwd $DUMMYPASS
|
||||
'
|
||||
|
||||
if test_have_prereq GPGRCPT; then
|
||||
|
||||
test_export "recipient" # Using already generated tomb
|
||||
test_expect_success 'Testing tomb with GnuPG keys: passwd' '
|
||||
tt passwd -k $tomb_key -g -r $KEY2
|
||||
tt passwd -f -k $tomb_key -g -r $KEY2
|
||||
'
|
||||
|
||||
fi
|
||||
|
||||
if test_have_prereq SPHINX ORACLE; then
|
||||
test_export "sphinx_test" # Using already generated tomb
|
||||
test_expect_success 'Testing changing tomb password with sphinx' '
|
||||
tt passwd -k $tomb_key --unsafe \
|
||||
tt passwd -f -k $tomb_key --unsafe \
|
||||
--tomb-old-pwd $DUMMYPASS --tomb-pwd $DUMMYPASSNEW \
|
||||
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
|
||||
tt passwd -k $tomb_key --unsafe \
|
||||
tt passwd -f -k $tomb_key --unsafe \
|
||||
--tomb-old-pwd $DUMMYPASSNEW --tomb-pwd $DUMMYPASS \
|
||||
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST
|
||||
'
|
||||
|
|
|
@ -18,10 +18,13 @@ test_expect_success 'Testing bind hooks' '
|
|||
tt_close &&
|
||||
touch "$TEMPHOME/$bindtest" &&
|
||||
tt_open --tomb-pwd $DUMMYPASS &&
|
||||
RND2=$(cat "$TEMPHOME/$bindtest") &&
|
||||
[[ "$RND" = "$RND2" ]] &&
|
||||
echo "$RND" &&
|
||||
cat "$TEMPHOME/$bindtest" &&
|
||||
tt list $testname &&
|
||||
tt_close
|
||||
'
|
||||
|
||||
# RND2=$(cat "$TEMPHOME/$bindtest") &&
|
||||
# [[ "$RND" = "$RND2" ]] &&
|
||||
|
||||
test_done
|
||||
|
|
|
@ -33,6 +33,7 @@ if test_have_prereq STEGHIDE; then
|
|||
tt_close
|
||||
'
|
||||
|
||||
if test_have_prereq GPGRCPT; then
|
||||
test_export "recipient" # Using already generated tomb
|
||||
cp -f "$TEST_HOME/arditi.jpg" "$tomb_img"
|
||||
test_expect_success 'Testing tomb with GnuPG keys and steganographic: bury' '
|
||||
|
@ -60,7 +61,9 @@ if test_have_prereq STEGHIDE; then
|
|||
tt open -k $tomb_img $tomb -g --unsafe --tomb-pwd $DUMMYPASS &&
|
||||
tt_close
|
||||
'
|
||||
fi
|
||||
fi # GPGRCPT
|
||||
|
||||
fi # STEGHIDE
|
||||
|
||||
if test_have_prereq PYTHON2 CLOAKIFY DECLOAKIFY; then
|
||||
test_expect_success 'Testing tomb and steganographic: cloak' '
|
||||
|
|
|
@ -6,41 +6,43 @@ source ./setup
|
|||
|
||||
test_export "test" # Using already generated tomb
|
||||
test_expect_success 'Testing set key' '
|
||||
tt forge -k $tomb_key_new --tomb-pwd $DUMMYPASS \
|
||||
tt forge -f -k $tomb_key_new --tomb-pwd $DUMMYPASS \
|
||||
--ignore-swap --unsafe --force &&
|
||||
tt setkey -k $tomb_key_new $tomb_key $tomb \
|
||||
tt setkey -f -k $tomb_key_new $tomb_key $tomb \
|
||||
--unsafe --tomb-pwd $DUMMYPASS --tomb-old-pwd $DUMMYPASS &&
|
||||
tt open -k $tomb_key_new $tomb \
|
||||
tt open -f -k $tomb_key_new $tomb \
|
||||
--unsafe --tomb-pwd $DUMMYPASS &&
|
||||
print $DUMMYPASS \
|
||||
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key_new \
|
||||
| hexdump -C &&
|
||||
| xxd &&
|
||||
tt_close
|
||||
'
|
||||
|
||||
if test_have_prereq GPGRCPT; then
|
||||
test_export "recipient" # Using already generated tomb
|
||||
test_expect_success 'Testing tomb with GnuPG keys: setkey' '
|
||||
tt forge $tomb_key_new -g -r $KEY2 --ignore-swap --unsafe &&
|
||||
tt setkey -k $tomb_key_new $tomb_key $tomb -g -r $KEY2 &&
|
||||
tt open -k $tomb_key_new $tomb -g &&
|
||||
tt forge -f $tomb_key_new -g -r $KEY2 --ignore-swap --unsafe &&
|
||||
tt setkey -f -k $tomb_key_new $tomb_key $tomb -g -r $KEY2 &&
|
||||
tt open -f -k $tomb_key_new $tomb -g &&
|
||||
tt_close
|
||||
'
|
||||
fi
|
||||
|
||||
if test_have_prereq SPHINX ORACLE; then
|
||||
test_export "sphinx_test" # Using already generated tomb
|
||||
test_expect_success 'Testing set key (sphinx)' '
|
||||
tt forge -k $tomb_key_new --tomb-pwd $DUMMYPASS \
|
||||
tt forge -f -k $tomb_key_new --tomb-pwd $DUMMYPASS \
|
||||
--ignore-swap --unsafe --force \
|
||||
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
|
||||
tt setkey -k $tomb_key_new $tomb_key $tomb \
|
||||
tt setkey -f -k $tomb_key_new $tomb_key $tomb \
|
||||
--unsafe --tomb-pwd $DUMMYPASS --tomb-old-pwd $DUMMYPASS \
|
||||
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
|
||||
tt open -k $tomb_key_new $tomb \
|
||||
tt open -f -k $tomb_key_new $tomb \
|
||||
--unsafe --tomb-pwd $DUMMYPASS \
|
||||
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
|
||||
print $DUMMYPASS \
|
||||
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key_new \
|
||||
| hexdump -C &&
|
||||
| xxd &&
|
||||
tt_close
|
||||
'
|
||||
fi
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
FROM dyne/devuan:beowulf
|
||||
FROM dyne/devuan:chimaera
|
||||
|
||||
RUN apt-get update -y -q --allow-releaseinfo-change && apt-get install -y -q zsh cryptsetup gawk libgcrypt20-dev steghide qrencode python python2.7 python3-pip python3-dev libsodium-dev libssl-dev make gcc g++ sudo gettext file bsdmainutils
|
||||
RUN echo "deb http://deb.devuan.org/merged chimaera main" >> /etc/apt/sources.list
|
||||
RUN apt-get update -y -q --allow-releaseinfo-change
|
||||
RUN apt-get install -y -q zsh cryptsetup gpg gawk libgcrypt20-dev steghide qrencode python python2.7 python3-pip python3-dev libssl-dev make gcc sudo gettext bsdmainutils file pinentry-curses xxd libsodium23 libsodium-dev doas
|
||||
RUN pip3 install setuptools wheel
|
||||
|
||||
COPY . /Tomb/
|
||||
|
||||
# WORKDIR /Tomb/extras
|
||||
# RUN ./install_sphinx.sh
|
||||
# RUN cp test/sphinx.cfg /etc/sphinx/config
|
||||
COPY extras/test/doas.conf /etc/doas.conf
|
||||
RUN chmod 400 /etc/doas.conf
|
||||
|
||||
WORKDIR /Tomb
|
||||
RUN make --directory=extras/kdf-keys
|
||||
|
|
20
extras/test/doas.conf
Normal file
20
extras/test/doas.conf
Normal file
|
@ -0,0 +1,20 @@
|
|||
permit nopass root cmd losetup
|
||||
permit nopass root cmd lsblk
|
||||
permit nopass root cmd mkfs.ext3
|
||||
permit nopass root cmd mkfs.ext4
|
||||
permit nopass root cmd mkfs.btrfs
|
||||
permit nopass root cmd touch
|
||||
permit nopass root cmd fsck
|
||||
permit nopass root cmd btrfs
|
||||
permit nopass root cmd tune2fs
|
||||
permit nopass root cmd mkdir
|
||||
permit nopass root cmd mount
|
||||
permit nopass root cmd rmdir
|
||||
permit nopass root cmd chown
|
||||
permit nopass root cmd umount
|
||||
permit nopass root cmd findmnt
|
||||
permit nopass root cmd e2fsck
|
||||
permit nopass root cmd resize2fs
|
||||
permit nopass root cmd lsof
|
||||
permit nopass root cmd kill
|
||||
permit nopass root cmd cryptsetup
|
|
@ -100,7 +100,7 @@ test-tomb-create() {
|
|||
notice "Dump of clear key contents to examine them:"
|
||||
print ${dummypass} \
|
||||
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d /tmp/test.tomb.key \
|
||||
| hexdump -C
|
||||
| xxd
|
||||
echo --
|
||||
}
|
||||
|
||||
|
@ -309,7 +309,7 @@ test-set-key() {
|
|||
notice "Dump of clear key contents to examine them:"
|
||||
print ${dummypass} \
|
||||
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d /tmp/test.tomb.new.key \
|
||||
| hexdump -C
|
||||
| xxd
|
||||
echo --
|
||||
mv /tmp/test.tomb.new.key /tmp/test.tomb.key
|
||||
tt close test
|
||||
|
|
|
@ -57,9 +57,12 @@ command -v cloakify > /dev/null && test_set_prereq CLOAKIFY
|
|||
command -v decloakify > /dev/null && test_set_prereq DECLOAKIFY
|
||||
command -v sphinx > /dev/null && test_set_prereq SPHINX
|
||||
command -v oracle > /dev/null && test_set_prereq ORACLE
|
||||
command -v doas > /dev/null && test_set_prereq DOAS
|
||||
|
||||
|
||||
# GnuPG config
|
||||
#test_set_prereq GPGRCPT
|
||||
if test_have_prereq GPGRCPT; then
|
||||
unset GNUPGHOME
|
||||
unset GPG_AGENT_INFO
|
||||
export GNUPGHOME="$TEST_HOME/gnupg/"
|
||||
|
@ -68,8 +71,8 @@ export KEY2="0B2235E660753AB0475FB3E23DC836481F44B31E"
|
|||
export SUBKEY1="D89BE71A935779961C130E50D9D7ACED39D3991C!"
|
||||
export SUBKEY2="843077BF7FD4A9C7BBFC3A69F065568B4F7D6CA9!"
|
||||
export KEY_UNTRUSTED="E6195F61F5EBA81FE4B1565AAC844B92004240CD"
|
||||
|
||||
chmod 700 "$GNUPGHOME"
|
||||
fi
|
||||
|
||||
|
||||
# Dummy passwords used in the tests suite
|
||||
|
|
|
@ -3,6 +3,7 @@ verbose = False
|
|||
address = 127.0.0.1
|
||||
port = 2355
|
||||
datadir = /tmp/.sphinx/
|
||||
ssl_cert = /etc/sphinx/server.crt
|
||||
|
||||
[server]
|
||||
verbose = False
|
||||
|
@ -10,7 +11,5 @@ address = 127.0.0.1
|
|||
port = 2355
|
||||
datadir = /tmp/.sphinx/
|
||||
keydir = /tmp/.sphinx/
|
||||
|
||||
[websphinx]
|
||||
pinentry=/usr/bin/pinentry
|
||||
log=
|
||||
ssl_cert = /etc/sphinx/server.crt
|
||||
ssl_key = /etc/sphinx/server.key
|
||||
|
|
|
@ -8,13 +8,14 @@ all:
|
|||
@echo "Strings generated in tomb.pot"
|
||||
|
||||
.PHONY: install
|
||||
install: es.mo ru.mo fr.mo de.mo sv.mo it.mo
|
||||
install -Dm644 es.mo ${DESTDIR}${LOCALEDIR}/es_ES/${TOMBFILE}
|
||||
install -Dm644 ru.mo ${DESTDIR}${LOCALEDIR}/ru_RU/${TOMBFILE}
|
||||
install -Dm644 fr.mo ${DESTDIR}${LOCALEDIR}/fr_FR/${TOMBFILE}
|
||||
install: de.mo es.mo fr.mo it.mo pt_BR.mo ru.mo sv.mo
|
||||
install -Dm644 de.mo ${DESTDIR}${LOCALEDIR}/de_DE/${TOMBFILE}
|
||||
install -Dm644 sv.mo ${DESTDIR}${LOCALEDIR}/sv_SV/${TOMBFILE}
|
||||
install -Dm644 es.mo ${DESTDIR}${LOCALEDIR}/es_ES/${TOMBFILE}
|
||||
install -Dm644 fr.mo ${DESTDIR}${LOCALEDIR}/fr_FR/${TOMBFILE}
|
||||
install -Dm644 it.mo ${DESTDIR}${LOCALEDIR}/it_IT/${TOMBFILE}
|
||||
install -Dm644 pt_BR.mo ${DESTDIR}${LOCALEDIR}/pt_BR/${TOMBFILE}
|
||||
install -Dm644 ru.mo ${DESTDIR}${LOCALEDIR}/ru_RU/${TOMBFILE}
|
||||
install -Dm644 sv.mo ${DESTDIR}${LOCALEDIR}/sv_SV/${TOMBFILE}
|
||||
@echo "Translations installed."
|
||||
|
||||
%.mo: %.po
|
||||
|
|
27
extras/translations/README.md
Normal file
27
extras/translations/README.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
# TRANSLATIONS
|
||||
## 1. How to translate
|
||||
Tomb uses the [Weblate](https://hosted.weblate.org/projects/tomb/tomb/) platform to manage the translation efforts of it's user community.
|
||||
|
||||
In Weblate, an user can click "Start new translation", choose a language and click "Request new translation". Additionally, just to reinforce your request, we recommend opening an [issue](https://github.com/dyne/Tomb/issues/new) requesting the development team to open a new translation for your language.
|
||||
|
||||
## 2. About files in this folder
|
||||
All translation files in this folder (those with a ".po" extension) are based on the `tomb.pot` file.
|
||||
|
||||
POT files are just templates and they don't contain any translations. To do a translation, create a new PO file based on the template.
|
||||
|
||||
The `tomb.pot` template must be created using the perl script `generate_translatable_strings.pl` **for each new version of Tomb**:
|
||||
|
||||
```sh
|
||||
$ perl generate_translatable_strings.pl > tomb.pot
|
||||
```
|
||||
|
||||
After that, just open the `tomb.pot` file in the [poedit](https://poedit.net/) program, and click on "Start new translation" (bottom left button), select your language, save and start translating.
|
||||
|
||||
## 3. Updating translation
|
||||
In your favorite shell, make a backup of old file adding it's version, then update with `msgmerge`:
|
||||
|
||||
```sh
|
||||
$ msgmerge --update <language>.po tomb.pot
|
||||
```
|
||||
|
||||
Open the new updated PO translation file and start reviewing the translation. Poedit will highlight what has changed and what needs revision.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -8,7 +8,7 @@ my $STRINGPATTERN = '(".*?[^\\\]")';
|
|||
my $date = localtime;
|
||||
print '
|
||||
# Tomb - The Crypto Undertaker.
|
||||
# Copyright (C) 2007-2014 Dyne.org Foundation
|
||||
# Copyright (C) 2007-2024 Dyne.org Foundation
|
||||
# Denis Roio <jaromil@dyne.org>, 2013.
|
||||
#
|
||||
#, fuzzy
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
cat <<EOF
|
||||
# Tomb - The Crypto Undertaker.
|
||||
# Copyright (C) 2007-2014 Dyne.org Foundation
|
||||
# Copyright (C) 2007-2024 Dyne.org Foundation
|
||||
# Denis Roio <jaromil@dyne.org>, 2013.
|
||||
#
|
||||
#, fuzzy
|
||||
|
|
File diff suppressed because it is too large
Load Diff
1528
extras/translations/pt_BR.po
Normal file
1528
extras/translations/pt_BR.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1423
extras/translations/zh_Hans.po
Normal file
1423
extras/translations/zh_Hans.po
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user