Use Opaque to represent empty Structs (#142, #143) (#166)

diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml
index a5cd11a..7de66eb 100644
--- a/.github/workflows/test-package.yml
+++ b/.github/workflows/test-package.yml
@@ -20,7 +20,7 @@
     strategy:
       fail-fast: false
       matrix:
-        sdk: [2.12.0-198.0.dev] # TODO(127): Revert to stable.
+        sdk: [2.12.0-237.0.dev] # TODO(127): Revert to stable.
     steps:
       - uses: actions/checkout@v2
       - uses: dart-lang/setup-dart@v1.0
@@ -44,7 +44,7 @@
       - uses: actions/checkout@v2
       - uses: dart-lang/setup-dart@v1.0
         with:
-          sdk: 2.12.0-198.0.dev # TODO(127): Revert to stable.
+          sdk: 2.12.0-237.0.dev # TODO(127): Revert to stable.
       - name: Install dependencies
         run: dart pub get
       - name: Install libclang-10-dev
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1e49bdc..e877eb7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 2.0.0-dev.5
+- Use `Opaque` for representing empty `Struct`s.
+
 # 2.0.0-dev.4
 - Add support for parsing and generating globals.
 
diff --git a/example/c_json/pubspec.yaml b/example/c_json/pubspec.yaml
index 931aaea..76e4198 100644
--- a/example/c_json/pubspec.yaml
+++ b/example/c_json/pubspec.yaml
@@ -5,7 +5,7 @@
 name: c_json_example
 
 environment:
-  sdk: '>=2.12.0-198.0.dev <3.0.0'
+  sdk: '>=2.12.0-237.0.dev <3.0.0'
 
 dependencies:
   ffi: ^0.2.0-nullsafety.1
diff --git a/example/libclang-example/generated_bindings.dart b/example/libclang-example/generated_bindings.dart
index 0edbcb9..330c63f 100644
--- a/example/libclang-example/generated_bindings.dart
+++ b/example/libclang-example/generated_bindings.dart
@@ -6250,9 +6250,9 @@
   external int Count;
 }
 
-class CXTargetInfoImpl extends ffi.Struct {}
+class CXTargetInfoImpl extends ffi.Opaque {}
 
-class CXTranslationUnitImpl extends ffi.Struct {}
+class CXTranslationUnitImpl extends ffi.Opaque {}
 
 /// Provides the contents of a file that has not yet been saved to disk.
 ///
@@ -6636,7 +6636,7 @@
   external CXString Message;
 }
 
-class CXCursorSetImpl extends ffi.Struct {}
+class CXCursorSetImpl extends ffi.Opaque {}
 
 /// Describes the kind of type
 abstract class CXTypeKind {
diff --git a/example/libclang-example/pubspec.yaml b/example/libclang-example/pubspec.yaml
index df66b0b..efce084 100644
--- a/example/libclang-example/pubspec.yaml
+++ b/example/libclang-example/pubspec.yaml
@@ -5,7 +5,7 @@
 name: libclang_example
 
 environment:
-  sdk: '>=2.12.0-198.0.dev <3.0.0'
+  sdk: '>=2.12.0-237.0.dev <3.0.0'
 
 dev_dependencies:
   ffigen:
diff --git a/example/simple/pubspec.yaml b/example/simple/pubspec.yaml
index ce3aa5e..d3562ef 100644
--- a/example/simple/pubspec.yaml
+++ b/example/simple/pubspec.yaml
@@ -5,7 +5,7 @@
 name: simple_example
 
 environment:
-  sdk: '>=2.12.0-198.0.dev <3.0.0'
+  sdk: '>=2.12.0-237.0.dev <3.0.0'
 
 dev_dependencies:
   ffigen:
diff --git a/lib/src/code_generator/global.dart b/lib/src/code_generator/global.dart
index d419e92..42db339 100644
--- a/lib/src/code_generator/global.dart
+++ b/lib/src/code_generator/global.dart
@@ -61,12 +61,18 @@
     final pointerName = w.wrapperLevelUniqueNamer.makeUnique('_$globalVarName');
     final dartType = type.getDartType(w);
     final cType = type.getCType(w);
-    final refOrValue = type.broadType == BroadType.Struct ? 'ref' : 'value';
 
     s.write(
-        "late final ${w.ffiLibraryPrefix}.Pointer<$dartType> $pointerName = ${w.dylibIdentifier}.lookup<$cType>('$originalName');\n\n");
-    s.write('$dartType get $globalVarName => $pointerName.$refOrValue;\n\n');
-    if (type.broadType != BroadType.Struct) {
+        "late final ${w.ffiLibraryPrefix}.Pointer<$cType> $pointerName = ${w.dylibIdentifier}.lookup<$cType>('$originalName');\n\n");
+    if (type.broadType == BroadType.Struct) {
+      if (type.struc!.isOpaque) {
+        s.write(
+            '${w.ffiLibraryPrefix}.Pointer<$cType> get $globalVarName => $pointerName;\n\n');
+      } else {
+        s.write('$dartType get $globalVarName => $pointerName.ref;\n\n');
+      }
+    } else {
+      s.write('$dartType get $globalVarName => $pointerName.value;\n\n');
       s.write(
           'set $globalVarName($dartType value) => $pointerName.value = value;\n\n');
     }
diff --git a/lib/src/code_generator/library.dart b/lib/src/code_generator/library.dart
index 4ea8e65..139ef4f 100644
--- a/lib/src/code_generator/library.dart
+++ b/lib/src/code_generator/library.dart
@@ -110,6 +110,7 @@
             runInShell: Platform.isWindows);
     if (result.stderr.toString().isNotEmpty) {
       _logger.severe(result.stderr);
+      throw FormatException('Unable to format generated file: $path.');
     }
   }
 
diff --git a/lib/src/code_generator/struc.dart b/lib/src/code_generator/struc.dart
index 8958139..2a0aca5 100644
--- a/lib/src/code_generator/struc.dart
+++ b/lib/src/code_generator/struc.dart
@@ -42,6 +42,8 @@
 
   List<Member> members;
 
+  bool get isOpaque => members.isEmpty;
+
   Struc({
     String? usr,
     String? originalName,
@@ -102,7 +104,7 @@
 
     // Write class declaration.
     s.write(
-        'class $enclosingClassName extends ${w.ffiLibraryPrefix}.Struct{\n');
+        'class $enclosingClassName extends ${w.ffiLibraryPrefix}.${isOpaque ? 'Opaque' : 'Struct'}{\n');
     for (final m in members) {
       final memberName = localUniqueNamer.makeUnique(m.name);
       if (m.type.broadType == BroadType.ConstantArray) {
diff --git a/lib/src/header_parser/clang_bindings/clang_bindings.dart b/lib/src/header_parser/clang_bindings/clang_bindings.dart
index 7e7ab7c..a68621e 100644
--- a/lib/src/header_parser/clang_bindings/clang_bindings.dart
+++ b/lib/src/header_parser/clang_bindings/clang_bindings.dart
@@ -920,7 +920,7 @@
   external int private_flags;
 }
 
-class CXTranslationUnitImpl extends ffi.Struct {}
+class CXTranslationUnitImpl extends ffi.Opaque {}
 
 /// Provides the contents of a file that has not yet been saved to disk.
 ///
diff --git a/lib/src/header_parser/data.dart b/lib/src/header_parser/data.dart
index c2b371e..ac16572 100644
--- a/lib/src/header_parser/data.dart
+++ b/lib/src/header_parser/data.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:ffi';
-import 'dart:isolate';
 
 import 'package:ffigen/src/code_generator.dart' show Constant;
 import 'package:ffigen/src/config_provider.dart' show Config;
diff --git a/pubspec.yaml b/pubspec.yaml
index 87663a2..cb13584 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,12 +3,12 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: ffigen
-version: 2.0.0-dev.4
+version: 2.0.0-dev.5
 homepage: https://github.com/dart-lang/ffigen
 description: Experimental generator for FFI bindings, using LibClang to parse C header files.
 
 environment:
-  sdk: '>=2.12.0-198.0.dev <3.0.0'
+  sdk: '>=2.12.0-237.0.dev <3.0.0'
 
 dependencies:
   ffi: ^0.2.0-nullsafety.1
diff --git a/test/code_generator_test.dart b/test/code_generator_test.dart
deleted file mode 100644
index aae8b0f..0000000
--- a/test/code_generator_test.dart
+++ /dev/null
@@ -1,921 +0,0 @@
-// Copyright (c) 2020, 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 'dart:io';
-
-import 'package:ffigen/src/code_generator.dart';
-import 'package:test/test.dart';
-
-void main() {
-  group('code_generator: ', () {
-    test('Function Binding (primitives, pointers)', () {
-      final library = Library(
-        name: 'Bindings',
-        bindings: [
-          Func(
-            name: 'noParam',
-            dartDoc: 'Just a test function\nheres another line',
-            returnType: Type.nativeType(
-              SupportedNativeType.Int32,
-            ),
-          ),
-          Func(
-            name: 'withPrimitiveParam',
-            parameters: [
-              Parameter(
-                name: 'a',
-                type: Type.nativeType(
-                  SupportedNativeType.Int32,
-                ),
-              ),
-              Parameter(
-                name: 'b',
-                type: Type.nativeType(
-                  SupportedNativeType.Uint8,
-                ),
-              ),
-            ],
-            returnType: Type.nativeType(
-              SupportedNativeType.Char,
-            ),
-          ),
-          Func(
-            name: 'withPointerParam',
-            parameters: [
-              Parameter(
-                name: 'a',
-                type: Type.pointer(
-                  Type.nativeType(
-                    SupportedNativeType.Int32,
-                  ),
-                ),
-              ),
-              Parameter(
-                name: 'b',
-                type: Type.pointer(
-                  Type.pointer(
-                    Type.nativeType(
-                      SupportedNativeType.Uint8,
-                    ),
-                  ),
-                ),
-              ),
-            ],
-            returnType: Type.pointer(
-              Type.nativeType(
-                SupportedNativeType.Double,
-              ),
-            ),
-          ),
-        ],
-      );
-
-      final gen = library.generate();
-
-      // Writing to file for debug purpose.
-      final file = File(
-        'test/debug_generated/Function-Binding-test-output.dart',
-      );
-      try {
-        expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
-// 
-// Generated by `package:ffigen`.
-import 'dart:ffi' as ffi;
-
-class Bindings{
-/// Holds the Dynamic library.
-final ffi.DynamicLibrary _dylib;
-
-/// The symbols are looked up in [dynamicLibrary].
-Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
-
-/// Just a test function
-/// heres another line
-int noParam(
-) {
-return (_noParam ??= _dylib.lookupFunction<_c_noParam,_dart_noParam>('noParam'))(
-  );
-}
-_dart_noParam? _noParam;
-
-int withPrimitiveParam(
-  int a,
-  int b,
-) {
-return (_withPrimitiveParam ??= _dylib.lookupFunction<_c_withPrimitiveParam,_dart_withPrimitiveParam>('withPrimitiveParam'))(
-    a,
-    b,
-  );
-}
-_dart_withPrimitiveParam? _withPrimitiveParam;
-
-ffi.Pointer<ffi.Double> withPointerParam(
-  ffi.Pointer<ffi.Int32> a,
-  ffi.Pointer<ffi.Pointer<ffi.Uint8>> b,
-) {
-return (_withPointerParam ??= _dylib.lookupFunction<_c_withPointerParam,_dart_withPointerParam>('withPointerParam'))(
-    a,
-    b,
-  );
-}
-_dart_withPointerParam? _withPointerParam;
-
-}
-
-typedef _c_noParam = ffi.Int32 Function(
-);
-
-typedef _dart_noParam = int Function(
-);
-
-typedef _c_withPrimitiveParam = ffi.Uint8 Function(
-  ffi.Int32 a,
-  ffi.Uint8 b,
-);
-
-typedef _dart_withPrimitiveParam = int Function(
-  int a,
-  int b,
-);
-
-typedef _c_withPointerParam = ffi.Pointer<ffi.Double> Function(
-  ffi.Pointer<ffi.Int32> a,
-  ffi.Pointer<ffi.Pointer<ffi.Uint8>> b,
-);
-
-typedef _dart_withPointerParam = ffi.Pointer<ffi.Double> Function(
-  ffi.Pointer<ffi.Int32> a,
-  ffi.Pointer<ffi.Pointer<ffi.Uint8>> b,
-);
-
-''');
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        file.writeAsStringSync(gen);
-        print('Failed test, Debug output: ${file.absolute.path}');
-        rethrow;
-      }
-    });
-
-    test('Struct Binding (primitives, pointers)', () {
-      final library = Library(
-        name: 'Bindings',
-        bindings: [
-          Struc(
-            name: 'NoMember',
-            dartDoc: 'Just a test struct\nheres another line',
-          ),
-          Struc(
-            name: 'WithPrimitiveMember',
-            members: [
-              Member(
-                name: 'a',
-                type: Type.nativeType(
-                  SupportedNativeType.Int32,
-                ),
-              ),
-              Member(
-                name: 'b',
-                type: Type.nativeType(
-                  SupportedNativeType.Double,
-                ),
-              ),
-              Member(
-                name: 'c',
-                type: Type.nativeType(
-                  SupportedNativeType.Char,
-                ),
-              ),
-            ],
-          ),
-          Struc(
-            name: 'WithPointerMember',
-            members: [
-              Member(
-                name: 'a',
-                type: Type.pointer(
-                  Type.nativeType(
-                    SupportedNativeType.Int32,
-                  ),
-                ),
-              ),
-              Member(
-                name: 'b',
-                type: Type.pointer(
-                  Type.pointer(
-                    Type.nativeType(
-                      SupportedNativeType.Double,
-                    ),
-                  ),
-                ),
-              ),
-              Member(
-                name: 'c',
-                type: Type.nativeType(
-                  SupportedNativeType.Char,
-                ),
-              ),
-            ],
-          ),
-        ],
-      );
-
-      final gen = library.generate();
-
-      // Writing to file for debug purpose.
-      final file = File('test/debug_generated/Struct-Binding-test-output.dart');
-
-      try {
-        expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
-// 
-// Generated by `package:ffigen`.
-import 'dart:ffi' as ffi;
-
-/// Just a test struct
-/// heres another line
-class NoMember extends ffi.Struct{
-}
-
-class WithPrimitiveMember extends ffi.Struct{
-  @ffi.Int32()
-  external int a;
-
-  @ffi.Double()
-  external double b;
-
-  @ffi.Uint8()
-  external int c;
-
-}
-
-class WithPointerMember extends ffi.Struct{
-  external ffi.Pointer<ffi.Int32> a;
-
-  external ffi.Pointer<ffi.Pointer<ffi.Double>> b;
-
-  @ffi.Uint8()
-  external int c;
-
-}
-
-''');
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        file.writeAsStringSync(gen);
-        print('Failed test, Debug output: ${file.absolute.path}');
-        rethrow;
-      }
-    });
-
-    test('Function and Struct Binding (pointer to Struct)', () {
-      final struct_some = Struc(
-        name: 'SomeStruc',
-        members: [
-          Member(
-            name: 'a',
-            type: Type.nativeType(
-              SupportedNativeType.Int32,
-            ),
-          ),
-          Member(
-            name: 'b',
-            type: Type.nativeType(
-              SupportedNativeType.Double,
-            ),
-          ),
-          Member(
-            name: 'c',
-            type: Type.nativeType(
-              SupportedNativeType.Char,
-            ),
-          ),
-        ],
-      );
-      final library = Library(
-        name: 'Bindings',
-        bindings: [
-          struct_some,
-          Func(
-            name: 'someFunc',
-            parameters: [
-              Parameter(
-                name: 'some',
-                type: Type.pointer(
-                  Type.pointer(
-                    Type.struct(
-                      struct_some,
-                    ),
-                  ),
-                ),
-              ),
-            ],
-            returnType: Type.pointer(
-              Type.struct(
-                struct_some,
-              ),
-            ),
-          ),
-        ],
-      );
-
-      final gen = library.generate();
-
-      // Writing to file for debug purpose.
-      final file =
-          File('test/debug_generated/Func-n-Struct-Binding-test-output.dart');
-      try {
-        //expect
-        expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
-// 
-// Generated by `package:ffigen`.
-import 'dart:ffi' as ffi;
-
-class Bindings{
-/// Holds the Dynamic library.
-final ffi.DynamicLibrary _dylib;
-
-/// The symbols are looked up in [dynamicLibrary].
-Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
-
-ffi.Pointer<SomeStruc> someFunc(
-  ffi.Pointer<ffi.Pointer<SomeStruc>> some,
-) {
-return (_someFunc ??= _dylib.lookupFunction<_c_someFunc,_dart_someFunc>('someFunc'))(
-    some,
-  );
-}
-_dart_someFunc? _someFunc;
-
-}
-
-class SomeStruc extends ffi.Struct{
-  @ffi.Int32()
-  external int a;
-
-  @ffi.Double()
-  external double b;
-
-  @ffi.Uint8()
-  external int c;
-
-}
-
-typedef _c_someFunc = ffi.Pointer<SomeStruc> Function(
-  ffi.Pointer<ffi.Pointer<SomeStruc>> some,
-);
-
-typedef _dart_someFunc = ffi.Pointer<SomeStruc> Function(
-  ffi.Pointer<ffi.Pointer<SomeStruc>> some,
-);
-
-''');
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        file.writeAsStringSync(gen);
-        print('Failed test, Debug output: ${file.absolute.path}');
-        rethrow;
-      }
-    });
-
-    test('global (primitives, pointers, pointer to struct)', () {
-      final struc_some = Struc(
-        name: 'Some',
-      );
-      final library = Library(
-        name: 'Bindings',
-        bindings: [
-          Global(
-            name: 'test1',
-            type: Type.nativeType(
-              SupportedNativeType.Int32,
-            ),
-          ),
-          Global(
-            name: 'test2',
-            type: Type.pointer(
-              Type.nativeType(
-                SupportedNativeType.Float,
-              ),
-            ),
-          ),
-          struc_some,
-          Global(
-            name: 'test5',
-            type: Type.pointer(
-              Type.struct(
-                struc_some,
-              ),
-            ),
-          ),
-        ],
-      );
-
-      final gen = library.generate();
-
-      // Writing to file for debug purpose.
-      final file = File(
-        'test/debug_generated/Global-Binding-test-output.dart',
-      );
-      try {
-        expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
-// 
-// Generated by `package:ffigen`.
-import 'dart:ffi' as ffi;
-
-class Bindings{
-/// Holds the Dynamic library.
-final ffi.DynamicLibrary _dylib;
-
-/// The symbols are looked up in [dynamicLibrary].
-Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
-
-late final ffi.Pointer<int> _test1 = _dylib.lookup<ffi.Int32>('test1');
-
-int get test1 => _test1.value;
-
-set test1(int value) => _test1.value = value;
-
-late final ffi.Pointer<ffi.Pointer<ffi.Float>> _test2 = _dylib.lookup<ffi.Pointer<ffi.Float>>('test2');
-
-ffi.Pointer<ffi.Float> get test2 => _test2.value;
-
-set test2(ffi.Pointer<ffi.Float> value) => _test2.value = value;
-
-late final ffi.Pointer<ffi.Pointer<Some>> _test5 = _dylib.lookup<ffi.Pointer<Some>>('test5');
-
-ffi.Pointer<Some> get test5 => _test5.value;
-
-set test5(ffi.Pointer<Some> value) => _test5.value = value;
-
-}
-
-class Some extends ffi.Struct{
-}
-
-''');
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        file.writeAsStringSync(gen);
-        print('Failed test, Debug output: ${file.absolute.path}');
-        rethrow;
-      }
-    });
-
-    test('constant', () {
-      final library = Library(
-        name: 'Bindings',
-        bindings: [
-          Constant(
-            name: 'test1',
-            rawType: 'int',
-            rawValue: '20',
-          ),
-          Constant(
-            name: 'test2',
-            rawType: 'double',
-            rawValue: '20.0',
-          ),
-        ],
-      );
-
-      final gen = library.generate();
-
-      // Writing to file for debug purpose.
-      final file = File(
-        'test/debug_generated/Constant-test-output.dart',
-      );
-      try {
-        expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
-// 
-// Generated by `package:ffigen`.
-import 'dart:ffi' as ffi;
-
-const int test1 = 20;
-
-const double test2 = 20.0;
-
-''');
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        file.writeAsStringSync(gen);
-        print('Failed test, Debug output: ${file.absolute.path}');
-        rethrow;
-      }
-    });
-
-    test('enum_class', () {
-      final library = Library(
-        name: 'Bindings',
-        bindings: [
-          EnumClass(
-            name: 'Constants',
-            dartDoc: 'test line 1\ntest line 2',
-            enumConstants: [
-              EnumConstant(
-                name: 'a',
-                value: 10,
-              ),
-              EnumConstant(name: 'b', value: -1, dartDoc: 'negative'),
-            ],
-          ),
-        ],
-      );
-
-      final gen = library.generate();
-
-      // Writing to file for debug purpose.
-      final file = File(
-        'test/debug_generated/enum-class-test-output.dart',
-      );
-      try {
-        expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
-// 
-// Generated by `package:ffigen`.
-import 'dart:ffi' as ffi;
-
-/// test line 1
-/// test line 2
-abstract class Constants {
-  static const int a = 10;
-  /// negative
-  static const int b = -1;
-}
-
-''');
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        file.writeAsStringSync(gen);
-        print('Failed test, Debug output: ${file.absolute.path}');
-        rethrow;
-      }
-    });
-    test('Internal conflict resolution', () {
-      final library = Library(
-        name: 'init_dylib',
-        bindings: [
-          Func(
-            name: 'test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
-          ),
-          Func(
-            name: '_test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
-          ),
-          Func(
-            name: '_c_test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
-          ),
-          Func(
-            name: '_dart_test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
-          ),
-          Struc(
-            name: '_Test',
-            members: [
-              Member(
-                name: 'array',
-                type: Type.constantArray(
-                  2,
-                  Type.nativeType(
-                    SupportedNativeType.Int8,
-                  ),
-                ),
-              ),
-            ],
-          ),
-          Struc(name: 'ArrayHelperPrefixCollisionTest'),
-          Func(
-            name: 'Test',
-            returnType: Type.nativeType(SupportedNativeType.Void),
-          ),
-          EnumClass(name: '_c_Test'),
-          EnumClass(name: 'init_dylib'),
-        ],
-      );
-
-      final gen = library.generate();
-
-      // Writing to file for debug purpose.
-      final file = File(
-        'test/debug_generated/internal-conflict-resolution.dart',
-      );
-      try {
-        expect(gen, r'''// AUTO GENERATED FILE, DO NOT EDIT.
-// 
-// Generated by `package:ffigen`.
-import 'dart:ffi' as ffi;
-
-class init_dylib_1{
-/// Holds the Dynamic library.
-final ffi.DynamicLibrary _dylib;
-
-/// The symbols are looked up in [dynamicLibrary].
-init_dylib_1(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
-
-void test(
-) {
-return (_test_1 ??= _dylib.lookupFunction<_c_test1,_dart_test1>('test'))(
-  );
-}
-_dart_test1? _test_1;
-
-void _test(
-) {
-return (__test ??= _dylib.lookupFunction<_c__test,_dart__test>('_test'))(
-  );
-}
-_dart__test? __test;
-
-void _c_test(
-) {
-return (__c_test ??= _dylib.lookupFunction<_c__c_test,_dart__c_test>('_c_test'))(
-  );
-}
-_dart__c_test? __c_test;
-
-void _dart_test(
-) {
-return (__dart_test ??= _dylib.lookupFunction<_c__dart_test,_dart__dart_test>('_dart_test'))(
-  );
-}
-_dart__dart_test? __dart_test;
-
-void Test(
-) {
-return (_Test ??= _dylib.lookupFunction<_c_Test1,_dart_Test>('Test'))(
-  );
-}
-_dart_Test? _Test;
-
-}
-
-class _Test extends ffi.Struct{
-  @ffi.Int8()
-  external int _unique_array_item_0;
-  @ffi.Int8()
-  external int _unique_array_item_1;
-/// Helper for array `array`.
-ArrayHelper1__Test_array_level0 get array => ArrayHelper1__Test_array_level0(this, [2], 0, 0);
-}
-
-/// Helper for array `array` in struct `_Test`.
-class ArrayHelper1__Test_array_level0{
-final _Test _struct;
-final List<int> dimensions;
-final int level;
-final int _absoluteIndex;
-int get length => dimensions[level];
-ArrayHelper1__Test_array_level0(this._struct, this.dimensions, this.level, this._absoluteIndex);
-  void _checkBounds(int index) {
-    if (index >= length || index < 0) {
-      throw RangeError('Dimension $level: index not in range 0..${length} exclusive.');
-    }
-  }
-  int operator[](int index){
-_checkBounds(index);
-switch(_absoluteIndex+index){
-case 0:
-  return _struct._unique_array_item_0;
-case 1:
-  return _struct._unique_array_item_1;
-default:
-  throw Exception('Invalid Array Helper generated.');}
-}
-void operator[]=(int index, int value){
-_checkBounds(index);
-switch(_absoluteIndex+index){
-case 0:
-  _struct._unique_array_item_0 = value;
-  break;
-case 1:
-  _struct._unique_array_item_1 = value;
-  break;
-default:
-  throw Exception('Invalid Array Helper generated.');
-}
-}
-}
-class ArrayHelperPrefixCollisionTest extends ffi.Struct{
-}
-
-abstract class _c_Test {
-}
-
-abstract class init_dylib {
-}
-
-typedef _c_test1 = ffi.Void Function(
-);
-
-typedef _dart_test1 = void Function(
-);
-
-typedef _c__test = ffi.Void Function(
-);
-
-typedef _dart__test = void Function(
-);
-
-typedef _c__c_test = ffi.Void Function(
-);
-
-typedef _dart__c_test = void Function(
-);
-
-typedef _c__dart_test = ffi.Void Function(
-);
-
-typedef _dart__dart_test = void Function(
-);
-
-typedef _c_Test1 = ffi.Void Function(
-);
-
-typedef _dart_Test = void Function(
-);
-
-''');
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        file.writeAsStringSync(gen);
-        print('Failed test, Debug output: ${file.absolute.path}');
-        rethrow;
-      }
-    });
-  });
-  test('boolean_dartBool', () {
-    final library = Library(
-      name: 'Bindings',
-      dartBool: true,
-      bindings: [
-        Func(
-          name: 'test1',
-          returnType: Type.boolean(),
-          parameters: [
-            Parameter(name: 'a', type: Type.boolean()),
-            Parameter(name: 'b', type: Type.pointer(Type.boolean())),
-          ],
-        ),
-        Struc(
-          name: 'test2',
-          members: [
-            Member(name: 'a', type: Type.boolean()),
-          ],
-        ),
-      ],
-    );
-
-    final gen = library.generate();
-
-    // Writing to file for debug purpose.
-    final file = File(
-      'test/debug_generated/boolean-dartbool-output.dart',
-    );
-    try {
-      expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
-// 
-// Generated by `package:ffigen`.
-import 'dart:ffi' as ffi;
-
-class Bindings{
-/// Holds the Dynamic library.
-final ffi.DynamicLibrary _dylib;
-
-/// The symbols are looked up in [dynamicLibrary].
-Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
-
-bool test1(
-  bool a,
-  ffi.Pointer<ffi.Uint8> b,
-) {
-return (_test1 ??= _dylib.lookupFunction<_c_test1,_dart_test1>('test1'))(
-    a?1:0,
-    b,
-  )!=0;
-}
-_dart_test1? _test1;
-
-}
-
-class test2 extends ffi.Struct{
-  @ffi.Uint8()
-  external int a;
-
-}
-
-typedef _c_test1 = ffi.Uint8 Function(
-  ffi.Uint8 a,
-  ffi.Pointer<ffi.Uint8> b,
-);
-
-typedef _dart_test1 = int Function(
-  int a,
-  ffi.Pointer<ffi.Uint8> b,
-);
-
-''');
-      if (file.existsSync()) {
-        file.delete();
-      }
-    } catch (e) {
-      file.writeAsStringSync(gen);
-      print('Failed test, Debug output: ${file.absolute.path}');
-      rethrow;
-    }
-  });
-  test('boolean_no_dartBool', () {
-    final library = Library(
-      name: 'Bindings',
-      dartBool: false,
-      bindings: [
-        Func(
-          name: 'test1',
-          returnType: Type.boolean(),
-          parameters: [
-            Parameter(name: 'a', type: Type.boolean()),
-            Parameter(name: 'b', type: Type.pointer(Type.boolean())),
-          ],
-        ),
-        Struc(
-          name: 'test2',
-          members: [
-            Member(name: 'a', type: Type.boolean()),
-          ],
-        ),
-      ],
-    );
-
-    final gen = library.generate();
-
-    // Writing to file for debug purpose.
-    final file = File(
-      'test/debug_generated/boolean-no-dartBool-output.dart',
-    );
-    try {
-      expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
-// 
-// Generated by `package:ffigen`.
-import 'dart:ffi' as ffi;
-
-class Bindings{
-/// Holds the Dynamic library.
-final ffi.DynamicLibrary _dylib;
-
-/// The symbols are looked up in [dynamicLibrary].
-Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
-
-int test1(
-  int a,
-  ffi.Pointer<ffi.Uint8> b,
-) {
-return (_test1 ??= _dylib.lookupFunction<_c_test1,_dart_test1>('test1'))(
-    a,
-    b,
-  );
-}
-_dart_test1? _test1;
-
-}
-
-class test2 extends ffi.Struct{
-  @ffi.Uint8()
-  external int a;
-
-}
-
-typedef _c_test1 = ffi.Uint8 Function(
-  ffi.Uint8 a,
-  ffi.Pointer<ffi.Uint8> b,
-);
-
-typedef _dart_test1 = int Function(
-  int a,
-  ffi.Pointer<ffi.Uint8> b,
-);
-
-''');
-      if (file.existsSync()) {
-        file.delete();
-      }
-    } catch (e) {
-      file.writeAsStringSync(gen);
-      print('Failed test, Debug output: ${file.absolute.path}');
-      rethrow;
-    }
-  });
-}
diff --git a/test/code_generator_tests/code_generator_test.dart b/test/code_generator_tests/code_generator_test.dart
new file mode 100644
index 0000000..350b799
--- /dev/null
+++ b/test/code_generator_tests/code_generator_test.dart
@@ -0,0 +1,382 @@
+// Copyright (c) 2021, 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:ffigen/src/code_generator.dart';
+import 'package:test/test.dart';
+import '../test_utils.dart';
+
+void main() {
+  group('code_generator: ', () {
+    test('Function Binding (primitives, pointers)', () {
+      final library = Library(
+        name: 'Bindings',
+        bindings: [
+          Func(
+            name: 'noParam',
+            dartDoc: 'Just a test function\nheres another line',
+            returnType: Type.nativeType(
+              SupportedNativeType.Int32,
+            ),
+          ),
+          Func(
+            name: 'withPrimitiveParam',
+            parameters: [
+              Parameter(
+                name: 'a',
+                type: Type.nativeType(
+                  SupportedNativeType.Int32,
+                ),
+              ),
+              Parameter(
+                name: 'b',
+                type: Type.nativeType(
+                  SupportedNativeType.Uint8,
+                ),
+              ),
+            ],
+            returnType: Type.nativeType(
+              SupportedNativeType.Char,
+            ),
+          ),
+          Func(
+            name: 'withPointerParam',
+            parameters: [
+              Parameter(
+                name: 'a',
+                type: Type.pointer(
+                  Type.nativeType(
+                    SupportedNativeType.Int32,
+                  ),
+                ),
+              ),
+              Parameter(
+                name: 'b',
+                type: Type.pointer(
+                  Type.pointer(
+                    Type.nativeType(
+                      SupportedNativeType.Uint8,
+                    ),
+                  ),
+                ),
+              ),
+            ],
+            returnType: Type.pointer(
+              Type.nativeType(
+                SupportedNativeType.Double,
+              ),
+            ),
+          ),
+        ],
+      );
+
+      _matchLib(library, 'function');
+    });
+
+    test('Struct Binding (primitives, pointers)', () {
+      final library = Library(
+        name: 'Bindings',
+        bindings: [
+          Struc(
+            name: 'NoMember',
+            dartDoc: 'Just a test struct\nheres another line',
+          ),
+          Struc(
+            name: 'WithPrimitiveMember',
+            members: [
+              Member(
+                name: 'a',
+                type: Type.nativeType(
+                  SupportedNativeType.Int32,
+                ),
+              ),
+              Member(
+                name: 'b',
+                type: Type.nativeType(
+                  SupportedNativeType.Double,
+                ),
+              ),
+              Member(
+                name: 'c',
+                type: Type.nativeType(
+                  SupportedNativeType.Char,
+                ),
+              ),
+            ],
+          ),
+          Struc(
+            name: 'WithPointerMember',
+            members: [
+              Member(
+                name: 'a',
+                type: Type.pointer(
+                  Type.nativeType(
+                    SupportedNativeType.Int32,
+                  ),
+                ),
+              ),
+              Member(
+                name: 'b',
+                type: Type.pointer(
+                  Type.pointer(
+                    Type.nativeType(
+                      SupportedNativeType.Double,
+                    ),
+                  ),
+                ),
+              ),
+              Member(
+                name: 'c',
+                type: Type.nativeType(
+                  SupportedNativeType.Char,
+                ),
+              ),
+            ],
+          ),
+        ],
+      );
+
+      _matchLib(library, 'struct');
+    });
+
+    test('Function and Struct Binding (pointer to Struct)', () {
+      final struct_some = Struc(
+        name: 'SomeStruc',
+        members: [
+          Member(
+            name: 'a',
+            type: Type.nativeType(
+              SupportedNativeType.Int32,
+            ),
+          ),
+          Member(
+            name: 'b',
+            type: Type.nativeType(
+              SupportedNativeType.Double,
+            ),
+          ),
+          Member(
+            name: 'c',
+            type: Type.nativeType(
+              SupportedNativeType.Char,
+            ),
+          ),
+        ],
+      );
+      final library = Library(
+        name: 'Bindings',
+        bindings: [
+          struct_some,
+          Func(
+            name: 'someFunc',
+            parameters: [
+              Parameter(
+                name: 'some',
+                type: Type.pointer(
+                  Type.pointer(
+                    Type.struct(
+                      struct_some,
+                    ),
+                  ),
+                ),
+              ),
+            ],
+            returnType: Type.pointer(
+              Type.struct(
+                struct_some,
+              ),
+            ),
+          ),
+        ],
+      );
+
+      _matchLib(library, 'function_n_struct');
+    });
+
+    test('global (primitives, pointers, pointer to struct)', () {
+      final struc_some = Struc(
+        name: 'Some',
+      );
+      final emptyGlobalStruc = Struc(name: 'EmptyStruct');
+
+      final library = Library(
+        name: 'Bindings',
+        bindings: [
+          Global(
+            name: 'test1',
+            type: Type.nativeType(
+              SupportedNativeType.Int32,
+            ),
+          ),
+          Global(
+            name: 'test2',
+            type: Type.pointer(
+              Type.nativeType(
+                SupportedNativeType.Float,
+              ),
+            ),
+          ),
+          struc_some,
+          Global(
+            name: 'test5',
+            type: Type.pointer(
+              Type.struct(
+                struc_some,
+              ),
+            ),
+          ),
+          emptyGlobalStruc,
+          Global(name: 'globalStruct', type: Type.struct(emptyGlobalStruc)),
+        ],
+      );
+      _matchLib(library, 'global');
+    });
+
+    test('constant', () {
+      final library = Library(
+        name: 'Bindings',
+        header: '// ignore_for_file: unused_import\n',
+        bindings: [
+          Constant(
+            name: 'test1',
+            rawType: 'int',
+            rawValue: '20',
+          ),
+          Constant(
+            name: 'test2',
+            rawType: 'double',
+            rawValue: '20.0',
+          ),
+        ],
+      );
+      _matchLib(library, 'constant');
+    });
+
+    test('enum_class', () {
+      final library = Library(
+        name: 'Bindings',
+        header: '// ignore_for_file: unused_import\n',
+        bindings: [
+          EnumClass(
+            name: 'Constants',
+            dartDoc: 'test line 1\ntest line 2',
+            enumConstants: [
+              EnumConstant(
+                name: 'a',
+                value: 10,
+              ),
+              EnumConstant(name: 'b', value: -1, dartDoc: 'negative'),
+            ],
+          ),
+        ],
+      );
+      _matchLib(library, 'enumclass');
+    });
+    test('Internal conflict resolution', () {
+      final library = Library(
+        name: 'init_dylib',
+        header: '// ignore_for_file: unused_element\n',
+        bindings: [
+          Func(
+            name: 'test',
+            returnType: Type.nativeType(SupportedNativeType.Void),
+          ),
+          Func(
+            name: '_test',
+            returnType: Type.nativeType(SupportedNativeType.Void),
+          ),
+          Func(
+            name: '_c_test',
+            returnType: Type.nativeType(SupportedNativeType.Void),
+          ),
+          Func(
+            name: '_dart_test',
+            returnType: Type.nativeType(SupportedNativeType.Void),
+          ),
+          Struc(
+            name: '_Test',
+            members: [
+              Member(
+                name: 'array',
+                type: Type.constantArray(
+                  2,
+                  Type.nativeType(
+                    SupportedNativeType.Int8,
+                  ),
+                ),
+              ),
+            ],
+          ),
+          Struc(name: 'ArrayHelperPrefixCollisionTest'),
+          Func(
+            name: 'Test',
+            returnType: Type.nativeType(SupportedNativeType.Void),
+          ),
+          EnumClass(name: '_c_Test'),
+          EnumClass(name: 'init_dylib'),
+        ],
+      );
+      _matchLib(library, 'internal_conflict_resolution');
+    });
+  });
+  test('boolean_dartBool', () {
+    final library = Library(
+      name: 'Bindings',
+      dartBool: true,
+      bindings: [
+        Func(
+          name: 'test1',
+          returnType: Type.boolean(),
+          parameters: [
+            Parameter(name: 'a', type: Type.boolean()),
+            Parameter(name: 'b', type: Type.pointer(Type.boolean())),
+          ],
+        ),
+        Struc(
+          name: 'test2',
+          members: [
+            Member(name: 'a', type: Type.boolean()),
+          ],
+        ),
+      ],
+    );
+    _matchLib(library, 'boolean_dartbool');
+  });
+  test('boolean_no_dartBool', () {
+    final library = Library(
+      name: 'Bindings',
+      dartBool: false,
+      bindings: [
+        Func(
+          name: 'test1',
+          returnType: Type.boolean(),
+          parameters: [
+            Parameter(name: 'a', type: Type.boolean()),
+            Parameter(name: 'b', type: Type.pointer(Type.boolean())),
+          ],
+        ),
+        Struc(
+          name: 'test2',
+          members: [
+            Member(name: 'a', type: Type.boolean()),
+          ],
+        ),
+      ],
+    );
+    _matchLib(library, 'boolean_no_dartbool');
+  });
+}
+
+/// Utility to match expected bindings to the generated bindings.
+void _matchLib(Library lib, String testName) {
+  matchLibraryWithExpected(lib, [
+    'test',
+    'debug_generated',
+    'code_generator_test_${testName}_output.dart'
+  ], [
+    'test',
+    'code_generator_tests',
+    'expected_bindings',
+    '_expected_${testName}_bindings.dart'
+  ]);
+}
diff --git a/test/code_generator_tests/expected_bindings/_expected_boolean_dartbool_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_boolean_dartbool_bindings.dart
new file mode 100644
index 0000000..9e9d6ac
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_boolean_dartbool_bindings.dart
@@ -0,0 +1,40 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class Bindings {
+  /// Holds the Dynamic library.
+  final ffi.DynamicLibrary _dylib;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  Bindings(ffi.DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary;
+
+  bool test1(
+    bool a,
+    ffi.Pointer<ffi.Uint8> b,
+  ) {
+    return (_test1 ??= _dylib.lookupFunction<_c_test1, _dart_test1>('test1'))(
+          a ? 1 : 0,
+          b,
+        ) !=
+        0;
+  }
+
+  _dart_test1? _test1;
+}
+
+class test2 extends ffi.Struct {
+  @ffi.Uint8()
+  external int a;
+}
+
+typedef _c_test1 = ffi.Uint8 Function(
+  ffi.Uint8 a,
+  ffi.Pointer<ffi.Uint8> b,
+);
+
+typedef _dart_test1 = int Function(
+  int a,
+  ffi.Pointer<ffi.Uint8> b,
+);
diff --git a/test/code_generator_tests/expected_bindings/_expected_boolean_no_dartbool_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_boolean_no_dartbool_bindings.dart
new file mode 100644
index 0000000..99ed563
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_boolean_no_dartbool_bindings.dart
@@ -0,0 +1,39 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class Bindings {
+  /// Holds the Dynamic library.
+  final ffi.DynamicLibrary _dylib;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  Bindings(ffi.DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary;
+
+  int test1(
+    int a,
+    ffi.Pointer<ffi.Uint8> b,
+  ) {
+    return (_test1 ??= _dylib.lookupFunction<_c_test1, _dart_test1>('test1'))(
+      a,
+      b,
+    );
+  }
+
+  _dart_test1? _test1;
+}
+
+class test2 extends ffi.Struct {
+  @ffi.Uint8()
+  external int a;
+}
+
+typedef _c_test1 = ffi.Uint8 Function(
+  ffi.Uint8 a,
+  ffi.Pointer<ffi.Uint8> b,
+);
+
+typedef _dart_test1 = int Function(
+  int a,
+  ffi.Pointer<ffi.Uint8> b,
+);
diff --git a/test/code_generator_tests/expected_bindings/_expected_constant_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_constant_bindings.dart
new file mode 100644
index 0000000..d01f2b1
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_constant_bindings.dart
@@ -0,0 +1,10 @@
+// ignore_for_file: unused_import
+
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+const int test1 = 20;
+
+const double test2 = 20.0;
diff --git a/test/code_generator_tests/expected_bindings/_expected_enumclass_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_enumclass_bindings.dart
new file mode 100644
index 0000000..0aa2dbb
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_enumclass_bindings.dart
@@ -0,0 +1,15 @@
+// ignore_for_file: unused_import
+
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+/// test line 1
+/// test line 2
+abstract class Constants {
+  static const int a = 10;
+
+  /// negative
+  static const int b = -1;
+}
diff --git a/test/code_generator_tests/expected_bindings/_expected_function_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_function_bindings.dart
new file mode 100644
index 0000000..82736d2
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_function_bindings.dart
@@ -0,0 +1,73 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class Bindings {
+  /// Holds the Dynamic library.
+  final ffi.DynamicLibrary _dylib;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  Bindings(ffi.DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary;
+
+  /// Just a test function
+  /// heres another line
+  int noParam() {
+    return (_noParam ??=
+        _dylib.lookupFunction<_c_noParam, _dart_noParam>('noParam'))();
+  }
+
+  _dart_noParam? _noParam;
+
+  int withPrimitiveParam(
+    int a,
+    int b,
+  ) {
+    return (_withPrimitiveParam ??=
+        _dylib.lookupFunction<_c_withPrimitiveParam, _dart_withPrimitiveParam>(
+            'withPrimitiveParam'))(
+      a,
+      b,
+    );
+  }
+
+  _dart_withPrimitiveParam? _withPrimitiveParam;
+
+  ffi.Pointer<ffi.Double> withPointerParam(
+    ffi.Pointer<ffi.Int32> a,
+    ffi.Pointer<ffi.Pointer<ffi.Uint8>> b,
+  ) {
+    return (_withPointerParam ??=
+        _dylib.lookupFunction<_c_withPointerParam, _dart_withPointerParam>(
+            'withPointerParam'))(
+      a,
+      b,
+    );
+  }
+
+  _dart_withPointerParam? _withPointerParam;
+}
+
+typedef _c_noParam = ffi.Int32 Function();
+
+typedef _dart_noParam = int Function();
+
+typedef _c_withPrimitiveParam = ffi.Uint8 Function(
+  ffi.Int32 a,
+  ffi.Uint8 b,
+);
+
+typedef _dart_withPrimitiveParam = int Function(
+  int a,
+  int b,
+);
+
+typedef _c_withPointerParam = ffi.Pointer<ffi.Double> Function(
+  ffi.Pointer<ffi.Int32> a,
+  ffi.Pointer<ffi.Pointer<ffi.Uint8>> b,
+);
+
+typedef _dart_withPointerParam = ffi.Pointer<ffi.Double> Function(
+  ffi.Pointer<ffi.Int32> a,
+  ffi.Pointer<ffi.Pointer<ffi.Uint8>> b,
+);
diff --git a/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart
new file mode 100644
index 0000000..4f0ea46
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_function_n_struct_bindings.dart
@@ -0,0 +1,42 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class Bindings {
+  /// Holds the Dynamic library.
+  final ffi.DynamicLibrary _dylib;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  Bindings(ffi.DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary;
+
+  ffi.Pointer<SomeStruc> someFunc(
+    ffi.Pointer<ffi.Pointer<SomeStruc>> some,
+  ) {
+    return (_someFunc ??=
+        _dylib.lookupFunction<_c_someFunc, _dart_someFunc>('someFunc'))(
+      some,
+    );
+  }
+
+  _dart_someFunc? _someFunc;
+}
+
+class SomeStruc extends ffi.Struct {
+  @ffi.Int32()
+  external int a;
+
+  @ffi.Double()
+  external double b;
+
+  @ffi.Uint8()
+  external int c;
+}
+
+typedef _c_someFunc = ffi.Pointer<SomeStruc> Function(
+  ffi.Pointer<ffi.Pointer<SomeStruc>> some,
+);
+
+typedef _dart_someFunc = ffi.Pointer<SomeStruc> Function(
+  ffi.Pointer<ffi.Pointer<SomeStruc>> some,
+);
diff --git a/test/code_generator_tests/expected_bindings/_expected_global_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_global_bindings.dart
new file mode 100644
index 0000000..215858b
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_global_bindings.dart
@@ -0,0 +1,41 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class Bindings {
+  /// Holds the Dynamic library.
+  final ffi.DynamicLibrary _dylib;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  Bindings(ffi.DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary;
+
+  late final ffi.Pointer<ffi.Int32> _test1 = _dylib.lookup<ffi.Int32>('test1');
+
+  int get test1 => _test1.value;
+
+  set test1(int value) => _test1.value = value;
+
+  late final ffi.Pointer<ffi.Pointer<ffi.Float>> _test2 =
+      _dylib.lookup<ffi.Pointer<ffi.Float>>('test2');
+
+  ffi.Pointer<ffi.Float> get test2 => _test2.value;
+
+  set test2(ffi.Pointer<ffi.Float> value) => _test2.value = value;
+
+  late final ffi.Pointer<ffi.Pointer<Some>> _test5 =
+      _dylib.lookup<ffi.Pointer<Some>>('test5');
+
+  ffi.Pointer<Some> get test5 => _test5.value;
+
+  set test5(ffi.Pointer<Some> value) => _test5.value = value;
+
+  late final ffi.Pointer<EmptyStruct> _globalStruct =
+      _dylib.lookup<EmptyStruct>('globalStruct');
+
+  ffi.Pointer<EmptyStruct> get globalStruct => _globalStruct;
+}
+
+class Some extends ffi.Opaque {}
+
+class EmptyStruct extends ffi.Opaque {}
diff --git a/test/code_generator_tests/expected_bindings/_expected_internal_conflict_resolution_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_internal_conflict_resolution_bindings.dart
new file mode 100644
index 0000000..a6fbe60
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_internal_conflict_resolution_bindings.dart
@@ -0,0 +1,126 @@
+// ignore_for_file: unused_element
+
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class init_dylib_1 {
+  /// Holds the Dynamic library.
+  final ffi.DynamicLibrary _dylib;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  init_dylib_1(ffi.DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary;
+
+  void test() {
+    return (_test_1 ??= _dylib.lookupFunction<_c_test1, _dart_test1>('test'))();
+  }
+
+  _dart_test1? _test_1;
+
+  void _test() {
+    return (__test ??= _dylib.lookupFunction<_c__test, _dart__test>('_test'))();
+  }
+
+  _dart__test? __test;
+
+  void _c_test() {
+    return (__c_test ??=
+        _dylib.lookupFunction<_c__c_test, _dart__c_test>('_c_test'))();
+  }
+
+  _dart__c_test? __c_test;
+
+  void _dart_test() {
+    return (__dart_test ??=
+        _dylib.lookupFunction<_c__dart_test, _dart__dart_test>('_dart_test'))();
+  }
+
+  _dart__dart_test? __dart_test;
+
+  void Test() {
+    return (_Test ??= _dylib.lookupFunction<_c_Test1, _dart_Test>('Test'))();
+  }
+
+  _dart_Test? _Test;
+}
+
+class _Test extends ffi.Struct {
+  @ffi.Int8()
+  external int _unique_array_item_0;
+  @ffi.Int8()
+  external int _unique_array_item_1;
+
+  /// Helper for array `array`.
+  ArrayHelper1__Test_array_level0 get array =>
+      ArrayHelper1__Test_array_level0(this, [2], 0, 0);
+}
+
+/// Helper for array `array` in struct `_Test`.
+class ArrayHelper1__Test_array_level0 {
+  final _Test _struct;
+  final List<int> dimensions;
+  final int level;
+  final int _absoluteIndex;
+  int get length => dimensions[level];
+  ArrayHelper1__Test_array_level0(
+      this._struct, this.dimensions, this.level, this._absoluteIndex);
+  void _checkBounds(int index) {
+    if (index >= length || index < 0) {
+      throw RangeError(
+          'Dimension $level: index not in range 0..${length} exclusive.');
+    }
+  }
+
+  int operator [](int index) {
+    _checkBounds(index);
+    switch (_absoluteIndex + index) {
+      case 0:
+        return _struct._unique_array_item_0;
+      case 1:
+        return _struct._unique_array_item_1;
+      default:
+        throw Exception('Invalid Array Helper generated.');
+    }
+  }
+
+  void operator []=(int index, int value) {
+    _checkBounds(index);
+    switch (_absoluteIndex + index) {
+      case 0:
+        _struct._unique_array_item_0 = value;
+        break;
+      case 1:
+        _struct._unique_array_item_1 = value;
+        break;
+      default:
+        throw Exception('Invalid Array Helper generated.');
+    }
+  }
+}
+
+class ArrayHelperPrefixCollisionTest extends ffi.Opaque {}
+
+abstract class _c_Test {}
+
+abstract class init_dylib {}
+
+typedef _c_test1 = ffi.Void Function();
+
+typedef _dart_test1 = void Function();
+
+typedef _c__test = ffi.Void Function();
+
+typedef _dart__test = void Function();
+
+typedef _c__c_test = ffi.Void Function();
+
+typedef _dart__c_test = void Function();
+
+typedef _c__dart_test = ffi.Void Function();
+
+typedef _dart__dart_test = void Function();
+
+typedef _c_Test1 = ffi.Void Function();
+
+typedef _dart_Test = void Function();
diff --git a/test/code_generator_tests/expected_bindings/_expected_struct_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_struct_bindings.dart
new file mode 100644
index 0000000..f84642b
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_struct_bindings.dart
@@ -0,0 +1,28 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+/// Just a test struct
+/// heres another line
+class NoMember extends ffi.Opaque {}
+
+class WithPrimitiveMember extends ffi.Struct {
+  @ffi.Int32()
+  external int a;
+
+  @ffi.Double()
+  external double b;
+
+  @ffi.Uint8()
+  external int c;
+}
+
+class WithPointerMember extends ffi.Struct {
+  external ffi.Pointer<ffi.Int32> a;
+
+  external ffi.Pointer<ffi.Pointer<ffi.Double>> b;
+
+  @ffi.Uint8()
+  external int c;
+}
diff --git a/test/header_parser_tests/globals.h b/test/header_parser_tests/globals.h
index f0f464f..80da2b0 100644
--- a/test/header_parser_tests/globals.h
+++ b/test/header_parser_tests/globals.h
@@ -13,3 +13,9 @@
 
 // This should be ignored
 int GlobalIgnore;
+
+struct EmptyStruct
+{
+};
+
+struct EmptyStruct globalStruct;
diff --git a/test/header_parser_tests/globals_test.dart b/test/header_parser_tests/globals_test.dart
index 7184792..832b89d 100644
--- a/test/header_parser_tests/globals_test.dart
+++ b/test/header_parser_tests/globals_test.dart
@@ -62,6 +62,7 @@
 }
 
 Library expectedLibrary() {
+  final globalStruc = Struc(name: 'EmptyStruct');
   return Library(
     name: 'Bindings',
     bindings: [
@@ -70,6 +71,8 @@
       Global(
           type: Type.pointer(Type.nativeType(SupportedNativeType.Int32)),
           name: 'aGlobalPointer'),
+      globalStruc,
+      Global(name: 'globalStruct', type: Type.struct(globalStruc)),
     ],
   );
 }
diff --git a/test/large_integration_tests/_expected_libclang_bindings.dart b/test/large_integration_tests/_expected_libclang_bindings.dart
index e0abce7..9d4c52d 100644
--- a/test/large_integration_tests/_expected_libclang_bindings.dart
+++ b/test/large_integration_tests/_expected_libclang_bindings.dart
@@ -5045,13 +5045,13 @@
   external int Count;
 }
 
-class CXVirtualFileOverlayImpl extends ffi.Struct {}
+class CXVirtualFileOverlayImpl extends ffi.Opaque {}
 
-class CXModuleMapDescriptorImpl extends ffi.Struct {}
+class CXModuleMapDescriptorImpl extends ffi.Opaque {}
 
-class CXTargetInfoImpl extends ffi.Struct {}
+class CXTargetInfoImpl extends ffi.Opaque {}
 
-class CXTranslationUnitImpl extends ffi.Struct {}
+class CXTranslationUnitImpl extends ffi.Opaque {}
 
 /// Provides the contents of a file that has not yet been saved to disk.
 class CXUnsavedFile extends ffi.Struct {
@@ -6333,7 +6333,7 @@
   static const int CXTLS_Static = 2;
 }
 
-class CXCursorSetImpl extends ffi.Struct {}
+class CXCursorSetImpl extends ffi.Opaque {}
 
 /// Describes the kind of type
 abstract class CXTypeKind {
diff --git a/test/large_integration_tests/_expected_sqlite_bindings.dart b/test/large_integration_tests/_expected_sqlite_bindings.dart
index 6abdd48..fa67d48 100644
--- a/test/large_integration_tests/_expected_sqlite_bindings.dart
+++ b/test/large_integration_tests/_expected_sqlite_bindings.dart
@@ -8984,9 +8984,9 @@
   _dart_sqlite3_rtree_query_callback? _sqlite3_rtree_query_callback;
 }
 
-class sqlite3 extends ffi.Struct {}
+class sqlite3 extends ffi.Opaque {}
 
-class sqlite3_file extends ffi.Struct {}
+class sqlite3_file extends ffi.Opaque {}
 
 class sqlite3_io_methods extends ffi.Struct {
   @ffi.Int32()
@@ -9031,19 +9031,19 @@
   external ffi.Pointer<ffi.NativeFunction<_typedefC_19>> xUnfetch;
 }
 
-class sqlite3_mutex extends ffi.Struct {}
+class sqlite3_mutex extends ffi.Opaque {}
 
-class sqlite3_api_routines extends ffi.Struct {}
+class sqlite3_api_routines extends ffi.Opaque {}
 
-class sqlite3_vfs extends ffi.Struct {}
+class sqlite3_vfs extends ffi.Opaque {}
 
-class sqlite3_mem_methods extends ffi.Struct {}
+class sqlite3_mem_methods extends ffi.Opaque {}
 
-class sqlite3_stmt extends ffi.Struct {}
+class sqlite3_stmt extends ffi.Opaque {}
 
-class sqlite3_value extends ffi.Struct {}
+class sqlite3_value extends ffi.Opaque {}
 
-class sqlite3_context extends ffi.Struct {}
+class sqlite3_context extends ffi.Opaque {}
 
 /// CAPI3REF: Virtual Table Instance Object
 /// KEYWORDS: sqlite3_vtab
@@ -9061,7 +9061,7 @@
 /// prior to assigning a new string to zErrMsg.  ^After the error message
 /// is delivered up to the client application, the string will be automatically
 /// freed by sqlite3_free() and the zErrMsg field will be zeroed.
-class sqlite3_vtab extends ffi.Struct {}
+class sqlite3_vtab extends ffi.Opaque {}
 
 /// CAPI3REF: Virtual Table Indexing Information
 /// KEYWORDS: sqlite3_index_info
@@ -9163,7 +9163,7 @@
 /// It may therefore only be used if
 /// sqlite3_libversion_number() returns a value greater than or equal to
 /// 3009000.
-class sqlite3_index_info extends ffi.Struct {}
+class sqlite3_index_info extends ffi.Opaque {}
 
 /// CAPI3REF: Virtual Table Cursor Object
 /// KEYWORDS: sqlite3_vtab_cursor {virtual table cursor}
@@ -9180,7 +9180,7 @@
 ///
 /// This superclass exists in order to define fields of the cursor that
 /// are common to all implementations.
-class sqlite3_vtab_cursor extends ffi.Struct {}
+class sqlite3_vtab_cursor extends ffi.Opaque {}
 
 /// CAPI3REF: Virtual Table Object
 /// KEYWORDS: sqlite3_module {virtual table module}
@@ -9196,23 +9196,23 @@
 /// module or until the [database connection] closes.  The content
 /// of this structure must not change while it is registered with
 /// any database connection.
-class sqlite3_module extends ffi.Struct {}
+class sqlite3_module extends ffi.Opaque {}
 
-class sqlite3_blob extends ffi.Struct {}
+class sqlite3_blob extends ffi.Opaque {}
 
-class sqlite3_mutex_methods extends ffi.Struct {}
+class sqlite3_mutex_methods extends ffi.Opaque {}
 
-class sqlite3_str extends ffi.Struct {}
+class sqlite3_str extends ffi.Opaque {}
 
-class sqlite3_pcache extends ffi.Struct {}
+class sqlite3_pcache extends ffi.Opaque {}
 
-class sqlite3_pcache_page extends ffi.Struct {}
+class sqlite3_pcache_page extends ffi.Opaque {}
 
-class sqlite3_pcache_methods2 extends ffi.Struct {}
+class sqlite3_pcache_methods2 extends ffi.Opaque {}
 
-class sqlite3_pcache_methods extends ffi.Struct {}
+class sqlite3_pcache_methods extends ffi.Opaque {}
 
-class sqlite3_backup extends ffi.Struct {}
+class sqlite3_backup extends ffi.Opaque {}
 
 /// CAPI3REF: Database Snapshot
 /// KEYWORDS: {snapshot} {sqlite3_snapshot}
@@ -9610,7 +9610,7 @@
 
 /// A pointer to a structure of the following type is passed as the first
 /// argument to callbacks registered using rtree_geometry_callback().
-class sqlite3_rtree_geometry extends ffi.Struct {}
+class sqlite3_rtree_geometry extends ffi.Opaque {}
 
 /// A pointer to a structure of the following type is passed as the
 /// argument to scored geometry callback registered using
@@ -9619,7 +9619,7 @@
 /// Note that the first 5 fields of this structure are identical to
 /// sqlite3_rtree_geometry.  This structure is a subclass of
 /// sqlite3_rtree_geometry.
-class sqlite3_rtree_query_info extends ffi.Struct {}
+class sqlite3_rtree_query_info extends ffi.Opaque {}
 
 /// EXTENSION API FUNCTIONS
 ///
@@ -9830,17 +9830,17 @@
 ///
 /// xPhraseNextColumn()
 /// See xPhraseFirstColumn above.
-class Fts5ExtensionApi extends ffi.Struct {}
+class Fts5ExtensionApi extends ffi.Opaque {}
 
-class Fts5Context extends ffi.Struct {}
+class Fts5Context extends ffi.Opaque {}
 
-class Fts5PhraseIter extends ffi.Struct {}
+class Fts5PhraseIter extends ffi.Opaque {}
 
-class Fts5Tokenizer extends ffi.Struct {}
+class Fts5Tokenizer extends ffi.Opaque {}
 
-class fts5_tokenizer extends ffi.Struct {}
+class fts5_tokenizer extends ffi.Opaque {}
 
-class fts5_api extends ffi.Struct {}
+class fts5_api extends ffi.Opaque {}
 
 const String SQLITE_VERSION = '3.32.3';
 
diff --git a/test/test_coverage.dart b/test/test_coverage.dart
index ede39f3..9f61cde 100644
--- a/test/test_coverage.dart
+++ b/test/test_coverage.dart
@@ -2,7 +2,7 @@
 // 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 'code_generator_test.dart' as code_generator_test;
+import 'code_generator_tests/code_generator_test.dart' as code_generator_test;
 import 'collision_tests/decl_decl_collision_test.dart'
     as collision_tests_decl_decl_collision_test;
 import 'collision_tests/reserved_keyword_collision_test.dart'
@@ -19,6 +19,8 @@
     as header_parser_tests_function_n_struct_test;
 import 'header_parser_tests/functions_test.dart'
     as header_parser_tests_functions_test;
+import 'header_parser_tests/globals_test.dart'
+    as header_parser_tests_globals_test;
 import 'header_parser_tests/macros_test.dart'
     as header_parser_tests_macros_test;
 import 'header_parser_tests/native_func_typedef_test.dart'
@@ -43,6 +45,7 @@
   collision_tests_reserved_keyword_collision_test.main();
   header_parser_tests_dart_handle_test.main();
   header_parser_tests_functions_test.main();
+  header_parser_tests_globals_test.main();
   header_parser_tests_macros_test.main();
   header_parser_tests_function_n_struct_test.main();
   header_parser_tests_native_func_typedef_test.main();
diff --git a/test/test_utils.dart b/test/test_utils.dart
index c2d95f0..a967f66 100644
--- a/test/test_utils.dart
+++ b/test/test_utils.dart
@@ -9,8 +9,6 @@
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
 
-/// Extracts a binding's string from a library.
-
 extension LibraryTestExt on Library {
   /// Get a [Binding]'s generated string with a given name.
   String getBindingAsString(String name) {