platform/packages/reflection/test/reflection_test.dart
2024-11-28 16:13:19 -07:00

236 lines
6.4 KiB
Dart

import 'package:platform_reflection/reflection.dart';
import 'package:test/test.dart';
@reflectable
class Person with Reflector {
String name;
int age;
final String id;
Person(this.name, this.age, {required this.id});
// Guest constructor
Person.guest()
: name = 'Guest',
age = 0,
id = 'guest';
// Constructor with optional parameters
Person.withDefaults(this.name, [this.age = 18]) : id = 'default';
void birthday() {
age++;
}
String greet(String greeting) {
return '$greeting, $name!';
}
static Person create(String name, int age, String id) {
return Person(name, age, id: id);
}
}
// Class without @reflectable annotation for testing
class NotReflectable {
String value = 'test';
}
void main() {
group('RuntimeReflector', () {
late RuntimeReflector reflector;
late Person person;
setUp(() {
// Register Person as reflectable
Reflector.register(Person);
// Register properties
Reflector.registerProperty(Person, 'name', String);
Reflector.registerProperty(Person, 'age', int);
Reflector.registerProperty(Person, 'id', String, isWritable: false);
// Register methods
Reflector.registerMethod(
Person,
'birthday',
[],
true,
);
Reflector.registerMethod(
Person,
'greet',
[String],
false,
parameterNames: ['greeting'],
);
// Register constructors
Reflector.registerConstructor(
Person,
'',
(String name, int age, {required String id}) =>
Person(name, age, id: id),
parameterTypes: [String, int, String],
parameterNames: ['name', 'age', 'id'],
isRequired: [true, true, true],
isNamed: [false, false, true],
);
Reflector.registerConstructor(
Person,
'guest',
() => Person.guest(),
);
Reflector.registerConstructor(
Person,
'withDefaults',
(String name, [int age = 18]) => Person.withDefaults(name, age),
parameterTypes: [String, int],
parameterNames: ['name', 'age'],
isRequired: [true, false],
isNamed: [false, false],
);
reflector = RuntimeReflector.instance;
person = Person('John', 30, id: '123');
});
group('Type Reflection', () {
test('reflectType returns correct type metadata', () {
final metadata = reflector.reflectType(Person);
expect(metadata.name, equals('Person'));
expect(metadata.properties.length, equals(3));
expect(metadata.methods.length, equals(2)); // birthday and greet
expect(metadata.constructors.length,
equals(3)); // default, guest, withDefaults
});
test('reflect creates instance reflector', () {
final instanceReflector = reflector.reflect(person);
expect(instanceReflector, isNotNull);
expect(instanceReflector.type.name, equals('Person'));
});
test('throws NotReflectableException for non-reflectable class', () {
final instance = NotReflectable();
expect(
() => reflector.reflect(instance),
throwsA(isA<NotReflectableException>()),
);
});
});
group('Property Access', () {
test('getField returns property value', () {
final instanceReflector = reflector.reflect(person);
expect(instanceReflector.getField('name'), equals('John'));
expect(instanceReflector.getField('age'), equals(30));
expect(instanceReflector.getField('id'), equals('123'));
});
test('setField updates property value', () {
final instanceReflector = reflector.reflect(person);
instanceReflector.setField('name', 'Jane');
instanceReflector.setField('age', 25);
expect(person.name, equals('Jane'));
expect(person.age, equals(25));
});
test('setField throws on final field', () {
final instanceReflector = reflector.reflect(person);
expect(
() => instanceReflector.setField('id', '456'),
throwsA(isA<ReflectionException>()),
);
});
});
group('Method Invocation', () {
test('invoke calls method with arguments', () {
final instanceReflector = reflector.reflect(person);
final result = instanceReflector.invoke('greet', ['Hello']);
expect(result, equals('Hello, John!'));
instanceReflector.invoke('birthday', []);
expect(person.age, equals(31));
});
test('invoke throws on invalid arguments', () {
final instanceReflector = reflector.reflect(person);
expect(
() => instanceReflector.invoke('greet', [42]),
throwsA(isA<InvalidArgumentsException>()),
);
});
});
group('Constructor Invocation', () {
test('creates instance with default constructor', () {
final instance = reflector.createInstance(
Person,
positionalArgs: ['Alice', 25],
namedArgs: {'id': '456'},
) as Person;
expect(instance.name, equals('Alice'));
expect(instance.age, equals(25));
expect(instance.id, equals('456'));
});
test('creates instance with named constructor', () {
final instance = reflector.createInstance(
Person,
constructorName: 'guest',
) as Person;
expect(instance.name, equals('Guest'));
expect(instance.age, equals(0));
expect(instance.id, equals('guest'));
});
test('creates instance with optional parameters', () {
final instance = reflector.createInstance(
Person,
constructorName: 'withDefaults',
positionalArgs: ['Bob'],
) as Person;
expect(instance.name, equals('Bob'));
expect(instance.age, equals(18)); // Default value
expect(instance.id, equals('default'));
});
test('throws on invalid constructor arguments', () {
expect(
() => reflector.createInstance(
Person,
positionalArgs: ['Alice'], // Missing required age
namedArgs: {'id': '456'},
),
throwsA(isA<InvalidArgumentsException>()),
);
});
test('throws on non-existent constructor', () {
expect(
() => reflector.createInstance(
Person,
constructorName: 'nonexistent',
),
throwsA(isA<ReflectionException>()),
);
});
});
});
}