106 lines
3.9 KiB
Dart
106 lines
3.9 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:fuel_tax_tracker/services/vin_parser.dart';
|
|
|
|
void main() {
|
|
group('VinParser', () {
|
|
test('parses a bare 17-character VIN', () {
|
|
const text = '''
|
|
VEHICLE IDENTIFICATION NUMBER
|
|
1HGCM82633A004352
|
|
MFD BY HONDA MFG CO
|
|
''';
|
|
|
|
expect(VinParser.parse(text), '1HGCM82633A004352');
|
|
});
|
|
|
|
test('prefers a "VIN:" labeled match over another candidate elsewhere', () {
|
|
const text = '''
|
|
PART NO 4F2CZ58899BA12345
|
|
VIN: 1HGCM82633A004352
|
|
''';
|
|
|
|
expect(VinParser.parse(text), '1HGCM82633A004352');
|
|
});
|
|
|
|
test('normalizes to uppercase', () {
|
|
expect(VinParser.parse('vin: 1hgcm82633a004352'), '1HGCM82633A004352');
|
|
});
|
|
|
|
test('normalizes an O to 0 — real VINs never contain one, so it reads as a misread zero', () {
|
|
// Real OCR output from a Kia/Hyundai data plate photo: "0" (zero)
|
|
// came back as "O" (letter) — this is exactly why I/O/Q are excluded
|
|
// from real VINs in the first place (too easily confused with
|
|
// 1/0/9), which cuts both ways for OCR reading one back off a plate.
|
|
expect(VinParser.parse('V.I.N 5XYZKDAG2BGO50982 TYPE:MPY'), '5XYZKDAG2BG050982');
|
|
});
|
|
|
|
test('normalizes an I to 1 the same way', () {
|
|
expect(VinParser.parse('VIN: 1HGCM82633AI04352'), '1HGCM82633A104352');
|
|
});
|
|
|
|
test('does not match a run containing Q — unlike I/O, not a plausible misread digit', () {
|
|
expect(VinParser.parse('1HGCM82633AQ04352'), isNull);
|
|
});
|
|
|
|
test('retries with whitespace stripped when OCR splits the VIN across a stray space', () {
|
|
// Real OCR output from an etched-glass VIN photo: a space landed
|
|
// mid-VIN (likely two separate detected text regions on the curved
|
|
// surface), so no single unbroken 17-character run exists until the
|
|
// whitespace is collapsed.
|
|
expect(VinParser.parse('5XYZKD AG2BG050982'), '5XYZKDAG2BG050982');
|
|
});
|
|
|
|
test('a whitespace-stripped retry is only a last resort, after a normal match is preferred',
|
|
() {
|
|
const text = '''
|
|
PART NO 4F2CZ 58899BA12345
|
|
VIN: 1HGCM82633A004352
|
|
''';
|
|
// The "VIN:"-labeled match (unaffected by the space in the unrelated
|
|
// part number above it) should win over anything the fallback might
|
|
// have found in the stripped text.
|
|
expect(VinParser.parse(text), '1HGCM82633A004352');
|
|
});
|
|
|
|
test('does not match a 16-character (too short) run', () {
|
|
expect(VinParser.parse('1HGCM82633A00435'), isNull);
|
|
});
|
|
|
|
test('does not match an 18-character (too long) run', () {
|
|
expect(VinParser.parse('1HGCM82633A0043521'), isNull);
|
|
});
|
|
|
|
test('returns null when nothing matches', () {
|
|
expect(VinParser.parse('THANK YOU FOR YOUR PURCHASE'), isNull);
|
|
});
|
|
|
|
test('matches a "V.I.N" label (period between each letter) — common on compliance/data plates',
|
|
() {
|
|
// Real text from a Kia/Hyundai-built data plate: "V.I.N" rather than
|
|
// "VIN:" — a manufacturer plate styling the labeled pattern didn't
|
|
// originally account for, so this fell through to relying on OCR
|
|
// reading the bare 17-char run cleanly.
|
|
const text = '''
|
|
THIS VEHICLE CONFORMS TO ALL APPLICABLE U.S.A. FEDERAL
|
|
MOTOR VEHICLE SAFETY AND THEFT PREVENTION STANDARDS
|
|
IN EFFECT ON THE DATE OF MANUFACTURE SHOWN ABOVE
|
|
V.I.N 5XYZKDAG2BG050982 TYPE:MPV
|
|
''';
|
|
|
|
expect(VinParser.parse(text), '5XYZKDAG2BG050982');
|
|
});
|
|
|
|
test('matches "V.I.N." with a trailing period too', () {
|
|
expect(VinParser.parse('V.I.N. 5XYZKDAG2BG050982'), '5XYZKDAG2BG050982');
|
|
});
|
|
|
|
test('a "V.I.N" labeled match is still preferred over another candidate elsewhere', () {
|
|
const text = '''
|
|
PART NO 4F2CZ58899BA12345
|
|
V.I.N 5XYZKDAG2BG050982
|
|
''';
|
|
|
|
expect(VinParser.parse(text), '5XYZKDAG2BG050982');
|
|
});
|
|
});
|
|
}
|