blob: b2bb0e1d0c8bcf6e10cda4a1625435e6fc8ce222 [file]
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:record_use/record_use.dart';
import 'package:record_use/src/canonicalization_context.dart';
import 'package:test/test.dart';
void main() {
group('Canonicalization', () {
test('canonicalizeConstant deduplicates identical constants', () {
final context = CanonicalizationContext();
// We avoid 'const' to ensure we have non-identical objects that are
// semantically equal, verifying that the canonicalization logic
// correctly deduplicates them.
// ignore: prefer_const_constructors
final c1 = IntConstant(42);
// ignore: prefer_const_constructors
final c2 = IntConstant(42);
expect(identical(c1, c2), isFalse);
final canonical1 = context.canonicalizeConstant(c1);
final canonical2 = context.canonicalizeConstant(c2);
expect(identical(canonical1, canonical2), isTrue);
});
test('canonicalizeConstant handles nested constants', () {
final context = CanonicalizationContext();
// ignore: prefer_const_constructors
final list1 = ListConstant([
// ignore: prefer_const_constructors
IntConstant(1),
// ignore: prefer_const_constructors
IntConstant(2),
]);
// ignore: prefer_const_constructors
final list2 = ListConstant([
// ignore: prefer_const_constructors
IntConstant(1),
// ignore: prefer_const_constructors
IntConstant(2),
]);
expect(identical(list1, list2), isFalse);
final canonical1 = context.canonicalizeConstant(list1);
final canonical2 = context.canonicalizeConstant(list2);
expect(identical(canonical1, canonical2), isTrue);
expect(identical(canonical1.value[0], canonical2.value[0]), isTrue);
});
test('Recordings.toJson canonicalizes constants across calls', () {
const definition = Method(
'foo',
Library('package:a/a.dart'),
);
const constant = IntConstant(42);
final recordings = Recordings(
calls: {
definition: [
const CallWithArguments(
positionalArguments: [constant],
namedArguments: {},
loadingUnit: LoadingUnit(''),
),
const CallWithArguments(
positionalArguments: [constant],
namedArguments: {},
loadingUnit: LoadingUnit(''),
),
],
},
instances: {},
);
final json = recordings.toJson();
final constants = json['constants'] as List;
// The constant 42 should only appear once in the constants table.
expect(constants, hasLength(1));
expect(constants[0], {'type': 'int', 'value': 42});
// Both calls are identical, so they should be deduplicated into one.
final uses = json['uses'] as Map;
final staticCalls = uses['static_calls'] as List;
final recording = staticCalls[0] as Map;
final calls = recording['uses'] as List;
expect(calls, hasLength(1));
expect((calls[0] as Map)['positional'], [0]);
});
test('Recordings.toJson deduplicates and sorts references', () {
const definition = Method(
'foo',
Library('package:a/a.dart'),
);
const unit1 = LoadingUnit('1');
const unit2 = LoadingUnit('2');
final recordings = Recordings(
calls: {
definition: [
const CallWithArguments(
positionalArguments: [IntConstant(2)],
namedArguments: {},
loadingUnit: unit1,
),
const CallWithArguments(
positionalArguments: [IntConstant(1)],
namedArguments: {},
loadingUnit: unit1,
),
const CallWithArguments(
positionalArguments: [IntConstant(1)],
namedArguments: {},
loadingUnit: unit1,
),
const CallWithArguments(
positionalArguments: [IntConstant(1)],
namedArguments: {},
loadingUnit: unit2,
),
],
},
instances: {},
);
final json = recordings.toJson();
final uses = json['uses'] as Map;
final staticCalls = uses['static_calls'] as List;
final recording = staticCalls[0] as Map;
final calls = recording['uses'] as List;
// Should have 3 unique calls after deduplication, sorted by toString.
// CallWithArguments(positional: IntConstant(1), loadingUnit: 1)
// CallWithArguments(positional: IntConstant(1), loadingUnit: 2)
// CallWithArguments(positional: IntConstant(2), loadingUnit: 1)
expect(calls, hasLength(3));
final backAgain = Recordings.fromJson(json);
final backCalls = backAgain.calls[definition]!;
expect(backCalls, hasLength(3));
// Verify sorting (based on toString which includes positional args and
// loading units)
expect(
backCalls[0].toString(),
contains('positional: IntConstant(1), loadingUnit: 1'),
);
expect(
backCalls[1].toString(),
contains('positional: IntConstant(2), loadingUnit: 1'),
);
expect(
backCalls[2].toString(),
contains('positional: IntConstant(1), loadingUnit: 2'),
);
});
});
}