236 lines
6.4 KiB
Dart
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>()),
|
|
);
|
|
});
|
|
});
|
|
});
|
|
}
|