blob: a148e270e31acc8528b93ecc9f66998e8b230dd6 [file]
// Copyright (c) 2025, 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:test/test.dart';
void main() {
const definition1 = Method(
'definition1',
Library('package:a/a.dart'),
);
const definition2 = Method(
'definition2',
Library('package:a/a.dart'),
);
const definition1differentLibrary = Method(
'definition1',
Library('package:a/b.dart'),
);
const definition3 = Method(
'definition1',
Class('SomeClass', Library('package:a/a.dart')),
isInstanceMember: true,
);
const classDefinition1 = Class(
'Class1',
Library('package:a/a.dart'),
);
const callDefintion1Static = CallWithArguments(
positionalArguments: [],
namedArguments: {},
loadingUnit: LoadingUnit(''),
);
const callDefintion1Static2 = CallWithArguments(
positionalArguments: [],
namedArguments: {},
loadingUnit: LoadingUnit(''),
);
const callDefinition2Static = CallWithArguments(
positionalArguments: [],
namedArguments: {},
loadingUnit: LoadingUnit(''),
);
const callDefinition1Tearoff = CallTearoff(
loadingUnit: LoadingUnit(''),
);
const definition1differentLibrary2 = Method(
'definition1',
Library('memory:a/a.dart'),
);
const callDefintion1StaticDifferentUri = CallWithArguments(
positionalArguments: [],
namedArguments: {},
loadingUnit: LoadingUnit(''),
);
test('Definition semantic equality', () {
expect(definition1.semanticEquals(definition1), isTrue);
expect(definition1.semanticEquals(definition2), isFalse);
expect(definition1.semanticEquals(definition1differentLibrary), isFalse);
expect(
definition1.semanticEquals(
definition1differentLibrary,
uriMapping: (uri) => uri.replaceFirst('a.dart', 'b.dart'),
),
isTrue,
);
expect(definition1.semanticEquals(definition3), isFalse);
});
test('Strict equality', () {
final recordings1 = Recordings(
calls: {
definition1: [callDefintion1Static, callDefintion1Static2],
definition2: [callDefinition2Static],
},
instances: const {},
);
final recordings2 = Recordings(
calls: {
definition2: [callDefinition2Static],
definition1: [callDefintion1Static2, callDefintion1Static],
},
instances: const {},
);
final recordings3 = Recordings(
calls: {
definition1: [callDefintion1Static],
},
instances: const {},
);
// Identical.
expect(recordings1.semanticEquals(recordings1), isTrue);
// Equal, but reorderings.
expect(recordings1.semanticEquals(recordings2), isTrue);
// Not equal.
expect(recordings1.semanticEquals(recordings3), isFalse);
});
test('otherIsSubset', () {
final recordings1 = Recordings(
calls: {
definition1: [callDefintion1Static],
definition2: [callDefinition2Static],
},
instances: const {},
);
final recordings2 = Recordings(
calls: {
definition1: [callDefintion1Static],
},
instances: const {},
);
expect(
recordings1.semanticEquals(recordings2, expectedIsSubset: true),
isTrue,
);
expect(
recordings1.semanticEquals(recordings2, expectedIsSubset: false),
isFalse,
);
// The arguments the wrong way around means something is missing.
expect(
recordings2.semanticEquals(recordings1, expectedIsSubset: true),
isFalse,
);
});
test('allowDeadCodeElimination', () {
final recordings1 = Recordings(
calls: {
definition1: [callDefintion1Static],
},
instances: const {},
);
final recordings2 = Recordings(
calls: {
definition1: [callDefintion1Static],
definition2: [callDefinition2Static],
},
instances: const {},
);
expect(
recordings1.semanticEquals(
recordings2,
allowDeadCodeElimination: true,
),
isTrue,
);
expect(
recordings1.semanticEquals(
recordings2,
allowDeadCodeElimination: false,
),
isFalse,
);
// Swapped arguments, that's not dead code elimination its an entry extra.
expect(
recordings2.semanticEquals(
recordings1,
allowDeadCodeElimination: true,
),
isFalse,
);
});
test('allowTearoffToStaticPromotion', () {
final recordings1 = Recordings(
calls: {
definition1: [callDefintion1Static],
},
instances: const {},
);
final recordings2 = Recordings(
calls: {
definition1: [callDefinition1Tearoff],
},
instances: const {},
);
expect(
recordings1.semanticEquals(
recordings2,
allowTearoffToStaticPromotion: true,
),
isTrue,
);
expect(
recordings1.semanticEquals(
recordings2,
allowTearoffToStaticPromotion: false,
),
isFalse,
);
// Don't allow downgrading a static call to a tear off.
expect(
recordings2.semanticEquals(
recordings1,
allowTearoffToStaticPromotion: true,
),
isFalse,
);
});
test('allowUriMismatch', () {
final recordings1 = Recordings(
calls: {
definition1: [
callDefintion1Static,
],
},
instances: const {},
);
final recordings2 = Recordings(
calls: {
definition1differentLibrary2: [
callDefintion1StaticDifferentUri,
],
},
instances: const {},
);
expect(
recordings1.semanticEquals(
recordings2,
uriMapping: (uri) => uri.replaceFirst('package:', 'memory:'),
),
isTrue,
);
expect(
recordings1.semanticEquals(recordings2),
isFalse,
);
});
test('CallWithArguments positional arguments different length', () {
final recordings1 = Recordings(
calls: {
definition1: [
const CallWithArguments(
positionalArguments: [IntConstant(1)],
namedArguments: {},
loadingUnit: LoadingUnit(''),
),
],
},
instances: const {},
);
final recordings2 = Recordings(
calls: {
definition1: [
const CallWithArguments(
positionalArguments: [IntConstant(1), IntConstant(2)],
namedArguments: {},
loadingUnit: LoadingUnit(''),
),
],
},
instances: const {},
);
expect(
recordings1.semanticEquals(recordings2),
isFalse,
);
});
test('InstanceConstantReference semantic equality with EnumConstant', () {
final recordings1 = Recordings(
calls: const {},
instances: {
classDefinition1: [
const InstanceConstantReference(
instanceConstant: EnumConstant(
definition: classDefinition1,
index: 0,
name: 'a',
fields: {'f': IntConstant(1)},
),
loadingUnit: LoadingUnit(''),
),
],
},
);
final recordings2 = Recordings(
calls: const {},
instances: {
classDefinition1: [
const InstanceConstantReference(
instanceConstant: EnumConstant(
definition: classDefinition1,
index: 0,
name: 'a',
fields: {'f': IntConstant(1)},
),
loadingUnit: LoadingUnit(''),
),
],
},
);
final recordings3 = Recordings(
calls: const {},
instances: {
classDefinition1: [
const InstanceConstantReference(
instanceConstant: EnumConstant(
definition: classDefinition1,
index: 1,
name: 'b',
fields: {'f': IntConstant(1)},
),
loadingUnit: LoadingUnit(''),
),
],
},
);
expect(recordings1.semanticEquals(recordings2), isTrue);
expect(recordings1.semanticEquals(recordings3), isFalse);
});
test('SetConstant semantic equality', () {
const set1 = SetConstant([IntConstant(1), IntConstant(2)]);
const set2 = SetConstant([IntConstant(2), IntConstant(1)]);
const set3 = SetConstant([IntConstant(1), IntConstant(3)]);
const set4 = SetConstant([IntConstant(1)]);
expect(set1.semanticEquals(set1), isTrue);
expect(set1.semanticEquals(set2), isTrue);
expect(set1.semanticEquals(set3), isFalse);
expect(set1.semanticEquals(set4), isFalse);
const unsupported = UnsupportedConstant('MethodTearoff');
const setWithUnsupported = SetConstant([IntConstant(1), unsupported]);
const setWithInt2 = SetConstant([
IntConstant(1),
IntConstant(2),
]);
// unsupported does not match IntConstant(2) by default
expect(setWithInt2.semanticEquals(setWithUnsupported), isFalse);
// unsupported matches any constant when promotion is allowed
expect(
setWithInt2.semanticEquals(
setWithUnsupported,
allowPromotionOfUnsupported: true,
),
isTrue,
);
});
}