Compare commits
8 Commits
5fde4e0d93
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9af115e403 | |||
| 5105d9e582 | |||
| 4e982e6a2b | |||
| 469bdf7389 | |||
| 5b710c7b67 | |||
| 3b39bacc80 | |||
| 5080958415 | |||
| 451cbdae15 |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 492 KiB After Width: | Height: | Size: 492 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 422 KiB After Width: | Height: | Size: 422 KiB |
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 3.1 MiB |
|
Before Width: | Height: | Size: 784 KiB After Width: | Height: | Size: 784 KiB |
|
Before Width: | Height: | Size: 907 KiB After Width: | Height: | Size: 906 KiB |
|
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 308 KiB After Width: | Height: | Size: 308 KiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 370 KiB After Width: | Height: | Size: 370 KiB |
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 890 KiB After Width: | Height: | Size: 890 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 3.7 MiB After Width: | Height: | Size: 3.7 MiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 4.9 MiB After Width: | Height: | Size: 4.9 MiB |
|
Before Width: | Height: | Size: 3.6 MiB After Width: | Height: | Size: 3.6 MiB |
|
Before Width: | Height: | Size: 832 KiB After Width: | Height: | Size: 833 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 803 KiB After Width: | Height: | Size: 803 KiB |
|
Before Width: | Height: | Size: 955 KiB After Width: | Height: | Size: 956 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 416 KiB After Width: | Height: | Size: 416 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 880 KiB After Width: | Height: | Size: 881 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 779 KiB After Width: | Height: | Size: 779 KiB |
|
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 3.0 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 578 KiB After Width: | Height: | Size: 578 KiB |
|
Before Width: | Height: | Size: 404 KiB After Width: | Height: | Size: 404 KiB |
|
Before Width: | Height: | Size: 592 KiB After Width: | Height: | Size: 592 KiB |
|
Before Width: | Height: | Size: 406 KiB After Width: | Height: | Size: 406 KiB |
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 3.0 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 336 KiB After Width: | Height: | Size: 336 KiB |
|
Before Width: | Height: | Size: 756 KiB After Width: | Height: | Size: 756 KiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 278 KiB After Width: | Height: | Size: 278 KiB |
|
Before Width: | Height: | Size: 379 KiB After Width: | Height: | Size: 379 KiB |
|
Before Width: | Height: | Size: 679 KiB After Width: | Height: | Size: 679 KiB |
|
Before Width: | Height: | Size: 671 KiB After Width: | Height: | Size: 671 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 506 KiB After Width: | Height: | Size: 506 KiB |
|
Before Width: | Height: | Size: 832 KiB After Width: | Height: | Size: 832 KiB |
|
Before Width: | Height: | Size: 752 KiB After Width: | Height: | Size: 752 KiB |
|
Before Width: | Height: | Size: 257 KiB After Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1015 KiB After Width: | Height: | Size: 1016 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 932 KiB After Width: | Height: | Size: 933 KiB |
|
Before Width: | Height: | Size: 543 KiB After Width: | Height: | Size: 543 KiB |
47
flake.lock
generated
@@ -31,15 +31,16 @@
|
|||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
|
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||||
"resume": "resume",
|
"resume": "resume",
|
||||||
"web-files": "web-files"
|
"web-files": "web-files"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1768847023,
|
"lastModified": 1779238599,
|
||||||
"narHash": "sha256-r9ah5RgR00FyZ9HENdyY52cK8T+Bw0YYitOwSHpng0Q=",
|
"narHash": "sha256-ZfuJmoP8aMt4ecCirmRTgx7mAKqgjaWjNZs0Rc6WKGs=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "78213700c8b858e0f92c4ff77d5c3c6dc4fe690d",
|
"rev": "4481c38cf3bed9a110b698afde2d6f7da02cb815",
|
||||||
"revCount": 227,
|
"revCount": 238,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://dev.danilafe.com/Nix-Configs/blog-static-flake.git"
|
"url": "https://dev.danilafe.com/Nix-Configs/blog-static-flake.git"
|
||||||
},
|
},
|
||||||
@@ -51,11 +52,11 @@
|
|||||||
"blog-source": {
|
"blog-source": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1768846982,
|
"lastModified": 1779238547,
|
||||||
"narHash": "sha256-q8lz1UUvA4D3e2Ankqj1naDxIykVt5k4OX5HjlHS1gY=",
|
"narHash": "sha256-rXs/xEhHugC5Z0ARzmShqEbXnKBEdg2NE+7M/zMHmQw=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "4d35ca04fea950136d59a371942f84044b87b513",
|
"rev": "98e2e7da6c9b87df24ea42ec76fb955ad6c59950",
|
||||||
"revCount": 927,
|
"revCount": 958,
|
||||||
"submodules": true,
|
"submodules": true,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://dev.danilafe.com/Web-Projects/blog-static.git"
|
"url": "https://dev.danilafe.com/Web-Projects/blog-static.git"
|
||||||
@@ -158,11 +159,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1768773494,
|
"lastModified": 1778430510,
|
||||||
"narHash": "sha256-XsM7GP3jHlephymxhDE+/TKKO1Q16phz/vQiLBGhpF4=",
|
"narHash": "sha256-Ti+ZBvW6yrWWAg2szExVTwCd4qOJ3KlVr1tFHfyfi8Q=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "77ef7a29d276c6d8303aece3444d61118ef71ac2",
|
"rev": "8fd9daa3db09ced9700431c5b7ad0e8ba199b575",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -172,6 +173,22 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs-unstable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777954456,
|
||||||
|
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"resume": {
|
"resume": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_2",
|
"flake-utils": "flake-utils_2",
|
||||||
@@ -181,11 +198,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1766794825,
|
"lastModified": 1778642986,
|
||||||
"narHash": "sha256-+Fabo0uQF9srEXdi9TB1wjB+PHaU9htXj/fjnvUFNAs=",
|
"narHash": "sha256-+0DskouobfQ0eEaJRzWACvjHswnRtdXMw4wl1Y/TLNg=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "f22cb5b79580020079ba7223346e612b41eb7d42",
|
"rev": "7f8d95c5e40ce63f7173d781d494ae44142a862c",
|
||||||
"revCount": 74,
|
"revCount": 76,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://dev.danilafe.com/DanilaFe/resume"
|
"url": "https://dev.danilafe.com/DanilaFe/resume"
|
||||||
},
|
},
|
||||||
|
|||||||
26
flake.nix
@@ -10,33 +10,33 @@
|
|||||||
flake-utils.lib.eachDefaultSystem (system:
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
let pkgs = import nixpkgs { inherit system; };
|
let pkgs = import nixpkgs { inherit system; };
|
||||||
builtBlog = blog.defaultPackage.${system};
|
builtBlog = blog.defaultPackage.${system};
|
||||||
blogWithRelativeLinks = pkgs.stdenv.mkDerivation {
|
blogWithRelativeLinks = pkgs.stdenv.mkDerivation {
|
||||||
name = "blogWithRelativeLinks";
|
name = "blogWithRelativeLinks";
|
||||||
version = "1.0";
|
version = "1.0";
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
(pkgs.python3.withPackages (ps: [
|
(pkgs.python3.withPackages (ps: [
|
||||||
ps.beautifulsoup4
|
ps.beautifulsoup4
|
||||||
ps.lxml
|
ps.lxml
|
||||||
]))
|
]))
|
||||||
];
|
];
|
||||||
builtBlog = builtBlog;
|
builtBlog = builtBlog;
|
||||||
urlScript = pkgs.writeTextFile { name = "chatgpt-fix-root-URLs.py"; text = builtins.readFile ./scripts/chatgpt-fix-root-URLs.py; };
|
urlScript = pkgs.writeTextFile { name = "chatgpt-fix-root-URLs.py"; text = builtins.readFile ./scripts/chatgpt-fix-root-URLs.py; };
|
||||||
builder = builtins.toFile "builder.sh" "
|
builder = builtins.toFile "builder.sh" "
|
||||||
source $stdenv/setup
|
source $stdenv/setup
|
||||||
mkdir -p code $out
|
mkdir -p code $out
|
||||||
cp -r $builtBlog/* code
|
cp -r $builtBlog/* code
|
||||||
chmod -R u+w code
|
chmod -R u+w code
|
||||||
(cd code && python3 $urlScript)
|
(cd code && python3 $urlScript)
|
||||||
cp -r code/* $out
|
cp -r code/* $out
|
||||||
";
|
";
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
devShell = pkgs.mkShell {
|
devShell = pkgs.mkShell {
|
||||||
packages = [
|
packages = [
|
||||||
pkgs.nodejs
|
pkgs.nodejs
|
||||||
pkgs.yarn
|
pkgs.yarn
|
||||||
pkgs.chromium
|
pkgs.chromium
|
||||||
pkgs.python3
|
pkgs.python3
|
||||||
];
|
];
|
||||||
|
|
||||||
# Configure to use system Chromium
|
# Configure to use system Chromium
|
||||||
|
|||||||
@@ -1,33 +1,18 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test';
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
|
||||||
/**
|
|
||||||
* Read environment variables from file.
|
|
||||||
* https://github.com/motdotla/dotenv
|
|
||||||
*/
|
|
||||||
// import dotenv from 'dotenv';
|
|
||||||
// import path from 'path';
|
|
||||||
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See https://playwright.dev/docs/test-configuration.
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './e2e',
|
testDir: './e2e',
|
||||||
/* Run tests in files in parallel */
|
/* Run tests in files in parallel */
|
||||||
fullyParallel: false,
|
fullyParallel: true,
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
/* Retry on CI only */
|
|
||||||
retries: process.env.CI ? 2 : 2,
|
|
||||||
/* Opt out of parallel tests on CI. */
|
|
||||||
workers: process.env.CI ? 1 : 1,
|
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
reporter: 'line',
|
reporter: 'line',
|
||||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
use: {
|
use: {
|
||||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
|
||||||
// baseURL: 'http://127.0.0.1:3000',
|
|
||||||
|
|
||||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
|
|
||||||
@@ -35,7 +20,7 @@ export default defineConfig({
|
|||||||
colorScheme: 'light',
|
colorScheme: 'light',
|
||||||
|
|
||||||
launchOptions: {
|
launchOptions: {
|
||||||
executablePath: process.env.PLAYWRIGHT_CHROMIUM_PATH
|
executablePath: process.env.PLAYWRIGHT_CHROMIUM_PATH,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -45,48 +30,18 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
/* Configure projects for Chrome */
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'chromium',
|
name: 'chromium',
|
||||||
use: { ...devices['Desktop Chrome'] },
|
use: { ...devices['Desktop Chrome'] },
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
|
||||||
// name: 'firefox',
|
|
||||||
// use: { ...devices['Desktop Firefox'] },
|
|
||||||
// },
|
|
||||||
|
|
||||||
// {
|
|
||||||
// name: 'webkit',
|
|
||||||
// use: { ...devices['Desktop Safari'] },
|
|
||||||
// },
|
|
||||||
|
|
||||||
/* Test against mobile viewports. */
|
|
||||||
// {
|
|
||||||
// name: 'Mobile Chrome',
|
|
||||||
// use: { ...devices['Pixel 5'] },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: 'Mobile Safari',
|
|
||||||
// use: { ...devices['iPhone 12'] },
|
|
||||||
// },
|
|
||||||
|
|
||||||
/* Test against branded browsers. */
|
|
||||||
// {
|
|
||||||
// name: 'Microsoft Edge',
|
|
||||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: 'Google Chrome',
|
|
||||||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/* Run your local dev server before starting the tests */
|
/* Run your local dev server before starting the tests */
|
||||||
// webServer: {
|
webServer: {
|
||||||
// command: 'npm run start',
|
command: `python3 -m http.server 8081 --directory ${process.env.blogWithRelativeLinks}`,
|
||||||
// url: 'http://127.0.0.1:3000',
|
url: 'http://localhost:8081',
|
||||||
// reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
// },
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
118
scripts/chatgpt-check-offset-diff.py
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from PIL import Image, ImageChops
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
|
||||||
|
def load_rgb(path: Path) -> Image.Image:
|
||||||
|
return Image.open(path).convert("RGB")
|
||||||
|
|
||||||
|
def mse(img: Image.Image) -> float:
|
||||||
|
hist = img.histogram()
|
||||||
|
sq = sum(v * ((i % 256) ** 2) for i, v in enumerate(hist))
|
||||||
|
return sq / (img.width * img.height * 3)
|
||||||
|
|
||||||
|
def crop_pair(expected, actual, shift):
|
||||||
|
# shift > 0 means actual content is lower than expected.
|
||||||
|
w = min(expected.width, actual.width)
|
||||||
|
|
||||||
|
if shift >= 0:
|
||||||
|
e_box = (0, 0, w, min(expected.height, actual.height - shift))
|
||||||
|
a_box = (0, shift, w, shift + (e_box[3] - e_box[1]))
|
||||||
|
else:
|
||||||
|
s = -shift
|
||||||
|
a_box = (0, 0, w, min(actual.height, expected.height - s))
|
||||||
|
e_box = (0, s, w, s + (a_box[3] - a_box[1]))
|
||||||
|
|
||||||
|
return expected.crop(e_box), actual.crop(a_box)
|
||||||
|
|
||||||
|
def best_vertical_shift(expected, actual, max_shift=100):
|
||||||
|
best = None
|
||||||
|
|
||||||
|
for shift in range(18, 19):
|
||||||
|
e, a = crop_pair(expected, actual, shift)
|
||||||
|
if e.height <= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
score = mse(ImageChops.difference(e, a))
|
||||||
|
if best is None or score < best[1]:
|
||||||
|
best = (shift, score)
|
||||||
|
|
||||||
|
return best
|
||||||
|
|
||||||
|
def changed_until_y(expected, actual, threshold=5, min_changed_pixels=10):
|
||||||
|
diff = ImageChops.difference(expected, actual)
|
||||||
|
last_changed = None
|
||||||
|
|
||||||
|
for y in range(diff.height):
|
||||||
|
row = diff.crop((0, y, diff.width, y + 1)).convert("L")
|
||||||
|
changed = sum(1 for px in row.getdata() if px > threshold)
|
||||||
|
|
||||||
|
if changed >= min_changed_pixels:
|
||||||
|
last_changed = y
|
||||||
|
|
||||||
|
return {
|
||||||
|
"last_changed_y": last_changed,
|
||||||
|
"clean_after_y": 0 if last_changed is None else last_changed + 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
def find_pairs(root: Path):
|
||||||
|
expected_files = sorted(root.rglob("*-expected.png"))
|
||||||
|
|
||||||
|
for expected in expected_files:
|
||||||
|
actual = expected.with_name(
|
||||||
|
expected.name.replace("-expected.png", "-actual.png")
|
||||||
|
)
|
||||||
|
if actual.exists():
|
||||||
|
yield expected, actual
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("root", type=Path, nargs="?", default=Path("test-results"))
|
||||||
|
ap.add_argument("--max-shift", type=int, default=100)
|
||||||
|
ap.add_argument("--make-diffs", action="store_true")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
failed = False
|
||||||
|
|
||||||
|
for expected_path, actual_path in find_pairs(args.root):
|
||||||
|
expected = load_rgb(expected_path)
|
||||||
|
actual = load_rgb(actual_path)
|
||||||
|
|
||||||
|
raw_score = mse(ImageChops.difference(
|
||||||
|
expected.crop((0, 0, min(expected.width, actual.width), min(expected.height, actual.height))),
|
||||||
|
actual.crop((0, 0, min(expected.width, actual.width), min(expected.height, actual.height))),
|
||||||
|
))
|
||||||
|
|
||||||
|
shift, shifted_score = best_vertical_shift(
|
||||||
|
expected, actual, max_shift=args.max_shift
|
||||||
|
)
|
||||||
|
|
||||||
|
status = "OK" if shifted_score < raw_score * 0.05 else "CHECK"
|
||||||
|
if status == "CHECK":
|
||||||
|
failed = True
|
||||||
|
|
||||||
|
rel = expected_path.relative_to(args.root)
|
||||||
|
print(f"{status} {rel}")
|
||||||
|
print(f" expected: {expected.size}, actual: {actual.size}")
|
||||||
|
print(f" best shift: {shift:+d}px")
|
||||||
|
print(f" raw mse: {raw_score:.3f}")
|
||||||
|
print(f" shifted mse: {shifted_score:.3f}")
|
||||||
|
|
||||||
|
if args.make_diffs:
|
||||||
|
e, a = crop_pair(expected, actual, shift)
|
||||||
|
change_info = changed_until_y(e, a, threshold=2)
|
||||||
|
print(f" last changed y after shift: {change_info['last_changed_y']}")
|
||||||
|
print(f" clean after y after shift: {change_info['clean_after_y']}")
|
||||||
|
diff = ImageChops.difference(e, a)
|
||||||
|
out = expected_path.with_name(
|
||||||
|
expected_path.name.replace("-expected.png", f"-shift-{shift:+d}-diff.png")
|
||||||
|
)
|
||||||
|
diff.save(out)
|
||||||
|
print(f" wrote: {out}")
|
||||||
|
|
||||||
|
raise SystemExit(1 if failed else 0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||