diff --git a/res/pictures/hash-wordmark-light.svg b/res/pictures/hash-wordmark-light.svg index 7d649c449..3a297c6ea 100644 --- a/res/pictures/hash-wordmark-light.svg +++ b/res/pictures/hash-wordmark-light.svg @@ -1,34 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +Hash Bags horizontal lockup +Hash Bags logo with green-boxed hash mark on the left and Hash Bags wordmark to the right. + +# +Hash Bags diff --git a/res/pictures/hash-wordmark.svg b/res/pictures/hash-wordmark.svg index 08d6e195b..161472a75 100644 --- a/res/pictures/hash-wordmark.svg +++ b/res/pictures/hash-wordmark.svg @@ -1,32 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +Hash Bags horizontal lockup (dark theme) +Hash Bags logo with green-boxed hash mark on the left and a white wordmark to the right, intended for dark-theme backgrounds. + +# +Hash Bags diff --git a/res/pictures/hash_squircle_icon.svg b/res/pictures/hash_squircle_icon.svg index 8bafc44c4..602461007 100644 --- a/res/pictures/hash_squircle_icon.svg +++ b/res/pictures/hash_squircle_icon.svg @@ -1,36 +1,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +Hash Bags app icon +Hash Bags app icon: brand-green rounded square with a centered white hash. + +# diff --git a/test/brand_assets_match_source_test.dart b/test/brand_assets_match_source_test.dart new file mode 100644 index 000000000..a6034687c --- /dev/null +++ b/test/brand_assets_match_source_test.dart @@ -0,0 +1,57 @@ +import 'dart:io'; +import 'package:flutter_test/flutter_test.dart'; + +// compile_graphics.sh compiles res/pictures/*.svg into assets/new-ui/*.svg.vec, +// and CI runs it on every platform. That makes res/pictures the source and +// assets/new-ui the output, which is easy to get backwards: the files sit side +// by side, have identical names, and the ones under assets/ are the ones the +// Dart code names. +// +// Getting it backwards shipped Cake Wallet's artwork twice. The August de-cake +// pass replaced the copies under assets/new-ui and left res/pictures untouched, +// so every CI build recompiled Cake's icon over the top: their app icon on the +// About screen, and their wordmark on the receive QR page, which is the screen +// a user screenshots to get paid. A local build looked right, because locally +// nobody re-runs the compiler. +// +// So: any SVG present in both places must be byte-identical. If they drift, the +// one under assets/ is a hand-edit that the next CI build will silently revert. +void main() { + test('brand SVGs under assets/new-ui match their res/pictures source', () { + final assetsDir = Directory('assets/new-ui'); + final sourceDir = Directory('res/pictures'); + expect(assetsDir.existsSync(), isTrue, reason: 'no assets/new-ui — wrong CWD?'); + expect(sourceDir.existsSync(), isTrue, reason: 'no res/pictures — wrong CWD?'); + + final drifted = []; + var compared = 0; + for (final f in assetsDir.listSync().whereType()) { + if (!f.path.endsWith('.svg')) continue; + final name = f.uri.pathSegments.last; + final source = File('${sourceDir.path}/$name'); + if (!source.existsSync()) continue; // not compiled from res/pictures + compared++; + final a = f.readAsBytesSync(); + final b = source.readAsBytesSync(); + if (a.length != b.length || !_same(a, b)) { + drifted.add( + '$name: assets/new-ui is ${a.length}b, res/pictures is ${b.length}b. ' + 'Edit res/pictures and re-run ./compile_graphics.sh; the assets copy ' + 'is output and CI overwrites it.', + ); + } + } + + // A silent zero would make this test vacuous, which is how the last two + // branding regressions reached users. + expect(compared, greaterThan(0), reason: 'compared no SVG pairs at all'); + expect(drifted, isEmpty, reason: 'generated brand assets drifted from source:\n${drifted.join('\n')}'); + }); +} + +bool _same(List a, List b) { + for (var i = 0; i < a.length; i++) { + if (a[i] != b[i]) return false; + } + return true; +}