Use firmware-config.js as driver for tests
This commit is contained in:
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -69,9 +69,15 @@ jobs:
|
|||||||
if: steps.check-e2e.outputs.run == 'true' && env.GITEA_ACTIONS != 'true'
|
if: steps.check-e2e.outputs.run == 'true' && env.GITEA_ACTIONS != 'true'
|
||||||
run: |
|
run: |
|
||||||
mkdir -p tests/cached_assets
|
mkdir -p tests/cached_assets
|
||||||
echo "Downloading firmware..."
|
node -e "console.log(JSON.stringify(require('./tests/firmware-config')))" | jq -c '.[]' | while IFS= read -r entry; do
|
||||||
curl -fL --progress-bar -o tests/cached_assets/kobo-update-4.45.23646.zip \
|
version=$(echo "$entry" | jq -r '.version')
|
||||||
https://ereaderfiles.kobo.com/firmwares/kobo13/Mar2026/kobo-update-4.45.23646.zip
|
url=$(echo "$entry" | jq -r '.url')
|
||||||
|
file="tests/cached_assets/kobo-update-${version}.zip"
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
echo "Downloading firmware $version..."
|
||||||
|
curl -fL --progress-bar -o "$file" "$url"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
- name: Full integration test (WASM)
|
- name: Full integration test (WASM)
|
||||||
if: steps.check-e2e.outputs.run == 'true' && env.GITEA_ACTIONS != 'true'
|
if: steps.check-e2e.outputs.run == 'true' && env.GITEA_ACTIONS != 'true'
|
||||||
|
|||||||
@@ -18,13 +18,12 @@ import (
|
|||||||
// TestIntegrationPatch runs the full patching pipeline with real patch files
|
// TestIntegrationPatch runs the full patching pipeline with real patch files
|
||||||
// and validates SHA1 checksums of the patched binaries.
|
// and validates SHA1 checksums of the patched binaries.
|
||||||
//
|
//
|
||||||
// Requires the firmware zip to be present at testdata/kobo-update-4.45.23646.zip
|
// All values are provided via environment variables by test-integration.sh,
|
||||||
// (or the path set via FIRMWARE_ZIP env var). Run test-integration.sh to download
|
// which reads from tests/firmware-config.js.
|
||||||
// the firmware and execute this test.
|
|
||||||
func TestIntegrationPatch(t *testing.T) {
|
func TestIntegrationPatch(t *testing.T) {
|
||||||
firmwarePath := os.Getenv("FIRMWARE_ZIP")
|
firmwarePath := os.Getenv("FIRMWARE_ZIP")
|
||||||
if firmwarePath == "" {
|
if firmwarePath == "" {
|
||||||
firmwarePath = "testdata/kobo-update-4.45.23646.zip"
|
t.Skip("FIRMWARE_ZIP not set (run test-integration.sh)")
|
||||||
}
|
}
|
||||||
|
|
||||||
firmwareZip, err := os.ReadFile(firmwarePath)
|
firmwareZip, err := os.ReadFile(firmwarePath)
|
||||||
@@ -33,32 +32,26 @@ func TestIntegrationPatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read patch files from the patches zip.
|
// Read patch files from the patches zip.
|
||||||
patchesZipPath := "../web/src/patches/patches_4.45.zip"
|
patchesZipPath := os.Getenv("PATCHES_ZIP")
|
||||||
|
if patchesZipPath == "" {
|
||||||
|
t.Fatal("PATCHES_ZIP not set (run test-integration.sh)")
|
||||||
|
}
|
||||||
patchesZip, err := os.ReadFile(patchesZipPath)
|
patchesZip, err := os.ReadFile(patchesZipPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not read patches zip: %v", err)
|
t.Fatalf("could not read patches zip: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
patchFiles, err := extractPatchFiles(patchesZip)
|
patchFiles, configYAML, err := extractPatchFilesAndConfig(patchesZip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not extract patch files: %v", err)
|
t.Fatalf("could not extract patch files: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config: all patches at their defaults, with one override enabled.
|
// Replace the existing overrides section with our test override.
|
||||||
configYAML := `
|
// The config from the zip has all patches disabled; we enable one to verify patching works.
|
||||||
version: 4.45.23646
|
if idx := strings.Index(configYAML, "\noverrides:"); idx != -1 {
|
||||||
in: unused
|
configYAML = configYAML[:idx]
|
||||||
out: unused
|
}
|
||||||
log: unused
|
configYAML += `
|
||||||
|
|
||||||
patches:
|
|
||||||
src/nickel.yaml: usr/local/Kobo/nickel
|
|
||||||
src/nickel_custom.yaml: usr/local/Kobo/nickel
|
|
||||||
src/libadobe.so.yaml: usr/local/Kobo/libadobe.so
|
|
||||||
src/libnickel.so.1.0.0.yaml: usr/local/Kobo/libnickel.so.1.0.0
|
|
||||||
src/librmsdk.so.1.0.0.yaml: usr/local/Kobo/librmsdk.so.1.0.0
|
|
||||||
src/cloud_sync.yaml: usr/local/Kobo/libnickel.so.1.0.0
|
|
||||||
|
|
||||||
overrides:
|
overrides:
|
||||||
src/nickel.yaml:
|
src/nickel.yaml:
|
||||||
"Remove footer (row3) on new home screen": yes
|
"Remove footer (row3) on new home screen": yes
|
||||||
@@ -78,13 +71,18 @@ overrides:
|
|||||||
t.Fatal("patchFirmware returned empty tgz")
|
t.Fatal("patchFirmware returned empty tgz")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expected SHA1 checksums for Kobo Libra Color, firmware 4.45.23646,
|
// Parse expected checksums from EXPECTED_CHECKSUMS env var.
|
||||||
// with only "Remove footer (row3) on new home screen" enabled.
|
// Format: "path1=hash1,path2=hash2,..."
|
||||||
expectedSHA1 := map[string]string{
|
checksumEnv := os.Getenv("EXPECTED_CHECKSUMS")
|
||||||
"usr/local/Kobo/libnickel.so.1.0.0": "ef64782895a47ac85f0829f06fffa4816d23512d",
|
if checksumEnv == "" {
|
||||||
"usr/local/Kobo/nickel": "80a607bac515457a6864be8be831df631a01005c",
|
t.Fatal("EXPECTED_CHECKSUMS not set (run test-integration.sh)")
|
||||||
"usr/local/Kobo/libadobe.so": "02dc99c71c4fef75401cd49ddc2e63f928a126e1",
|
}
|
||||||
"usr/local/Kobo/librmsdk.so.1.0.0": "e3819260c9fc539a53db47e9d3fe600ec11633d5",
|
expectedSHA1 := map[string]string{}
|
||||||
|
for _, entry := range strings.Split(checksumEnv, ",") {
|
||||||
|
parts := strings.SplitN(entry, "=", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
expectedSHA1[parts[0]] = parts[1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the output tgz and check SHA1 of each patched binary.
|
// Extract the output tgz and check SHA1 of each patched binary.
|
||||||
@@ -114,31 +112,37 @@ overrides:
|
|||||||
t.Logf("log output:\n%s", result.log)
|
t.Logf("log output:\n%s", result.log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractPatchFiles reads a patches zip and returns a map of filename -> contents
|
// extractPatchFilesAndConfig reads a patches zip and returns the src/*.yaml
|
||||||
// for all src/*.yaml files.
|
// patch files and the kobopatch.yaml config content.
|
||||||
func extractPatchFiles(zipData []byte) (map[string][]byte, error) {
|
func extractPatchFilesAndConfig(zipData []byte) (map[string][]byte, string, error) {
|
||||||
r, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData)))
|
r, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
files := make(map[string][]byte)
|
files := make(map[string][]byte)
|
||||||
|
var configYAML string
|
||||||
for _, f := range r.File {
|
for _, f := range r.File {
|
||||||
if !strings.HasPrefix(f.Name, "src/") || !strings.HasSuffix(f.Name, ".yaml") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rc, err := f.Open()
|
rc, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("open %s: %w", f.Name, err)
|
return nil, "", fmt.Errorf("open %s: %w", f.Name, err)
|
||||||
}
|
}
|
||||||
data, err := io.ReadAll(rc)
|
data, err := io.ReadAll(rc)
|
||||||
rc.Close()
|
rc.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read %s: %w", f.Name, err)
|
return nil, "", fmt.Errorf("read %s: %w", f.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f.Name == "kobopatch.yaml" {
|
||||||
|
configYAML = string(data)
|
||||||
|
} else if strings.HasPrefix(f.Name, "src/") && strings.HasSuffix(f.Name, ".yaml") {
|
||||||
files[f.Name] = data
|
files[f.Name] = data
|
||||||
}
|
}
|
||||||
return files, nil
|
}
|
||||||
|
if configYAML == "" {
|
||||||
|
return nil, "", fmt.Errorf("kobopatch.yaml not found in patches zip")
|
||||||
|
}
|
||||||
|
return files, configYAML, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractTgzSHA1 reads a tgz and returns a map of entry name -> SHA1 hex string.
|
// extractTgzSHA1 reads a tgz and returns a map of entry name -> SHA1 hex string.
|
||||||
|
|||||||
@@ -13,7 +13,16 @@ if [ -x "$LOCAL_GO_DIR/bin/go" ]; then
|
|||||||
export PATH="$LOCAL_GO_DIR/bin:$PATH"
|
export PATH="$LOCAL_GO_DIR/bin:$PATH"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
FIRMWARE_FILE="${FIRMWARE_ZIP:-$(cd .. && pwd)/tests/cached_assets/kobo-update-4.45.23646.zip}"
|
FIRMWARE_CONFIG="$(cd .. && pwd)/tests/firmware-config.js"
|
||||||
|
PRIMARY=$(node -e "
|
||||||
|
const c = require('$FIRMWARE_CONFIG')[0];
|
||||||
|
console.log(JSON.stringify(c));
|
||||||
|
")
|
||||||
|
PRIMARY_VERSION=$(echo "$PRIMARY" | jq -r '.version')
|
||||||
|
PATCHES_ZIP="$(cd .. && pwd)/web/src/patches/$(echo "$PRIMARY" | jq -r '.patches')"
|
||||||
|
CHECKSUMS=$(echo "$PRIMARY" | jq -r '.checksums | to_entries | map("\(.key)=\(.value)") | join(",")')
|
||||||
|
ORIGINAL_TGZ_SHA1=$(echo "$PRIMARY" | jq -r '.originalTgzChecksum')
|
||||||
|
FIRMWARE_FILE="${FIRMWARE_ZIP:-$(cd .. && pwd)/tests/cached_assets/kobo-update-${PRIMARY_VERSION}.zip}"
|
||||||
if [ ! -f "$FIRMWARE_FILE" ]; then
|
if [ ! -f "$FIRMWARE_FILE" ]; then
|
||||||
echo "ERROR: Firmware zip not found at $FIRMWARE_FILE"
|
echo "ERROR: Firmware zip not found at $FIRMWARE_FILE"
|
||||||
echo "Run ./test.sh from the project root to download test assets."
|
echo "Run ./test.sh from the project root to download test assets."
|
||||||
@@ -32,4 +41,8 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Running integration test..."
|
echo "Running integration test..."
|
||||||
FIRMWARE_ZIP="$FIRMWARE_FILE" GOOS=js GOARCH=wasm go test -v -run TestIntegrationPatch -timeout 300s -exec="$EXEC" .
|
FIRMWARE_ZIP="$FIRMWARE_FILE" \
|
||||||
|
PATCHES_ZIP="$PATCHES_ZIP" \
|
||||||
|
EXPECTED_CHECKSUMS="$CHECKSUMS" \
|
||||||
|
ORIGINAL_TGZ_SHA1="$ORIGINAL_TGZ_SHA1" \
|
||||||
|
GOOS=js GOARCH=wasm go test -v -run TestIntegrationPatch -timeout 300s -exec="$EXEC" .
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Test all patches against the cached firmware using kobopatch -t.
|
# Test all patches against cached firmware using kobopatch -t.
|
||||||
# This builds the native kobopatch binary, extracts the patch set,
|
# Iterates over all firmware versions in tests/firmware-config.js,
|
||||||
# and runs each patch in test mode to check if it can be applied.
|
# builds the native kobopatch binary, and generates blacklist.json.
|
||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
@@ -14,19 +14,10 @@ if [ -x "$LOCAL_GO_DIR/bin/go" ]; then
|
|||||||
export PATH="$LOCAL_GO_DIR/bin:$PATH"
|
export PATH="$LOCAL_GO_DIR/bin:$PATH"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
FIRMWARE_FILE="${FIRMWARE_ZIP:-$(cd .. && pwd)/tests/cached_assets/kobo-update-4.45.23646.zip}"
|
FIRMWARE_CONFIG="$(cd .. && pwd)/tests/firmware-config.js"
|
||||||
PATCHES_ZIP="${PATCHES_ZIP:-$(cd .. && pwd)/web/src/patches/patches_4.45.zip}"
|
CACHED_ASSETS="$(cd .. && pwd)/tests/cached_assets"
|
||||||
|
PATCHES_DIR="$(cd .. && pwd)/web/src/patches"
|
||||||
if [ ! -f "$FIRMWARE_FILE" ]; then
|
BLACKLIST_FILE="$PATCHES_DIR/blacklist.json"
|
||||||
echo "ERROR: Firmware zip not found at $FIRMWARE_FILE"
|
|
||||||
echo "Run ./test.sh from the project root to download test assets first."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$PATCHES_ZIP" ]; then
|
|
||||||
echo "ERROR: Patches zip not found at $PATCHES_ZIP"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build the native kobopatch binary.
|
# Build the native kobopatch binary.
|
||||||
echo "=== Building kobopatch ==="
|
echo "=== Building kobopatch ==="
|
||||||
@@ -35,64 +26,81 @@ go build -o ../kobopatch ./kobopatch
|
|||||||
cd ..
|
cd ..
|
||||||
echo "Built kobopatch successfully."
|
echo "Built kobopatch successfully."
|
||||||
|
|
||||||
|
# Start with an empty blacklist.
|
||||||
|
echo "{}" > "$BLACKLIST_FILE"
|
||||||
|
|
||||||
|
# Iterate over all firmware versions in the config.
|
||||||
|
CONFIGS=$(node -e "console.log(JSON.stringify(require('$FIRMWARE_CONFIG')))")
|
||||||
|
COUNT=$(echo "$CONFIGS" | jq 'length')
|
||||||
|
|
||||||
|
for i in $(seq 0 $((COUNT - 1))); do
|
||||||
|
ENTRY=$(echo "$CONFIGS" | jq -c ".[$i]")
|
||||||
|
VERSION=$(echo "$ENTRY" | jq -r '.version')
|
||||||
|
SHORT_VERSION=$(echo "$ENTRY" | jq -r '.shortVersion')
|
||||||
|
PATCHES=$(echo "$ENTRY" | jq -r '.patches')
|
||||||
|
|
||||||
|
FIRMWARE_FILE="$CACHED_ASSETS/kobo-update-${VERSION}.zip"
|
||||||
|
PATCHES_ZIP="$PATCHES_DIR/$PATCHES"
|
||||||
|
|
||||||
|
if [ ! -f "$FIRMWARE_FILE" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "=== Skipping $VERSION (firmware not downloaded) ==="
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$PATCHES_ZIP" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "=== Skipping $VERSION (patches zip $PATCHES not found) ==="
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
# Extract patches to a temp directory.
|
# Extract patches to a temp directory.
|
||||||
TMPDIR="$(mktemp -d)"
|
TMPDIR="$(mktemp -d)"
|
||||||
trap 'rm -rf "$TMPDIR"' EXIT
|
trap 'rm -rf "$TMPDIR"' EXIT
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Extracting patches ==="
|
echo "=== Extracting $PATCHES ==="
|
||||||
unzip -q "$PATCHES_ZIP" -d "$TMPDIR"
|
unzip -q "$PATCHES_ZIP" -d "$TMPDIR"
|
||||||
|
|
||||||
# Rewrite the config to point at the cached firmware and create output dir.
|
# Rewrite the config to point at the cached firmware and create output dir.
|
||||||
sed -i "s|^in:.*|in: $FIRMWARE_FILE|" "$TMPDIR/kobopatch.yaml"
|
sed -i "s|^in:.*|in: $FIRMWARE_FILE|" "$TMPDIR/kobopatch.yaml"
|
||||||
mkdir -p "$TMPDIR/out"
|
mkdir -p "$TMPDIR/out"
|
||||||
|
|
||||||
BLACKLIST_FILE="$(cd .. && pwd)/web/src/patches/blacklist.json"
|
|
||||||
VERSION="${VERSION:-4.45}"
|
|
||||||
|
|
||||||
# Run patch tests and capture output.
|
# Run patch tests and capture output.
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Testing patches against $(basename "$FIRMWARE_FILE") ==="
|
echo "=== Testing patches against kobo-update-${VERSION}.zip ==="
|
||||||
echo ""
|
echo ""
|
||||||
OUTPUT=$(./kobopatch -t -f "$FIRMWARE_FILE" "$TMPDIR/kobopatch.yaml" 2>&1 || true)
|
OUTPUT=$(./kobopatch -t -f "$FIRMWARE_FILE" "$TMPDIR/kobopatch.yaml" 2>&1 || true)
|
||||||
echo "$OUTPUT"
|
echo "$OUTPUT"
|
||||||
|
|
||||||
# Generate blacklist.json from failed patches.
|
# Update blacklist.json with failed patches for this version.
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Generating blacklist.json ==="
|
echo "=== Updating blacklist.json for $SHORT_VERSION ==="
|
||||||
echo "$OUTPUT" | python3 -c "
|
echo "$OUTPUT" | python3 -c "
|
||||||
import sys, json, os
|
import sys, json, os
|
||||||
|
|
||||||
version = '$VERSION'
|
version = '$SHORT_VERSION'
|
||||||
blacklist_file = '$BLACKLIST_FILE'
|
blacklist_file = '$BLACKLIST_FILE'
|
||||||
|
tmpdir = '$TMPDIR'
|
||||||
|
|
||||||
# Load existing blacklist to preserve other versions.
|
|
||||||
if os.path.exists(blacklist_file):
|
|
||||||
with open(blacklist_file) as f:
|
with open(blacklist_file) as f:
|
||||||
blacklist = json.load(f)
|
blacklist = json.load(f)
|
||||||
else:
|
|
||||||
blacklist = {}
|
|
||||||
|
|
||||||
# Map binary paths back to patch file names.
|
|
||||||
# kobopatch prints 'Patching ./usr/local/Kobo/libnickel.so.1.0.0' but we need 'src/libnickel.so.1.0.0.yaml'.
|
|
||||||
target_to_src = {}
|
|
||||||
current_file = None
|
current_file = None
|
||||||
failed = {}
|
failed = {}
|
||||||
|
|
||||||
for line in sys.stdin:
|
for line in sys.stdin:
|
||||||
line = line.rstrip()
|
line = line.rstrip()
|
||||||
if line.startswith('Patching ./'):
|
if line.startswith('Patching ./'):
|
||||||
target = line.split('Patching ./')[1]
|
current_file = line.split('Patching ./')[1]
|
||||||
current_file = target
|
|
||||||
elif '✕' in line and current_file:
|
elif '✕' in line and current_file:
|
||||||
name = line.split('✕')[1].strip()
|
name = line.split('✕')[1].strip()
|
||||||
failed.setdefault(current_file, []).append(name)
|
failed.setdefault(current_file, []).append(name)
|
||||||
|
|
||||||
# Read kobopatch.yaml to get target -> src mapping.
|
# Parse kobopatch.yaml patches section to get target -> src mapping.
|
||||||
# Parse the 'patches:' section without a YAML dependency.
|
|
||||||
src_to_target = {}
|
src_to_target = {}
|
||||||
in_patches = False
|
in_patches = False
|
||||||
with open('$TMPDIR/kobopatch.yaml') as f:
|
with open(os.path.join(tmpdir, 'kobopatch.yaml')) as f:
|
||||||
for cfg_line in f:
|
for cfg_line in f:
|
||||||
cfg_line = cfg_line.rstrip()
|
cfg_line = cfg_line.rstrip()
|
||||||
if cfg_line.startswith('patches:'):
|
if cfg_line.startswith('patches:'):
|
||||||
@@ -108,7 +116,7 @@ with open('$TMPDIR/kobopatch.yaml') as f:
|
|||||||
# Build a patch-name -> src file mapping by scanning patch files.
|
# Build a patch-name -> src file mapping by scanning patch files.
|
||||||
patch_name_to_src = {}
|
patch_name_to_src = {}
|
||||||
for src in src_to_target:
|
for src in src_to_target:
|
||||||
src_path = os.path.join('$TMPDIR', src)
|
src_path = os.path.join(tmpdir, src)
|
||||||
if not os.path.exists(src_path):
|
if not os.path.exists(src_path):
|
||||||
continue
|
continue
|
||||||
with open(src_path) as pf:
|
with open(src_path) as pf:
|
||||||
@@ -131,5 +139,12 @@ with open(blacklist_file, 'w') as f:
|
|||||||
f.write('\n')
|
f.write('\n')
|
||||||
|
|
||||||
total_failed = sum(len(v) for v in version_entry.values())
|
total_failed = sum(len(v) for v in version_entry.values())
|
||||||
print(f'Wrote {total_failed} blacklisted patch(es) for version {version} to {blacklist_file}')
|
print(f'Wrote {total_failed} blacklisted patch(es) for version {version}')
|
||||||
"
|
"
|
||||||
|
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
trap - EXIT
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Blacklist written to $BLACKLIST_FILE ==="
|
||||||
|
|||||||
35
test.sh
35
test.sh
@@ -30,21 +30,35 @@ done
|
|||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
CACHED_ASSETS="$SCRIPT_DIR/tests/cached_assets"
|
CACHED_ASSETS="$SCRIPT_DIR/tests/cached_assets"
|
||||||
|
FIRMWARE_CONFIG="$SCRIPT_DIR/tests/firmware-config.js"
|
||||||
|
|
||||||
FIRMWARE_FILE="$CACHED_ASSETS/kobo-update-4.45.23646.zip"
|
# Check if any firmware files need to be downloaded.
|
||||||
FIRMWARE_URL="https://ereaderfiles.kobo.com/firmwares/kobo13/Mar2026/kobo-update-4.45.23646.zip"
|
MISSING=()
|
||||||
|
while IFS= read -r line; do
|
||||||
|
version=$(echo "$line" | jq -r '.version')
|
||||||
|
url=$(echo "$line" | jq -r '.url')
|
||||||
|
file="$CACHED_ASSETS/kobo-update-${version}.zip"
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
MISSING+=("$version|$url|$file")
|
||||||
|
fi
|
||||||
|
done < <(node -e "console.log(JSON.stringify(require('$FIRMWARE_CONFIG')))" | jq -c '.[]')
|
||||||
|
|
||||||
# Check if firmware needs to be downloaded.
|
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||||
if [ ! -f "$FIRMWARE_FILE" ]; then
|
echo "The following firmware test assets are not cached locally (~150 MB each):"
|
||||||
echo "Firmware test asset is not cached locally (~150 MB)."
|
for entry in "${MISSING[@]}"; do
|
||||||
|
echo " - $(echo "$entry" | cut -d'|' -f1)"
|
||||||
|
done
|
||||||
echo ""
|
echo ""
|
||||||
read -rp "Download it now? Tests that need the firmware will be skipped otherwise. [y/N] " answer
|
read -rp "Download them now? Tests that need firmware will be skipped otherwise. [y/N] " answer
|
||||||
if [[ "$answer" =~ ^[Yy]$ ]]; then
|
if [[ "$answer" =~ ^[Yy]$ ]]; then
|
||||||
mkdir -p "$CACHED_ASSETS"
|
mkdir -p "$CACHED_ASSETS"
|
||||||
echo "Downloading firmware..."
|
for entry in "${MISSING[@]}"; do
|
||||||
curl -fL --progress-bar -o "$FIRMWARE_FILE.tmp" "$FIRMWARE_URL"
|
IFS='|' read -r version url file <<< "$entry"
|
||||||
mv "$FIRMWARE_FILE.tmp" "$FIRMWARE_FILE"
|
echo "Downloading firmware $version..."
|
||||||
|
curl -fL --progress-bar -o "$file.tmp" "$url"
|
||||||
|
mv "$file.tmp" "$file"
|
||||||
echo ""
|
echo ""
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -80,7 +94,8 @@ echo "=== Building WASM ==="
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Running WASM integration test ==="
|
echo "=== Running WASM integration test ==="
|
||||||
if [ -f "$FIRMWARE_FILE" ]; then
|
PRIMARY_FW="$CACHED_ASSETS/kobo-update-$(node -e "console.log(require('$FIRMWARE_CONFIG')[0].version)").zip"
|
||||||
|
if [ -f "$PRIMARY_FW" ]; then
|
||||||
"$SCRIPT_DIR/kobopatch-wasm/test-integration.sh"
|
"$SCRIPT_DIR/kobopatch-wasm/test-integration.sh"
|
||||||
else
|
else
|
||||||
echo "Skipped (firmware not downloaded)"
|
echo "Skipped (firmware not downloaded)"
|
||||||
|
|||||||
19
tests/firmware-config.js
Normal file
19
tests/firmware-config.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// Firmware versions used for testing. Shell scripts read this via jq-compatible
|
||||||
|
// JSON output from: node -e "console.log(JSON.stringify(require('./tests/firmware-config')))"
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
version: '4.45.23646',
|
||||||
|
shortVersion: '4.45',
|
||||||
|
url: 'https://ereaderfiles.kobo.com/firmwares/kobo13/Mar2026/kobo-update-4.45.23646.zip',
|
||||||
|
patches: 'patches_4.45.zip',
|
||||||
|
checksums: {
|
||||||
|
'usr/local/Kobo/libnickel.so.1.0.0': 'ef64782895a47ac85f0829f06fffa4816d23512d',
|
||||||
|
'usr/local/Kobo/nickel': '80a607bac515457a6864be8be831df631a01005c',
|
||||||
|
'usr/local/Kobo/libadobe.so': '02dc99c71c4fef75401cd49ddc2e63f928a126e1',
|
||||||
|
'usr/local/Kobo/librmsdk.so.1.0.0': 'e3819260c9fc539a53db47e9d3fe600ec11633d5',
|
||||||
|
},
|
||||||
|
// SHA1 of the original unmodified KoboRoot.tgz inside the firmware zip.
|
||||||
|
// Used to verify the "restore original firmware" flow extracts correctly.
|
||||||
|
originalTgzChecksum: 'b5c3307e8e7ec036f4601135f0b741c37b899db4',
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,25 +1,19 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
|
const firmwareConfig = require('../firmware-config');
|
||||||
|
|
||||||
const CACHED_ASSETS = path.resolve(__dirname, '..', 'cached_assets');
|
const CACHED_ASSETS = path.resolve(__dirname, '..', 'cached_assets');
|
||||||
|
|
||||||
const FIRMWARE_PATH = path.join(CACHED_ASSETS, 'kobo-update-4.45.23646.zip');
|
|
||||||
|
|
||||||
const WEBROOT = path.resolve(__dirname, '..', '..', 'web', 'dist');
|
const WEBROOT = path.resolve(__dirname, '..', '..', 'web', 'dist');
|
||||||
const WEBROOT_FIRMWARE = path.join(WEBROOT, '_test_firmware.zip');
|
const WEBROOT_FIRMWARE = path.join(WEBROOT, '_test_firmware.zip');
|
||||||
|
|
||||||
// Expected SHA1 checksums for Kobo Libra Color, firmware 4.45.23646,
|
// Primary firmware entry (first in config) is used for E2E/integration tests.
|
||||||
// with only "Remove footer (row3) on new home screen" enabled.
|
const primary = firmwareConfig[0];
|
||||||
const EXPECTED_SHA1 = {
|
const FIRMWARE_PATH = path.join(CACHED_ASSETS, `kobo-update-${primary.version}.zip`);
|
||||||
'usr/local/Kobo/libnickel.so.1.0.0': 'ef64782895a47ac85f0829f06fffa4816d23512d',
|
const EXPECTED_SHA1 = primary.checksums;
|
||||||
'usr/local/Kobo/nickel': '80a607bac515457a6864be8be831df631a01005c',
|
const ORIGINAL_TGZ_SHA1 = primary.originalTgzChecksum;
|
||||||
'usr/local/Kobo/libadobe.so': '02dc99c71c4fef75401cd49ddc2e63f928a126e1',
|
|
||||||
'usr/local/Kobo/librmsdk.so.1.0.0': 'e3819260c9fc539a53db47e9d3fe600ec11633d5',
|
|
||||||
};
|
|
||||||
|
|
||||||
// SHA1 of the original unmodified KoboRoot.tgz inside firmware 4.45.23646.
|
|
||||||
const ORIGINAL_TGZ_SHA1 = 'b5c3307e8e7ec036f4601135f0b741c37b899db4';
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
firmwareConfig,
|
||||||
FIRMWARE_PATH,
|
FIRMWARE_PATH,
|
||||||
WEBROOT,
|
WEBROOT,
|
||||||
WEBROOT_FIRMWARE,
|
WEBROOT_FIRMWARE,
|
||||||
|
|||||||
Reference in New Issue
Block a user