5 Commits
Author SHA1 Message Date
catarrh 5fcd8e9fd6 Relicense to MIT (v2.7.13)
Release / release (push) Waiting to run
The project's only runtime dependency is PyUSB (BSD-3-Clause) and it bundles
no third-party code (the PWA's DES/3DES is written from the FIPS 46-3 tables,
AES uses WebCrypto, the decoders come from the ISO 7816/ETSI/3GPP specs), so
nothing forced GPL; the GPL-2.0-or-later declaration is replaced with MIT.

- LICENSE: the MIT text, Copyright (c) 2026 Anton Troshin.
- pyproject: license = "MIT" + license-files = ["LICENSE"] (PEP 639), and the
  build requirement moves to setuptools>=77 for the SPDX form.  The wheel now
  carries dist-info/licenses/LICENSE and License-Expression: MIT, the sdist
  and the runtime zip carry LICENSE at the top.
- READMEs (EN/RU): a License section (original decoders/DES implementation,
  PyUSB as the only runtime dependency).
- tests: the packaging test pins the MIT declaration and the LICENSE text;
  the release-zip test requires LICENSE in the archive.
- version 2.7.13; SW cache v95.

Copies published before this tag (v2.7.10-v2.7.12) remain under the license
they were released with.
2026-10-21 01:03:55 +03:00
catarrh e00a96b918 Release a Windows-friendly runtime zip (v2.7.12)
Release / release (push) Waiting to run
The releases carried the wheel and the sdist; the only zip was GitHub's source
archive of the whole repository (tests, CI, tools).  A Windows user just wants
to extract a folder and double-click start.bat.

- scripts/build_release_zip.py builds simtrace-analyser-<version>.zip with a
  single top-level directory: the two Python packages, the PWA runtime files
  (frontend/tests and package.json stay out), the four launchers, the udev
  rule, the READMEs and the screenshot.  The .bat entries are written with
  CRLF line endings (cmd.exe mishandles bare-LF labels/goto), the .sh files
  keep their executable bit; LICENSE is included automatically once the repo
  has one.
- tests/test_release_zip.py pins the archive shape, the exclusions, the sw.js
  PRECACHE drift (every precached asset must travel), the CRLF/LF line endings
  and start.sh's mode.
- the release workflow builds the zip, checks it carries start.bat and the
  PWA, uploads it with the run and attaches it to the release.
- READMEs (EN/RU) and the release-notes template offer the zip as the
  Windows-friendly option; version 2.7.12, PWA cache v94.

Verified: the built zip (34 entries, 658 KB compressed) extracted and started
on Linux serves the same index.html; 553 Python + 80 Node tests.
2026-10-21 00:17:06 +03:00
catarrh b19abbd514 CI: install pyusb before the tests
tests/test_capture.py builds a DirectSniffer, whose lazy import reaches
simtrace2_pysniff.device -> usb.core; the runner has no PyUSB, so two tests
failed there while passing on a workstation that already had it.  The job now
installs the declared runtime dependency next to build.
2026-10-20 23:29:07 +03:00
catarrh 0b3c483c0b CI: a tag push builds, tests and publishes the release
The artifacts and the GitHub release were made by hand (`python -m build` plus
`gh release create`).  `.github/workflows/release.yml` now does it from the
pushed tag: Python and PWA tests, the sdist/wheel build, a check that the
wheel carries the PWA, an install-and-serve smoke test of that wheel, and
`gh release create` with the generated notes (prepended by
`.github/release-notes.md`) and the three assets (wheel, sdist, udev rule).

A `workflow_dispatch` run does everything except publish, so the pipeline can
be exercised without minting a release; the artifacts land in the run.  A tag
that does not match version.py/PWA_VERSION fails before anything is published.
2026-10-20 23:28:13 +03:00
catarrh 9e46c64490 README: pip is the install command (pipx dropped)
The release notes and the README's local-wheel examples still led with
`pipx install`; only the direct-URL form had been switched to pip earlier.
Both READMEs now show a plain `pip install <wheel>` (with the PEP 668 note to
use a virtual environment when the system Python is externally managed), and
the `gh release download` example installs with pip too.  The v2.7.10 and
v2.7.11 release bodies are edited to match.
2026-10-20 23:25:24 +03:00
14 changed files with 455 additions and 17 deletions

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
Install the wheel below — the PWA is bundled inside it:
```
pip install ./simtrace_analyser-*.whl
simtrace-analyser-server --capture gsmtap
```
The UI is then served at http://127.0.0.1:8081/. If your system Python is
externally managed (PEP 668 — common on Arch and Fedora), install into a
virtual environment first (`python3 -m venv .venv && . .venv/bin/activate`).
The source archive is the same tree a clone would have: unzip it and run
`./start.sh` (Linux/macOS) or `start.bat` (Windows). Direct SIMtrace2 capture
on Linux additionally needs the attached `70-simtrace2-pysniff.rules` udev rule.
**Runtime zip (Windows-friendly, no installer)** — `simtrace-analyser-*.zip`
is the runtime tree only (packages, PWA, launchers, docs — no tests, no git):
extract it anywhere and run `start.bat` (Windows) or `./start.sh`
(Linux/macOS). Python 3 has to be on `PATH`; `pip install pyusb` is only
needed for direct SIMtrace2 capture.
The project is MIT-licensed (see `LICENSE`).
+115
View File
@@ -0,0 +1,115 @@
name: Release
# A pushed v* tag publishes a GitHub release with the wheel, the sdist and the
# udev rule. A manual run (workflow_dispatch) does everything except publish,
# so the pipeline can be exercised without minting a release.
on:
push:
tags: ['v*']
workflow_dispatch:
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: The tag matches the version strings
if: startsWith(github.ref, 'refs/tags/')
run: |
version=$(python -c "from simtrace_analyser.version import __version__; print(__version__)")
if [ "$GITHUB_REF_NAME" != "v$version" ]; then
echo "tag $GITHUB_REF_NAME does not match version.py ($version)" >&2
exit 1
fi
grep -q "PWA_VERSION = '$version'" frontend/index.html
- name: Install build tooling and the runtime dependency
run: python -m pip install --upgrade pip pyusb build
- name: Python tests
run: python -m unittest discover -s tests
- name: PWA tests
working-directory: frontend
run: node --test
- name: Build the sdist and the wheel
run: python -m build --sdist --wheel
- name: Build the runtime zip
run: python scripts/build_release_zip.py --output dist
- name: The runtime zip is complete
run: |
python - <<'PY'
import glob
import zipfile
archive = glob.glob('dist/*.zip')[0]
names = zipfile.ZipFile(archive).namelist()
for required in ('start.bat', 'frontend/index.html'):
assert any(n.endswith('/' + required) for n in names), \
'%s is missing from %s' % (required, archive)
print('%s: %d entries, launchers and PWA bundled' % (archive, len(names)))
PY
- name: The wheel bundles the PWA
run: |
python - <<'PY'
import glob
import zipfile
wheel = glob.glob('dist/*.whl')[0]
names = zipfile.ZipFile(wheel).namelist()
for required in ('simtrace_analyser/web/index.html',
'simtrace_analyser/web/sw.js'):
assert required in names, '%s is missing from %s' % (required, wheel)
print('%s: %d entries, PWA bundled' % (wheel, len(names)))
PY
- name: The installed wheel serves the PWA
run: |
python -m venv /tmp/smoke
/tmp/smoke/bin/pip install --no-deps dist/*.whl
cd /tmp
/tmp/smoke/bin/simtrace-analyser-server --capture disabled \
--db /tmp/smoke.db --port 8099 &
server=$!
for _ in $(seq 1 20); do
if curl -fsS http://127.0.0.1:8099/ -o /tmp/served-index.html; then
break
fi
sleep 0.5
done
kill "$server"
cmp /tmp/served-index.html "$GITHUB_WORKSPACE/frontend/index.html"
/tmp/smoke/bin/python -c \
"import simtrace_analyser.__main__ as m; p = m._default_web_dir(); assert p.endswith('/simtrace_analyser/web'), p"
- name: Keep the artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/*
- name: Publish the release
if: startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "$GITHUB_REF_NAME" \
--title "$GITHUB_REF_NAME" \
--generate-notes \
--notes-file .github/release-notes.md \
dist/*.whl dist/*.tar.gz dist/*.zip 70-simtrace2-pysniff.rules
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Anton Troshin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1
View File
@@ -1,3 +1,4 @@
graft frontend graft frontend
include LICENSE
include README.md README_RUS.md 70-simtrace2-pysniff.rules include README.md README_RUS.md 70-simtrace2-pysniff.rules
include start.sh start.bat sniff-start.sh sniff-start.bat include start.sh start.bat sniff-start.sh sniff-start.bat
+21 -5
View File
@@ -102,34 +102,43 @@ history), or clone the repository for development. Both bundle the same PWA.
Everything is on the Everything is on the
[latest release](https://github.com/anttro/simtrace-analyser/releases/latest). [latest release](https://github.com/anttro/simtrace-analyser/releases/latest).
**Wheel** (recommended — an isolated install, the PWA is inside the package): **Wheel** (the PWA is inside the package):
```sh ```sh
pipx install ./simtrace_analyser-2.7.11-py3-none-any.whl pip install ./simtrace_analyser-2.7.13-py3-none-any.whl
# or: pip install ./simtrace_analyser-2.7.11-py3-none-any.whl
# then run (the UI is served at http://127.0.0.1:8081/): # then run (the UI is served at http://127.0.0.1:8081/):
simtrace-analyser-server --capture gsmtap simtrace-analyser-server --capture gsmtap
``` ```
If your system Python is externally managed (PEP 668 — common on Arch and
Fedora), install into a virtual environment first:
`python3 -m venv .venv && . .venv/bin/activate`.
`pip` can also fetch the wheel straight from the release page (use the version `pip` can also fetch the wheel straight from the release page (use the version
shown there): shown there):
```sh ```sh
pip install https://github.com/anttro/simtrace-analyser/releases/download/v2.7.11/simtrace_analyser-2.7.11-py3-none-any.whl pip install https://github.com/anttro/simtrace-analyser/releases/download/v2.7.13/simtrace_analyser-2.7.13-py3-none-any.whl
``` ```
With the GitHub CLI, download the latest wheel without knowing the version: With the GitHub CLI, download the latest wheel without knowing the version:
```sh ```sh
gh release download --pattern '*.whl' gh release download --pattern '*.whl'
pipx install ./simtrace_analyser-*.whl pip install ./simtrace_analyser-*.whl
``` ```
**Source archive** — the *Source code (zip / tar.gz)* assets are the full tree **Source archive** — the *Source code (zip / tar.gz)* assets are the full tree
without the git history: unzip, `cd` into it and use the launchers below without the git history: unzip, `cd` into it and use the launchers below
(`./start.sh` on Linux/macOS, `start.bat` on Windows). (`./start.sh` on Linux/macOS, `start.bat` on Windows).
**Zip (Windows-friendly, no installer)** — `simtrace-analyser-2.7.13.zip` is
the runtime tree only (packages, PWA, launchers, docs; no tests, no git):
download it, extract it anywhere and run `start.bat` (Windows) or
`./start.sh` (Linux/macOS). Python 3 has to be on `PATH`; `pip install pyusb`
is only needed for direct SIMtrace2 capture.
### From a git clone (development) ### From a git clone (development)
```sh ```sh
@@ -501,3 +510,10 @@ Use `--inactivity-timeout` to also trigger reconnect on silent firmware hangs.
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** — - **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
FX2LP-based passive ISO 7816 sniffer that emits the same GSMTAP-SIM stream FX2LP-based passive ISO 7816 sniffer that emits the same GSMTAP-SIM stream
on UDP 4729 (accepted by this project's GSMTAP listener). on UDP 4729 (accepted by this project's GSMTAP listener).
## License
MIT — see [LICENSE](LICENSE). The decoders and the PWA's DES/3DES
implementation are original, written from the ISO 7816, ETSI and 3GPP
specifications; no third-party code is bundled. PyUSB (BSD-3-Clause) is a
runtime dependency for direct hardware capture.
+21 -5
View File
@@ -108,34 +108,43 @@ git) или склонировать репозиторий для разраб
Всё находится на странице Всё находится на странице
[последнего релиза](https://github.com/anttro/simtrace-analyser/releases/latest). [последнего релиза](https://github.com/anttro/simtrace-analyser/releases/latest).
**Wheel** (рекомендуется — изолированная установка, PWA внутри пакета): **Wheel** (PWA внутри пакета):
```sh ```sh
pipx install ./simtrace_analyser-2.7.11-py3-none-any.whl pip install ./simtrace_analyser-2.7.13-py3-none-any.whl
# или: pip install ./simtrace_analyser-2.7.11-py3-none-any.whl
# затем запуск (интерфейс отдаётся на http://127.0.0.1:8081/): # затем запуск (интерфейс отдаётся на http://127.0.0.1:8081/):
simtrace-analyser-server --capture gsmtap simtrace-analyser-server --capture gsmtap
``` ```
Если системный Python управляется извне (PEP 668 — обычное дело в Arch и
Fedora), сначала создайте виртуальное окружение:
`python3 -m venv .venv && . .venv/bin/activate`.
`pip` также может скачать wheel прямо со страницы релиза (подставьте версию, `pip` также может скачать wheel прямо со страницы релиза (подставьте версию,
указанную там): указанную там):
```sh ```sh
pip install https://github.com/anttro/simtrace-analyser/releases/download/v2.7.11/simtrace_analyser-2.7.11-py3-none-any.whl pip install https://github.com/anttro/simtrace-analyser/releases/download/v2.7.13/simtrace_analyser-2.7.13-py3-none-any.whl
``` ```
С GitHub CLI можно скачать wheel последнего релиза, не зная версии: С GitHub CLI можно скачать wheel последнего релиза, не зная версии:
```sh ```sh
gh release download --pattern '*.whl' gh release download --pattern '*.whl'
pipx install ./simtrace_analyser-*.whl pip install ./simtrace_analyser-*.whl
``` ```
**Архив исходников** — ассеты *Source code (zip / tar.gz)* содержат полное **Архив исходников** — ассеты *Source code (zip / tar.gz)* содержат полное
дерево без истории git: распакуйте, перейдите в каталог и используйте скрипты дерево без истории git: распакуйте, перейдите в каталог и используйте скрипты
запуска ниже (`./start.sh` в Linux/macOS, `start.bat` в Windows). запуска ниже (`./start.sh` в Linux/macOS, `start.bat` в Windows).
**Zip (для Windows, без установки)** — `simtrace-analyser-2.7.13.zip`
содержит только рабочее дерево (пакеты, PWA, скрипты запуска, документацию;
без тестов и git): скачайте, распакуйте в любое место и запустите `start.bat`
(Windows) или `./start.sh` (Linux/macOS). Python 3 должен быть в `PATH`;
`pip install pyusb` нужен только для прямого захвата с SIMtrace2.
### Из клона репозитория (для разработки) ### Из клона репозитория (для разработки)
```sh ```sh
@@ -521,3 +530,10 @@ for msg in session.iter_messages():
- **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** — - **[sigrok_iso7816_stream](https://github.com/anttro/sigrok_iso7816_stream)** —
пассивный ISO 7816 сниффер на базе FX2LP, выдающий такой же поток пассивный ISO 7816 сниффер на базе FX2LP, выдающий такой же поток
GSMTAP-SIM на UDP 4729 (принимается GSMTAP-листенером этого проекта). GSMTAP-SIM на UDP 4729 (принимается GSMTAP-листенером этого проекта).
## Лицензия
MIT — см. [LICENSE](LICENSE). Декодеры и реализация DES/3DES в PWA написаны
самостоятельно по спецификациям ISO 7816, ETSI и 3GPP; сторонний код в проект
не включён. PyUSB (BSD-3-Clause) — зависимость для прямого захвата с
оборудования.
+1 -1
View File
@@ -3389,7 +3389,7 @@ function onSearchChange() {
} }
// ──────────────────── Versioning ──────────────────── // ──────────────────── Versioning ────────────────────
const PWA_VERSION = '2.7.11'; const PWA_VERSION = '2.7.13';
const MIN_SERVER_VERSION = '2.3.0'; const MIN_SERVER_VERSION = '2.3.0';
function parseVersion(v) { function parseVersion(v) {
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simtrace-analyser-v93'; const CACHE = 'simtrace-analyser-v95';
const PRECACHE = [ const PRECACHE = [
'index.html', 'index.html',
+4 -3
View File
@@ -1,13 +1,14 @@
[build-system] [build-system]
requires = ["setuptools>=64"] requires = ["setuptools>=77"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "simtrace-analyser" name = "simtrace-analyser"
version = "2.7.11" version = "2.7.13"
description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)" description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)"
readme = {file = "README.md", content-type = "text/markdown"} readme = {file = "README.md", content-type = "text/markdown"}
license = {text = "GPL-2.0-or-later"} license = "MIT"
license-files = ["LICENSE"]
requires-python = ">=3.9" requires-python = ">=3.9"
dependencies = [ dependencies = [
"pyusb>=1.2", "pyusb>=1.2",
+132
View File
@@ -0,0 +1,132 @@
"""Build the ready-to-run runtime zip for a GitHub release.
The wheel needs ``pip`` and the GitHub source archive carries the whole
repository (tests, CI, the gitignored ``tools/``). This zip is the runtime
tree only: extract it anywhere, run ``start.bat`` on Windows or
``./start.sh`` elsewhere, and the server serves the bundled PWA. Python 3
must be on ``PATH``; PyUSB is only needed for direct hardware capture.
The build is deliberately simple - the explicit include lists below are the
whole manifest, so nothing from ``tests/``, ``.github/``, ``pyproject.toml``
or a build directory can leak into the archive by accident.
"""
import argparse
import sys
import zipfile
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
# The script lives outside the packages; import the version from the checkout.
sys.path.insert(0, str(REPO))
from simtrace_analyser.version import __version__ # noqa: E402
# The Python packages (all modules, no caches).
PACKAGES = ('simtrace_analyser', 'simtrace2_pysniff')
# The PWA runtime files; ``tests/`` and ``package.json`` are dev-only.
FRONTEND = 'frontend'
FRONTEND_EXCLUDE = ('tests', 'package.json')
# Repo-root files that make the extracted tree usable. LICENSE is included
# once the repository has one.
ROOT_FILES = (
'start.sh',
'start.bat',
'sniff-start.sh',
'sniff-start.bat',
'70-simtrace2-pysniff.rules',
'README.md',
'README_RUS.md',
'frontend.png',
'LICENSE',
)
# Skip editor/byte-code droppings wherever they appear.
SKIP_DIRS = ('__pycache__',)
SKIP_NAMES = ('.DS_Store',)
SKIP_SUFFIXES = ('.pyc',)
def _files(root):
"""Yield the regular files under *root*, sorted and cache-free."""
for path in sorted(root.rglob('*')):
if not path.is_file():
continue
if any(part in SKIP_DIRS for part in path.parts):
continue
if path.name in SKIP_NAMES or path.suffix in SKIP_SUFFIXES:
continue
yield path
def _write_bat(zf, path, arcname):
"""Write a .bat entry with CRLF line endings.
The checkout stores the batch files with LF, and cmd.exe mishandles a
batch file whose lines end in a bare LF (labels and ``goto`` in
particular), so the archive gets the Windows form. The source files
stay untouched. ``writestr`` does not copy the file mode, so set it.
"""
data = path.read_bytes().replace(b'\r\n', b'\n').replace(b'\n', b'\r\n')
info = zipfile.ZipInfo.from_file(path, arcname)
info.external_attr = 0o644 << 16
info.compress_type = zipfile.ZIP_DEFLATED
zf.writestr(info, data)
def build_zip(output_dir, version=None):
"""Write ``simtrace-analyser-<version>.zip`` into *output_dir*.
Returns the archive path. The zip has a single top-level directory
``simtrace-analyser-<version>/`` holding the runtime tree.
"""
version = version or __version__
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
archive = output_dir / ('simtrace-analyser-%s.zip' % version)
top = 'simtrace-analyser-%s' % version
with zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED) as zf:
for package in PACKAGES:
for path in _files(REPO / package):
zf.write(path, '%s/%s' % (top, path.relative_to(REPO).as_posix()))
for path in _files(REPO / FRONTEND):
rel = path.relative_to(REPO / FRONTEND)
if rel.parts[0] in FRONTEND_EXCLUDE:
continue
zf.write(path, '%s/%s' % (top, path.relative_to(REPO).as_posix()))
for name in ROOT_FILES:
path = REPO / name
if not path.is_file():
# LICENSE is not in the repository yet; include it once it is.
continue
arcname = '%s/%s' % (top, name)
if path.suffix == '.bat':
_write_bat(zf, path, arcname)
else:
zf.write(path, arcname)
return archive
def main(argv=None):
parser = argparse.ArgumentParser(
description='Build the runtime zip attached to a release')
parser.add_argument('--output', default='dist', metavar='DIR',
help='directory for the archive (default: dist)')
args = parser.parse_args(argv)
archive = build_zip(args.output)
with zipfile.ZipFile(archive) as zf:
entries = len(zf.namelist())
print(archive)
print('%d entries' % entries)
return 0
if __name__ == '__main__':
sys.exit(main())
+1 -1
View File
@@ -1,3 +1,3 @@
"""Single source of truth for the SIMtrace Analyser version.""" """Single source of truth for the SIMtrace Analyser version."""
__version__ = '2.7.11' __version__ = '2.7.13'
+9
View File
@@ -70,6 +70,15 @@ class TestDistributionMetadata(unittest.TestCase):
# The sdist carries frontend/, so a wheel built from it has the PWA. # The sdist carries frontend/, so a wheel built from it has the PWA.
self.assertIn('graft frontend', self._read('MANIFEST.in')) self.assertIn('graft frontend', self._read('MANIFEST.in'))
def test_license_is_mit(self):
# The project is MIT; the metadata and the file must not drift apart.
pyproject = self._read('pyproject.toml')
self.assertIn('license = "MIT"', pyproject)
self.assertIn('license-files = ["LICENSE"]', pyproject)
license_text = self._read('LICENSE')
self.assertTrue(license_text.startswith('MIT License\n'))
self.assertIn('Permission is hereby granted, free of charge', license_text)
def test_setup_py_bundles_the_pwa(self): def test_setup_py_bundles_the_pwa(self):
setup_py = self._read('setup.py') setup_py = self._read('setup.py')
self.assertIn('frontend', setup_py) self.assertIn('frontend', setup_py)
+1 -1
View File
@@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase):
class TestVersion(unittest.TestCase): class TestVersion(unittest.TestCase):
def test_package_version(self): def test_package_version(self):
from simtrace2_pysniff import __version__ from simtrace2_pysniff import __version__
self.assertEqual(__version__, '2.7.11') self.assertEqual(__version__, '2.7.13')
def test_status_includes_version(self): def test_status_includes_version(self):
import os import os
+105
View File
@@ -0,0 +1,105 @@
"""Tests for the ready-to-run release zip (scripts/build_release_zip.py)."""
import importlib.util
import re
import tempfile
import unittest
import zipfile
from pathlib import Path
from simtrace_analyser.version import __version__
REPO = Path(__file__).resolve().parents[1]
# scripts/ is not a package, so load the builder from its path.
_spec = importlib.util.spec_from_file_location(
'build_release_zip', REPO / 'scripts' / 'build_release_zip.py')
build_release_zip = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(build_release_zip)
class TestReleaseZip(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._tmp = tempfile.TemporaryDirectory()
cls.archive = build_release_zip.build_zip(cls._tmp.name)
with zipfile.ZipFile(cls.archive) as zf:
cls.names = zf.namelist()
@classmethod
def tearDownClass(cls):
cls._tmp.cleanup()
def test_name_and_single_top_level_directory(self):
top = 'simtrace-analyser-%s' % __version__
self.assertEqual(self.archive.name, top + '.zip')
for name in self.names:
self.assertTrue(name.startswith(top + '/'),
'%s is outside %s/' % (name, top))
def test_required_entries(self):
top = 'simtrace-analyser-%s' % __version__
for rel in (
'simtrace_analyser/__main__.py',
'simtrace_analyser/version.py',
'simtrace2_pysniff/__main__.py',
'frontend/index.html',
'frontend/sw.js',
'frontend/manifest.json',
'start.sh',
'start.bat',
'sniff-start.sh',
'sniff-start.bat',
'70-simtrace2-pysniff.rules',
'LICENSE',
'README.md'):
self.assertIn('%s/%s' % (top, rel), self.names)
def test_dev_only_entries_are_excluded(self):
for name in self.names:
self.assertNotIn('/tests/', name)
self.assertNotIn('/__pycache__/', name)
self.assertNotIn('.pyc', name)
self.assertNotIn('/.git', name)
self.assertNotIn('/.github/', name)
self.assertNotIn(
'simtrace-analyser-%s/frontend/package.json' % __version__,
self.names)
self.assertNotIn(
'simtrace-analyser-%s/pyproject.toml' % __version__, self.names)
def test_precache_files_are_in_the_zip(self):
# Drift guard: every asset the service worker precaches must travel
# with the PWA, otherwise an offline install fails on a fresh extract.
text = (REPO / 'frontend' / 'sw.js').read_text()
match = re.search(r'PRECACHE\s*=\s*\[(.*?)\]', text, re.S)
self.assertIsNotNone(match, 'no PRECACHE array in frontend/sw.js')
assets = re.findall(r"'([^']+)'", match.group(1))
self.assertTrue(assets)
top = 'simtrace-analyser-%s' % __version__
for asset in assets:
self.assertIn('%s/frontend/%s' % (top, asset), self.names,
'precached %s is missing from the zip' % asset)
def test_batch_files_use_crlf(self):
with zipfile.ZipFile(self.archive) as zf:
bats = [n for n in self.names if n.endswith('.bat')]
self.assertTrue(bats)
for name in bats:
data = zf.read(name)
self.assertIn(b'\r\n', data, name)
self.assertNotIn(b'\n', data.replace(b'\r\n', b''),
'%s has a bare LF' % name)
def test_shell_script_is_lf_and_executable(self):
name = 'simtrace-analyser-%s/start.sh' % __version__
with zipfile.ZipFile(self.archive) as zf:
info = zf.getinfo(name)
data = zf.read(name)
self.assertNotIn(b'\r\n', data)
self.assertTrue(info.external_attr >> 16 & 0o100,
'start.sh lost its executable bit')
if __name__ == '__main__':
unittest.main()