Skip to content

Commit

Permalink
feat: add allure diff support
Browse files Browse the repository at this point in the history
  • Loading branch information
rIIh committed Jul 16, 2024
1 parent f019c04 commit 2a84443
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 8 deletions.
6 changes: 5 additions & 1 deletion apps/sandbox/test/flutter_test_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import 'dart:async';

import 'package:alchemist/alchemist.dart';
import 'package:alchemist_test_reporter/alchemist_test_reporter.dart';
import 'package:allure_report/allure_report.dart';

Future<void> testExecutable(FutureOr<void> Function() testMain) {
const isRunningInCi = bool.fromEnvironment('CI', defaultValue: false);
goldenTestRunner = GoldenTestRunnerWithReports(inner: goldenTestRunner);
goldenTestRunner = GoldenTestRunnerWithReports(
inner: goldenTestRunner,
onAttachmentCreated: Allure.diff,
);

return AlchemistConfig.runWithConfig(
config: AlchemistConfig(
Expand Down
4 changes: 4 additions & 0 deletions packages/alchemist_test_reporter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.1.0

- **feat:** add diff file generation

## 1.0.1

- lower dart sdk version bound
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
// ignore_for_file: implementation_imports

import 'dart:io';
import 'dart:math';

import 'package:path/path.dart' as p;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:alchemist/alchemist.dart';
import 'package:alchemist/src/golden_test_runner.dart';
import 'package:image/image.dart' as img;
import 'package:diff_image2/diff_image2.dart';

typedef AttachmentCallback = void Function(
String expected,
String actual,
String diff,
);

class GoldenTestRunnerWithReports extends GoldenTestRunner {
final GoldenTestRunner _inner;
final AttachmentCallback? onAttachmentCreated;

GoldenTestRunnerWithReports({required GoldenTestRunner inner})
: _inner = inner;
GoldenTestRunnerWithReports({
required GoldenTestRunner inner,
this.onAttachmentCreated,
}) : _inner = inner;

@override
Future<void> run({
Expand Down Expand Up @@ -61,8 +73,15 @@ class GoldenTestRunnerWithReports extends GoldenTestRunner {
'failures',
'${name}_testImage$extension',
));
final diffFile = File(p.join(
Directory.current.path,
'test',
'failures',
'${name}_maskedDiff$extension',
));

if (masterFile.existsSync() && testFile.existsSync()) {
final time = DateTime.now();
final subfolder =
p.dirname(goldenPath.toString()).replaceAll('goldens/', '');

Expand All @@ -77,13 +96,69 @@ class GoldenTestRunnerWithReports extends GoldenTestRunner {
subfolder, p.basename(masterFile.path));
final test = p.join(Directory.current.path, 'reports', 'failures',
subfolder, p.basename(testFile.path));
final diff = p.join(Directory.current.path, 'reports', 'failures',
subfolder, p.basename(diffFile.path));

masterFile.copySync(base);
testFile.copySync(test);

print('event:attachment:$base');
print('event:attachment:$test');
try {
if (diffFile.existsSync()) {
diffFile.copySync(diff);
} else {
_fitImages(base, test);
_writeDiff(base, test, diff);
}

print('Elapsed - ${DateTime.now().difference(time)}');

if (onAttachmentCreated case AttachmentCallback onAttachmentCreated) {
onAttachmentCreated(base, test, diff);
} else {
print('event:attachment:$base');
print('event:attachment:$test');
print('event:attachment:$diff');
}
} catch (e) {
print(e.toString());
}
}
}
}
}

Future<void> _fitImages(String left, String right) async {
var (leftImage, rightImage) = (
img.decodePng(File(left).readAsBytesSync()),
img.decodePng(File(right).readAsBytesSync()),
);

if (leftImage == null || rightImage == null) return;

final newLeftImage = img.copyExpandCanvas(
leftImage,
newWidth: max(leftImage.width, rightImage.width),
newHeight: max(leftImage.height, rightImage.height),
position: img.ExpandCanvasPosition.topLeft,
backgroundColor: img.ColorRgba8(255, 255, 255, 255),
);
final newRightImage = img.copyExpandCanvas(
rightImage,
newWidth: max(leftImage.width, rightImage.width),
newHeight: max(leftImage.height, rightImage.height),
position: img.ExpandCanvasPosition.topLeft,
backgroundColor: img.ColorRgba8(255, 255, 255, 255),
);

File(left).writeAsBytesSync(img.encodePng(newLeftImage));
File(right).writeAsBytesSync(img.encodePng(newRightImage));
}

void _writeDiff(String left, String right, String diff) {
final leftFile = img.decodePng(File(left).readAsBytesSync());
final rightFile = img.decodePng(File(right).readAsBytesSync());

var diffResult = DiffImage.compareFromMemory(leftFile!, rightFile!);
final png = img.encodePng(diffResult.diffImage);
File(diff).writeAsBytesSync(png);
}
4 changes: 3 additions & 1 deletion packages/alchemist_test_reporter/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: alchemist_test_reporter
description: Alchemist Test Reporter Addon
version: 1.0.1
version: 1.1.0
homepage: https://github.com/rIIh/dart_test_reporter
issue_tracker: https://github.com/rIIh/dart_test_reporter/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+%5Balchemist_test_reporter%5D

Expand All @@ -17,6 +17,8 @@ dependencies:
alchemist: ^0.7.0
test: ^1.24.0
path: ^1.0.0
image: ^4.2.0
diff_image2: ^1.2.1


dev_dependencies:
Expand Down
5 changes: 5 additions & 0 deletions packages/allure_report/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.2.0

- **feat:** add diff comparison support


## 1.1.1

- lower dart sdk version bound
Expand Down
3 changes: 3 additions & 0 deletions packages/allure_report/lib/src/allure.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ abstract final class Allure {

// #endregion

static void diff(String expected, String actual, String diff) =>
print('allure:event:diff:$expected:$actual:$diff');

static void severity(Severity severity) =>
print('allure:event:severity:${severity.name}');

Expand Down
36 changes: 35 additions & 1 deletion packages/allure_report/lib/src/allure_reporter.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'dart:async';
import 'dart:convert';

import 'package:allure_report/src/events/diff_event.dart';
import 'package:allure_report/src/models/allure_link.dart';
import 'package:allure_report/src/models/severity.dart';
import 'package:allure_report/src/test_report.dart';
import 'package:mime/mime.dart';
import 'package:test_reporter/test_reporter.dart';
import 'package:universal_io/io.dart';
import 'package:uuid/data.dart';
Expand Down Expand Up @@ -36,7 +38,7 @@ class AllureReporter implements TestReporter {
}

@override
FutureOr<void> onEvent(TestEvent event) {
Future<void> onEvent(TestEvent event) async {
switch (event) {
case TestSuiteEvent event:
suites[event.suite.id] = event.suite;
Expand All @@ -59,6 +61,33 @@ class AllureReporter implements TestReporter {
],
);

case TestMessageEvent event
when event.messageType == 'print' &&
DiffEvent.isEligible(event.message):
try {
final diffEvent = DiffEvent.fromMessage(event.message);
final expectedBytes = await File(diffEvent.expected).readAsBytes();
final actualBytes = await File(diffEvent.actual).readAsBytes();
final diffBytes = await File(diffEvent.diff).readAsBytes();

final id = Uuid().v4();
final allureDiff = p.join(Directory.systemTemp.path, '$id.imagediff');
File(allureDiff).writeAsString(jsonEncode({
'expected': 'data:image/png;base64,${base64.encode(expectedBytes)}',
'actual': 'data:image/png;base64,${base64.encode(actualBytes)}',
'diff': 'data:image/png;base64,${base64.encode(diffBytes)}',
}));

tests[event.testID] = tests[event.testID]!.copyWith(
attachments: [
...tests[event.testID]!.attachments,
allureDiff,
],
);
} catch (e) {
print("[E]: Failed to create diff: $e");
}

case TestMessageEvent event
when event.messageType == 'print' &&
event.message.startsWith('allure:event:') &&
Expand Down Expand Up @@ -141,6 +170,11 @@ class AllureReporter implements TestReporter {
attachments.add({
'name': p.basenameWithoutExtension(path),
'source': p.basename(path),
'type': switch (p.basename(path)) {
String filename when filename.endsWith('.imagediff') =>
'application/vnd.allure.image.diff',
String filename => lookupMimeType(filename)
},
});
}

Expand Down
22 changes: 22 additions & 0 deletions packages/allure_report/lib/src/events/diff_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class DiffEvent {
static const kTag = 'allure:event:diff';

static bool isEligible(String message) {
return message.startsWith('$kTag:') &&
':'.allMatches(message.replaceAll('$kTag:', '')).length == 2;
}

DiffEvent(this.expected, this.actual, this.diff);

factory DiffEvent.fromMessage(String message) {
final [expected, actual, diff] = message //
.replaceAll('$kTag:', '')
.split(':');

return DiffEvent(expected, actual, diff);
}

final String expected;
final String actual;
final String diff;
}
3 changes: 2 additions & 1 deletion packages/allure_report/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: allure_report
description: Allure Report Adapter. Use it with [test_reporter](https://pub.dev/packages/test_reporter) package
version: 1.1.1
version: 1.2.0
repository: https://github.com/rIIh/dart_test_reporter
issue_tracker: https://github.com/rIIh/dart_test_reporter/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+%5Ballure_report%5D

Expand All @@ -13,6 +13,7 @@ dependencies:
test_reporter: ^1.0.0
universal_io: ^2.2.2
uuid: ^4.4.0
mime: ^1.0.5

dev_dependencies:
build_runner: ^2.4.11
Expand Down

0 comments on commit 2a84443

Please sign in to comment.