import 'package:platform_mirrors/mirrors.dart'; import 'package:test/test.dart'; @reflectable class TestClass { String name; final int id; List tags; static const version = '1.0.0'; TestClass(this.name, {required this.id, List? tags}) : tags = List.from(tags ?? []); // Make sure tags is mutable TestClass.guest() : name = 'Guest', id = 0, tags = []; // Initialize with empty mutable list void addTag(String tag) { tags.add(tag); } String greet([String greeting = 'Hello']) { return '$greeting $name!'; } static TestClass create(String name, {required int id}) { return TestClass(name, id: id); } } @reflectable class GenericTestClass { T value; List items; GenericTestClass(this.value, {List? items}) : items = List.from(items ?? []); // Make sure items is mutable void addItem(T item) { items.add(item); } T getValue() => value; } @reflectable class ParentTestClass { String name; ParentTestClass(this.name); String getName() => name; } @reflectable class ChildTestClass extends ParentTestClass { int age; ChildTestClass(String name, this.age) : super(name); @override String getName() => '$name ($age)'; } void main() { group('Scanner', () { setUp(() { ReflectionRegistry.reset(); }); test('scans properties correctly', () { // Register base metadata ReflectionRegistry.register(TestClass); ReflectionRegistry.registerProperty(TestClass, 'name', String); ReflectionRegistry.registerProperty(TestClass, 'id', int, isWritable: false); ReflectionRegistry.registerProperty(TestClass, 'tags', List); ReflectionRegistry.registerProperty(TestClass, 'version', String, isWritable: false); // Scan type RuntimeTypeDiscoverer.scanType(TestClass); final metadata = ReflectionRegistry.getPropertyMetadata(TestClass); expect(metadata, isNotNull); expect(metadata!['name'], isNotNull); expect(metadata['name']!.type, equals(String)); expect(metadata['name']!.isWritable, isTrue); expect(metadata['id'], isNotNull); expect(metadata['id']!.type, equals(int)); expect(metadata['id']!.isWritable, isFalse); expect(metadata['tags'], isNotNull); expect(metadata['tags']!.type, equals(List)); expect(metadata['tags']!.isWritable, isTrue); expect(metadata['version'], isNotNull); expect(metadata['version']!.type, equals(String)); expect(metadata['version']!.isWritable, isFalse); }); test('scans methods correctly', () { // Register base metadata ReflectionRegistry.register(TestClass); ReflectionRegistry.registerMethod( TestClass, 'addTag', [String], true, parameterNames: ['tag'], isRequired: [true], ); ReflectionRegistry.registerMethod( TestClass, 'greet', [String], false, parameterNames: ['greeting'], isRequired: [false], ); ReflectionRegistry.registerMethod( TestClass, 'create', [String, int], false, parameterNames: ['name', 'id'], isRequired: [true, true], isNamed: [false, true], isStatic: true, ); // Scan type RuntimeTypeDiscoverer.scanType(TestClass); final metadata = ReflectionRegistry.getMethodMetadata(TestClass); expect(metadata, isNotNull); // addTag method expect(metadata!['addTag'], isNotNull); expect(metadata['addTag']!.parameterTypes, equals([String])); expect(metadata['addTag']!.parameters.length, equals(1)); expect(metadata['addTag']!.parameters[0].name, equals('tag')); expect(metadata['addTag']!.parameters[0].type, equals(String)); expect(metadata['addTag']!.parameters[0].isRequired, isTrue); expect(metadata['addTag']!.returnsVoid, isTrue); expect(metadata['addTag']!.isStatic, isFalse); // greet method expect(metadata['greet'], isNotNull); expect(metadata['greet']!.parameterTypes, equals([String])); expect(metadata['greet']!.parameters.length, equals(1)); expect(metadata['greet']!.parameters[0].name, equals('greeting')); expect(metadata['greet']!.parameters[0].type, equals(String)); expect(metadata['greet']!.parameters[0].isRequired, isFalse); expect(metadata['greet']!.returnsVoid, isFalse); expect(metadata['greet']!.isStatic, isFalse); // create method expect(metadata['create'], isNotNull); expect(metadata['create']!.parameterTypes, equals([String, int])); expect(metadata['create']!.parameters.length, equals(2)); expect(metadata['create']!.parameters[0].name, equals('name')); expect(metadata['create']!.parameters[0].type, equals(String)); expect(metadata['create']!.parameters[0].isRequired, isTrue); expect(metadata['create']!.parameters[1].name, equals('id')); expect(metadata['create']!.parameters[1].type, equals(int)); expect(metadata['create']!.parameters[1].isRequired, isTrue); expect(metadata['create']!.parameters[1].isNamed, isTrue); expect(metadata['create']!.returnsVoid, isFalse); expect(metadata['create']!.isStatic, isTrue); }); test('scans constructors correctly', () { // Register base metadata ReflectionRegistry.register(TestClass); ReflectionRegistry.registerConstructor( TestClass, '', parameterTypes: [String, int, List], parameterNames: ['name', 'id', 'tags'], isRequired: [true, true, false], isNamed: [false, true, true], ); ReflectionRegistry.registerConstructor( TestClass, 'guest', ); // Scan type RuntimeTypeDiscoverer.scanType(TestClass); final metadata = ReflectionRegistry.getConstructorMetadata(TestClass); expect(metadata, isNotNull); expect(metadata!.length, equals(2)); // Default constructor final defaultCtor = metadata.firstWhere((m) => m.name.isEmpty); expect(defaultCtor.parameterTypes, equals([String, int, List])); expect(defaultCtor.parameters.length, equals(3)); expect(defaultCtor.parameters[0].name, equals('name')); expect(defaultCtor.parameters[0].type, equals(String)); expect(defaultCtor.parameters[0].isRequired, isTrue); expect(defaultCtor.parameters[1].name, equals('id')); expect(defaultCtor.parameters[1].type, equals(int)); expect(defaultCtor.parameters[1].isRequired, isTrue); expect(defaultCtor.parameters[1].isNamed, isTrue); expect(defaultCtor.parameters[2].name, equals('tags')); expect(defaultCtor.parameters[2].type, equals(List)); expect(defaultCtor.parameters[2].isRequired, isFalse); expect(defaultCtor.parameters[2].isNamed, isTrue); // Guest constructor final guestCtor = metadata.firstWhere((m) => m.name == 'guest'); expect(guestCtor.parameterTypes, isEmpty); expect(guestCtor.parameters, isEmpty); }); test('scanned type works with reflection', () { // Register base metadata ReflectionRegistry.register(TestClass); ReflectionRegistry.registerProperty(TestClass, 'name', String); ReflectionRegistry.registerProperty(TestClass, 'id', int, isWritable: false); ReflectionRegistry.registerProperty(TestClass, 'tags', List); ReflectionRegistry.registerMethod( TestClass, 'addTag', [String], true, parameterNames: ['tag'], isRequired: [true], ); ReflectionRegistry.registerMethod( TestClass, 'greet', [String], false, parameterNames: ['greeting'], isRequired: [false], ); ReflectionRegistry.registerConstructor( TestClass, '', parameterTypes: [String, int, List], parameterNames: ['name', 'id', 'tags'], isRequired: [true, true, false], isNamed: [false, true, true], creator: (String name, {required int id, List? tags}) => TestClass(name, id: id, tags: tags), ); ReflectionRegistry.registerConstructor( TestClass, 'guest', creator: () => TestClass.guest(), ); // Scan type RuntimeTypeDiscoverer.scanType(TestClass); final reflector = RuntimeReflector.instance; // Create instance final instance = reflector.createInstance( TestClass, positionalArgs: ['John'], namedArgs: {'id': 123}, ) as TestClass; expect(instance.name, equals('John')); expect(instance.id, equals(123)); expect(instance.tags, isEmpty); // Create guest instance final guest = reflector.createInstance( TestClass, constructorName: 'guest', ) as TestClass; expect(guest.name, equals('Guest')); expect(guest.id, equals(0)); expect(guest.tags, isEmpty); // Reflect on instance final mirror = reflector.reflect(instance); // Access properties expect(mirror.getField(const Symbol('name')).reflectee, equals('John')); expect(mirror.getField(const Symbol('id')).reflectee, equals(123)); // Modify properties mirror.setField(const Symbol('name'), 'Jane'); expect(instance.name, equals('Jane')); // Invoke methods mirror.invoke(const Symbol('addTag'), ['test']); expect(instance.tags, equals(['test'])); final greeting = mirror.invoke(const Symbol('greet'), ['Hi']).reflectee; expect(greeting, equals('Hi Jane!')); }); test('handles generic types correctly', () { // Register base metadata ReflectionRegistry.register(GenericTestClass); ReflectionRegistry.registerProperty(GenericTestClass, 'value', dynamic); ReflectionRegistry.registerProperty(GenericTestClass, 'items', List); ReflectionRegistry.registerMethod( GenericTestClass, 'addItem', [dynamic], true, parameterNames: ['item'], isRequired: [true], ); ReflectionRegistry.registerMethod( GenericTestClass, 'getValue', [], false, ); // Scan type RuntimeTypeDiscoverer.scanType(GenericTestClass); final metadata = ReflectionRegistry.getPropertyMetadata(GenericTestClass); expect(metadata, isNotNull); expect(metadata!['value'], isNotNull); expect(metadata['items'], isNotNull); expect(metadata['items']!.type, equals(List)); final methodMeta = ReflectionRegistry.getMethodMetadata(GenericTestClass); expect(methodMeta, isNotNull); expect(methodMeta!['addItem'], isNotNull); expect(methodMeta['getValue'], isNotNull); }); test('handles inheritance correctly', () { // Register base metadata ReflectionRegistry.register(ParentTestClass); ReflectionRegistry.register(ChildTestClass); ReflectionRegistry.registerProperty(ParentTestClass, 'name', String); ReflectionRegistry.registerProperty(ChildTestClass, 'name', String); ReflectionRegistry.registerProperty(ChildTestClass, 'age', int); ReflectionRegistry.registerMethod( ParentTestClass, 'getName', [], false, ); ReflectionRegistry.registerMethod( ChildTestClass, 'getName', [], false, ); ReflectionRegistry.registerConstructor( ChildTestClass, '', parameterTypes: [String, int], parameterNames: ['name', 'age'], isRequired: [true, true], isNamed: [false, false], creator: (String name, int age) => ChildTestClass(name, age), ); // Scan types RuntimeTypeDiscoverer.scanType(ParentTestClass); RuntimeTypeDiscoverer.scanType(ChildTestClass); final parentMeta = ReflectionRegistry.getPropertyMetadata(ParentTestClass); final childMeta = ReflectionRegistry.getPropertyMetadata(ChildTestClass); expect(parentMeta, isNotNull); expect(parentMeta!['name'], isNotNull); expect(childMeta, isNotNull); expect(childMeta!['name'], isNotNull); expect(childMeta['age'], isNotNull); final reflector = RuntimeReflector.instance; final child = reflector.createInstance( ChildTestClass, positionalArgs: ['John', 30], ) as ChildTestClass; final mirror = reflector.reflect(child); final result = mirror.invoke(const Symbol('getName'), []).reflectee; expect(result, equals('John (30)')); }); }); }