Update headers config. (#60)

Closes #52 
- Updated headers config to have sub-fields `entry-points` and `include-directives`, removed `header-filter` config.
- Globs are supported by both subkeys.
- Updated tests, examples, readme, changelog.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a32792..bcd11c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 0.2.0-dev
+- Updated header config. Header `entry-points` and `include-directives` are now specified under `headers` key. Glob syntax is allowed.
+
 # 0.1.5
 - Added support for parsing macros and anonymous unnamed enums. These are generated as top level constants.
 
diff --git a/README.md b/README.md
index 604a1bc..7cee823 100644
--- a/README.md
+++ b/README.md
@@ -75,20 +75,17 @@
   </tr>
   <tr>
     <td>headers<br><i>(Required)</i></td>
-    <td>List of C headers to use. Glob syntax is allowed.</td>
+    <td>The header entry-points and include-directives. Glob syntax is allowed.</td>
     <td><pre lang="yaml"><code>
 headers:
-  - 'folder/**.h'
-  - 'folder/specific_header.h'</code></pre></td>
-  </tr>
-  <tr>
-    <td>header-filter</td>
-    <td>Name of headers to include/exclude.</td>
-    <td><pre lang="yaml"><code>
-header-filter:
-  include:
-    - 'index.h'
-    - 'platform.h'</code></pre></td>
+  entry-points:
+    - 'folder/**.h'
+    - 'folder/specific_header.h'
+  include-directives:
+    - '**index.h'
+    - '**/clang-c/**'
+    - '/full/path/to/a/header.h'
+  </code></pre></td>
   </tr>
   <tr>
     <td>name<br><i>(Prefer)</i></td>
diff --git a/example/c_json/pubspec.yaml b/example/c_json/pubspec.yaml
index ef1f484..1058579 100644
--- a/example/c_json/pubspec.yaml
+++ b/example/c_json/pubspec.yaml
@@ -19,8 +19,8 @@
   name: 'CJson'
   description: 'Holds bindings to cJSON.'
   headers:
-    - '../../third_party/cjson_library/cJSON.h'
-  header-filter:
-    include:
-      - 'cJSON.h'
+    entry-points:
+      - '../../third_party/cjson_library/cJSON.h'
+    include-directives:
+      - '**cJSON.h'
   comments: false
diff --git a/example/libclang-example/pubspec.yaml b/example/libclang-example/pubspec.yaml
index 7cb89db..b05796f 100644
--- a/example/libclang-example/pubspec.yaml
+++ b/example/libclang-example/pubspec.yaml
@@ -18,13 +18,11 @@
   # Bash style Glob matching is also supported.
   # TODO(11): Globs dont work on windows if they begin with '.' or '..'.
   headers:
-    - ../../third_party/libclang/include/clang-c/Index.h
-
-  # Excludes included headers based on their names (not fullpath name).
-  header-filter:
-    include:
-      - 'CXString.h'
-      - 'Index.h'
+    entry-points:
+      - ../../third_party/libclang/include/clang-c/Index.h
+    include-directives: # use glob syntax to match with header file path.
+      - '**CXString.h'
+      - '**Index.h'
 
   compiler-opts: '-I/usr/lib/llvm-9/include/ -I/usr/lib/llvm-10/include/ -IC:\Progra~1\LLVM\include -I/usr/local/opt/llvm/include/ -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ -Wno-nullability-completeness'
   functions:
diff --git a/example/simple/pubspec.yaml b/example/simple/pubspec.yaml
index 58aaa7e..c742a26 100644
--- a/example/simple/pubspec.yaml
+++ b/example/simple/pubspec.yaml
@@ -16,4 +16,5 @@
   description: Bindings to `headers/example.h`.
   output: 'generated_bindings.dart'
   headers:
-    - 'headers/example.h'
+    entry-points:
+      - 'headers/example.h'
diff --git a/lib/src/code_generator/struc.dart b/lib/src/code_generator/struc.dart
index 9fd8d05..dec4c2c 100644
--- a/lib/src/code_generator/struc.dart
+++ b/lib/src/code_generator/struc.dart
@@ -248,8 +248,8 @@
         s.write('''
   ${helperClassGroupName}_level${dim + 1} operator [](int index) {
     $checkBoundsFunctionIdentifier(index);
-    int offset = index;
-    for (int i = level + 1; i < $dimensionsIdentifier.length; i++) {
+    var offset = index;
+    for (var i = level + 1; i < $dimensionsIdentifier.length; i++) {
       offset *= $dimensionsIdentifier[i];
     }
     return ${helperClassGroupName}_level${dim + 1}(
diff --git a/lib/src/config_provider/config.dart b/lib/src/config_provider/config.dart
index eb75b80..583287d 100644
--- a/lib/src/config_provider/config.dart
+++ b/lib/src/config_provider/config.dart
@@ -23,13 +23,8 @@
   /// output file name.
   String output;
 
-  /// Path to headers.
-  ///
-  /// This contains all the headers, after extraction from Globs.
-  List<String> headers;
-
-  /// Filter for headers.
-  HeaderFilter headerFilter;
+  // Holds headers and filters for header.
+  Headers headers;
 
   /// CommandLine Arguments to pass to clang_compiler.
   List<String> compilerOpts;
@@ -135,30 +130,18 @@
   Map<String, Specification> _getSpecs() {
     return <String, Specification>{
       strings.output: Specification<String>(
-        description: 'Output file name',
         requirement: Requirement.yes,
         validator: outputValidator,
         extractor: outputExtractor,
         extractedResult: (dynamic result) => output = result as String,
       ),
-      strings.headers: Specification<List<String>>(
-        description: 'List of C headers to generate bindings of',
+      strings.headers: Specification<Headers>(
         requirement: Requirement.yes,
         validator: headersValidator,
         extractor: headersExtractor,
-        extractedResult: (dynamic result) => headers = result as List<String>,
-      ),
-      strings.headerFilter: Specification<HeaderFilter>(
-        description: 'Include/Exclude inclusion headers',
-        validator: headerFilterValidator,
-        extractor: headerFilterExtractor,
-        defaultValue: () => HeaderFilter(),
-        extractedResult: (dynamic result) {
-          return headerFilter = result as HeaderFilter;
-        },
+        extractedResult: (dynamic result) => headers = result as Headers,
       ),
       strings.compilerOpts: Specification<List<String>>(
-        description: 'Raw compiler options to pass to clang compiler',
         requirement: Requirement.no,
         validator: compilerOptsValidator,
         extractor: compilerOptsExtractor,
@@ -166,7 +149,6 @@
             compilerOpts = result as List<String>,
       ),
       strings.functions: Specification<Declaration>(
-        description: 'Filter for functions',
         requirement: Requirement.no,
         validator: declarationConfigValidator,
         extractor: declarationConfigExtractor,
@@ -176,7 +158,6 @@
         },
       ),
       strings.structs: Specification<Declaration>(
-        description: 'Filter for Structs',
         requirement: Requirement.no,
         validator: declarationConfigValidator,
         extractor: declarationConfigExtractor,
@@ -186,7 +167,6 @@
         },
       ),
       strings.enums: Specification<Declaration>(
-        description: 'Filter for enums',
         requirement: Requirement.no,
         validator: declarationConfigValidator,
         extractor: declarationConfigExtractor,
@@ -196,7 +176,6 @@
         },
       ),
       strings.macros: Specification<Declaration>(
-        description: 'Filter for macros',
         requirement: Requirement.no,
         validator: declarationConfigValidator,
         extractor: declarationConfigExtractor,
@@ -206,7 +185,6 @@
         },
       ),
       strings.sizemap: Specification<Map<int, SupportedNativeType>>(
-        description: 'map of types: byte size in int',
         validator: sizemapValidator,
         extractor: sizemapExtractor,
         defaultValue: () => <int, SupportedNativeType>{},
@@ -220,7 +198,6 @@
         },
       ),
       strings.sort: Specification<bool>(
-        description: 'whether or not to sort the bindings alphabetically',
         requirement: Requirement.no,
         validator: booleanValidator,
         extractor: booleanExtractor,
@@ -228,7 +205,6 @@
         extractedResult: (dynamic result) => sort = result as bool,
       ),
       strings.useSupportedTypedefs: Specification<bool>(
-        description: 'whether or not to directly map supported typedef by name',
         requirement: Requirement.no,
         validator: booleanValidator,
         extractor: booleanExtractor,
@@ -237,7 +213,6 @@
             useSupportedTypedefs = result as bool,
       ),
       strings.comments: Specification<CommentType>(
-        description: 'Type of comment to extract',
         requirement: Requirement.no,
         validator: commentValidator,
         extractor: commentExtractor,
@@ -246,8 +221,6 @@
             commentType = result as CommentType,
       ),
       strings.arrayWorkaround: Specification<bool>(
-        description:
-            'whether or not to generate workarounds for inline arrays in structures',
         requirement: Requirement.no,
         validator: booleanValidator,
         extractor: booleanExtractor,
@@ -255,7 +228,6 @@
         extractedResult: (dynamic result) => arrayWorkaround = result as bool,
       ),
       strings.unnamedEnums: Specification<bool>(
-        description: 'whether or not to generate constants for unnamed enums.',
         requirement: Requirement.no,
         validator: booleanValidator,
         extractor: booleanExtractor,
@@ -263,7 +235,6 @@
         extractedResult: (dynamic result) => unnamedEnums = result as bool,
       ),
       strings.name: Specification<String>(
-        description: 'Name of the wrapper class',
         requirement: Requirement.prefer,
         validator: dartClassNameValidator,
         extractor: stringExtractor,
@@ -271,7 +242,6 @@
         extractedResult: (dynamic result) => wrapperName = result as String,
       ),
       strings.description: Specification<String>(
-        description: 'Doc comment for the wrapper class',
         requirement: Requirement.prefer,
         validator: nonEmptyStringValidator,
         extractor: stringExtractor,
@@ -280,7 +250,6 @@
             wrapperDocComment = result as String,
       ),
       strings.preamble: Specification<String>(
-        description: 'Raw header string for the generated file',
         requirement: Requirement.no,
         validator: nonEmptyStringValidator,
         extractor: stringExtractor,
diff --git a/lib/src/config_provider/config_types.dart b/lib/src/config_provider/config_types.dart
index c44a72b..a417a18 100644
--- a/lib/src/config_provider/config_types.dart
+++ b/lib/src/config_provider/config_types.dart
@@ -5,6 +5,7 @@
 /// Contains all the neccesary classes required by config.
 
 import 'package:meta/meta.dart';
+import 'package:quiver/pattern.dart' as quiver;
 
 class CommentType {
   CommentStyle style;
@@ -30,7 +31,6 @@
 ///
 /// [E] is the return type of the extractedResult.
 class Specification<E> {
-  final String description;
   final bool Function(String name, dynamic value) validator;
   final E Function(dynamic map) extractor;
   final E Function() defaultValue;
@@ -40,7 +40,6 @@
 
   Specification({
     @required this.extractedResult,
-    @required this.description,
     @required this.validator,
     @required this.extractor,
     this.defaultValue,
@@ -50,14 +49,46 @@
 
 enum Requirement { yes, prefer, no }
 
-class HeaderFilter {
-  Set<String> includedInclusionHeaders;
-  Set<String> excludedInclusionHeaders;
+// Holds headers and filters for header.
+class Headers {
+  /// Path to headers.
+  ///
+  /// This contains all the headers, after extraction from Globs.
+  List<String> entryPoints = [];
 
-  HeaderFilter({
-    this.includedInclusionHeaders = const {},
-    this.excludedInclusionHeaders = const {},
+  /// Include filter for headers.
+  HeaderIncludeFilter includeFilter = GlobHeaderFilter();
+
+  Headers({this.entryPoints, this.includeFilter});
+}
+
+abstract class HeaderIncludeFilter {
+  bool shouldInclude(String headerSourceFile);
+}
+
+class GlobHeaderFilter extends HeaderIncludeFilter {
+  List<quiver.Glob> includeGlobs = [];
+
+  GlobHeaderFilter({
+    this.includeGlobs,
   });
+
+  @override
+  bool shouldInclude(String header) {
+    // Return true if header was included.
+    for (final globPattern in includeGlobs) {
+      if (quiver.matchesFull(globPattern, header)) {
+        return true;
+      }
+    }
+
+    // If any includedInclusionHeaders is provided, return false.
+    if (includeGlobs.isNotEmpty) {
+      return false;
+    } else {
+      return true;
+    }
+  }
 }
 
 /// A generic declaration config.
diff --git a/lib/src/config_provider/spec_utils.dart b/lib/src/config_provider/spec_utils.dart
index 5170e5f..d6d08cf 100644
--- a/lib/src/config_provider/spec_utils.dart
+++ b/lib/src/config_provider/spec_utils.dart
@@ -9,6 +9,7 @@
 import 'package:logging/logging.dart';
 import 'package:path/path.dart' as p;
 import 'package:yaml/yaml.dart';
+import 'package:quiver/pattern.dart' as quiver;
 
 import '../strings.dart' as strings;
 import 'config_types.dart';
@@ -24,16 +25,19 @@
   }
 }
 
+/// Checks if type of value is [T], logs an error if it's not.
+bool checkType<T>(String key, dynamic value) {
+  if (value is! T) {
+    _logger.severe("Expected value of key '$key' to be of type '${T}'.");
+    return false;
+  }
+  return true;
+}
+
 bool booleanExtractor(dynamic value) => value as bool;
 
-bool booleanValidator(String name, dynamic value) {
-  if (value is! bool) {
-    _logger.severe("Expected value of key '$name' to be a bool.");
-    return false;
-  } else {
-    return true;
-  }
-}
+bool booleanValidator(String name, dynamic value) =>
+    checkType<bool>(name, value);
 
 Map<int, SupportedNativeType> sizemapExtractor(dynamic yamlConfig) {
   final resultMap = <int, SupportedNativeType>{};
@@ -52,8 +56,7 @@
 }
 
 bool sizemapValidator(String name, dynamic yamlConfig) {
-  if (yamlConfig is! YamlMap) {
-    _logger.severe("Expected value of key '$name' to be a Map.");
+  if (!checkType<YamlMap>(name, yamlConfig)) {
     return false;
   }
   for (final key in (yamlConfig as YamlMap).keys) {
@@ -68,71 +71,65 @@
 List<String> compilerOptsExtractor(dynamic value) =>
     (value as String)?.split(' ');
 
-bool compilerOptsValidator(String name, dynamic value) {
-  if (value is! String) {
-    _logger.severe("Expected value of key '$name' to be a string.");
-    return false;
-  } else {
-    return true;
-  }
-}
+bool compilerOptsValidator(String name, dynamic value) =>
+    checkType<String>(name, value);
 
-HeaderFilter headerFilterExtractor(dynamic yamlConfig) {
-  final includedInclusionHeaders = <String>{};
-  final excludedInclusionHeaders = <String>{};
-
-  final headerFilter = yamlConfig as YamlMap;
-  if (headerFilter != null) {
-    // Add include/excluded header-filter from Yaml.
-    final include = headerFilter[strings.include] as YamlList;
-    include?.cast<String>()?.forEach(includedInclusionHeaders.add);
-
-    final exclude = headerFilter[strings.exclude] as YamlList;
-    exclude?.cast<String>()?.forEach(excludedInclusionHeaders.add);
-  }
-
-  return HeaderFilter(
-    includedInclusionHeaders: includedInclusionHeaders,
-    excludedInclusionHeaders: excludedInclusionHeaders,
-  );
-}
-
-bool headerFilterValidator(String name, dynamic value) {
-  if (value is! YamlMap) {
-    _logger.severe("Expected value of key '$name' to be a Map.");
-    return false;
-  } else {
-    return true;
-  }
-}
-
-List<String> headersExtractor(dynamic yamlConfig) {
-  final headers = <String>[];
-  for (final h in (yamlConfig as YamlList)) {
-    final headerGlob = h as String;
-    // Add file directly to header if it's not a Glob but a File.
-    if (File(headerGlob).existsSync()) {
-      final osSpecificPath = _replaceSeparators(headerGlob);
-      headers.add(osSpecificPath);
-      _logger.fine('Adding header/file: $headerGlob');
-    } else {
-      final glob = Glob(headerGlob);
-      for (final file in glob.listSync(followLinks: true)) {
-        final fixedPath = _replaceSeparators(file.path);
-        headers.add(fixedPath);
-        _logger.fine('Adding header/file: ${fixedPath}');
+Headers headersExtractor(dynamic yamlConfig) {
+  final entryPoints = <String>[];
+  final includeGlobs = <quiver.Glob>[];
+  for (final key in (yamlConfig as YamlMap).keys) {
+    if (key == strings.entryPoints) {
+      for (final h in (yamlConfig[key] as YamlList)) {
+        final headerGlob = h as String;
+        // Add file directly to header if it's not a Glob but a File.
+        if (File(headerGlob).existsSync()) {
+          final osSpecificPath = _replaceSeparators(headerGlob);
+          entryPoints.add(osSpecificPath);
+          _logger.fine('Adding header/file: $headerGlob');
+        } else {
+          final glob = Glob(headerGlob);
+          for (final file in glob.listSync(followLinks: true)) {
+            final fixedPath = _replaceSeparators(file.path);
+            entryPoints.add(fixedPath);
+            _logger.fine('Adding header/file: ${fixedPath}');
+          }
+        }
+      }
+    }
+    if (key == strings.includeDirectives) {
+      for (final h in (yamlConfig[key] as YamlList)) {
+        final headerGlob = h as String;
+        includeGlobs.add(quiver.Glob(headerGlob));
       }
     }
   }
-  return headers;
+  return Headers(
+    entryPoints: entryPoints,
+    includeFilter: GlobHeaderFilter(
+      includeGlobs: includeGlobs,
+    ),
+  );
 }
 
 bool headersValidator(String name, dynamic value) {
-  if (value is! YamlList) {
-    _logger.severe(
-        "Expected value of key '${strings.headers}' to be a List of String.");
+  if (!checkType<YamlMap>(name, value)) {
+    return false;
+  }
+  if (!(value as YamlMap).containsKey(strings.entryPoints)) {
+    _logger.severe("Expected '$name -> ${strings.entryPoints}' to be a Map.");
     return false;
   } else {
+    for (final key in (value as YamlMap).keys) {
+      if (key == strings.entryPoints || key == strings.includeDirectives) {
+        if (!checkType<YamlList>(key as String, value[key])) {
+          _logger.severe("Expected '$name -> $key' to be a Map.");
+          return false;
+        }
+      } else {
+        _logger.severe("Unknown key '$key' in '$name'.");
+        return false;
+      }
+    }
     return true;
   }
 }
@@ -140,8 +137,7 @@
 String libclangDylibExtractor(dynamic value) => getDylibPath(value as String);
 
 bool libclangDylibValidator(String name, dynamic value) {
-  if (value is! String) {
-    _logger.severe("Expected value of key '$name' to be a string.");
+  if (!checkType<String>(name, value)) {
     return false;
   } else {
     final dylibPath = getDylibPath(value as String);
@@ -170,14 +166,8 @@
 
 String outputExtractor(dynamic value) => _replaceSeparators(value as String);
 
-bool outputValidator(String name, dynamic value) {
-  if (value is String) {
-    return true;
-  } else {
-    _logger.severe("Expected value of key '$name' to be a String.");
-    return false;
-  }
-}
+bool outputValidator(String name, dynamic value) =>
+    checkType<String>(name, value);
 
 Declaration declarationConfigExtractor(dynamic yamlMap) {
   List<String> includeMatchers, includeFull, excludeMatchers, excludeFull;
diff --git a/lib/src/header_parser/includer.dart b/lib/src/header_parser/includer.dart
index b00fa4f..c17b13e 100644
--- a/lib/src/header_parser/includer.dart
+++ b/lib/src/header_parser/includer.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:ffigen/src/code_generator.dart';
-import 'package:path/path.dart' as p;
 import 'data.dart';
 
 /// Utility functions to check whether a binding should be parsed or not
@@ -58,7 +57,10 @@
   }
 }
 
-/// True if a cursor should be included based on header-filter, use for root
+/// Cache for headers.
+final _headerCache = <String, bool>{};
+
+/// True if a cursor should be included based on headers config, used on root
 /// declarations.
 bool shouldIncludeRootCursor(String sourceFile) {
   // Handle null in case of system headers or macros.
@@ -66,22 +68,13 @@
     return false;
   }
 
-  final name = p.basename(sourceFile);
-
-  if (config.headerFilter.excludedInclusionHeaders.contains(name)) {
-    return false;
+  // Add header to cache if its not.
+  if (!_headerCache.containsKey(sourceFile)) {
+    _headerCache[sourceFile] =
+        config.headers.includeFilter.shouldInclude(sourceFile);
   }
 
-  if (config.headerFilter.includedInclusionHeaders.contains(name)) {
-    return true;
-  }
-
-  // If any includedInclusionHeaders is provided, return false.
-  if (config.headerFilter.includedInclusionHeaders.isNotEmpty) {
-    return false;
-  } else {
-    return true;
-  }
+  return _headerCache[sourceFile];
 }
 
 bool isSeenStruc(String originalName) {
diff --git a/lib/src/header_parser/parser.dart b/lib/src/header_parser/parser.dart
index 6730d22..29a86e5 100644
--- a/lib/src/header_parser/parser.dart
+++ b/lib/src/header_parser/parser.dart
@@ -86,9 +86,9 @@
   final bindings = <Binding>[];
 
   // Log all headers for user.
-  _logger.info('Input Headers: ${config.headers}');
+  _logger.info('Input Headers: ${config.headers.entryPoints}');
 
-  for (final headerLocation in config.headers) {
+  for (final headerLocation in config.headers.entryPoints) {
     _logger.fine('Creating TranslationUnit for header: $headerLocation');
 
     final tu = clang.clang_parseTranslationUnit(
diff --git a/lib/src/header_parser/sub_parsers/macro_parser.dart b/lib/src/header_parser/sub_parsers/macro_parser.dart
index 4da1b04..3971daf 100644
--- a/lib/src/header_parser/sub_parsers/macro_parser.dart
+++ b/lib/src/header_parser/sub_parsers/macro_parser.dart
@@ -195,7 +195,7 @@
 
   // Write file contents.
   final sb = StringBuffer();
-  for (final h in config.headers) {
+  for (final h in config.headers.entryPoints) {
     sb.writeln('#include "$h"');
   }
 
diff --git a/lib/src/header_parser/translation_unit_parser.dart b/lib/src/header_parser/translation_unit_parser.dart
index 4723fe4..4c1f36c 100644
--- a/lib/src/header_parser/translation_unit_parser.dart
+++ b/lib/src/header_parser/translation_unit_parser.dart
@@ -64,7 +64,7 @@
       }
     } else {
       _logger.finest(
-          'rootCursorVisitor:(excluded in header-filter) ${cursor.completeStringRepr()}');
+          'rootCursorVisitor:(not included) ${cursor.completeStringRepr()}');
     }
 
     cursor.dispose();
diff --git a/lib/src/strings.dart b/lib/src/strings.dart
index cfa1f5d..6196075 100644
--- a/lib/src/strings.dart
+++ b/lib/src/strings.dart
@@ -27,10 +27,14 @@
 const ffigenFolderName = 'ffigen';
 
 const output = 'output';
+
 const headers = 'headers';
-const headerFilter = 'header-filter';
+
+// Sub-fields of headers
+const entryPoints = 'entry-points';
+const includeDirectives = 'include-directives';
+
 const compilerOpts = 'compiler-opts';
-const filters = 'filters';
 
 // Declarations.
 const functions = 'functions';
diff --git a/pubspec.yaml b/pubspec.yaml
index fb8ad12..3151c7d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: ffigen
-version: 0.1.5
+version: 0.2.0-dev
 homepage: https://github.com/dart-lang/ffigen
 description: Experimental generator for FFI bindings, using LibClang to parse C/C++ header files.
 
@@ -18,6 +18,7 @@
   logging: ^0.11.4
   glob: ^1.2.0
   path: ^1.7.0
+  quiver: ^2.1.3
 
 dev_dependencies:
   pedantic: ^1.9.2
diff --git a/test/example_tests/cjson_example_test.dart b/test/example_tests/cjson_example_test.dart
new file mode 100644
index 0000000..b4093a8
--- /dev/null
+++ b/test/example_tests/cjson_example_test.dart
@@ -0,0 +1,40 @@
+// 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 'package:ffigen/src/header_parser.dart';
+import 'package:logging/logging.dart';
+import 'package:yaml/yaml.dart';
+import 'package:ffigen/src/config_provider/config.dart';
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:test/test.dart';
+
+import '../test_utils.dart';
+
+void main() {
+  group('cjson_example_test', () {
+    setUpAll(() {
+      logWarnings(Level.SEVERE);
+    });
+    test('c_json', () {
+      final config = Config.fromYaml(loadYaml('''
+${strings.output}: 'cjson_generated_bindings.dart'
+${strings.name}: 'CJson'
+${strings.description}: 'Holds bindings to cJSON.'
+${strings.headers}:
+  ${strings.entryPoints}:
+    - 'third_party/cjson_library/cJSON.h'
+  ${strings.includeDirectives}:
+    - '**cJSON.h'
+${strings.comments}: false
+''') as YamlMap);
+      final library = parse(config);
+
+      matchLibraryWithExpected(
+        library,
+        ['test', 'debug_generated', 'c_json.dart'],
+        ['example', 'c_json', config.output],
+      );
+    });
+  });
+}
diff --git a/test/example_tests/libclang_example_test.dart b/test/example_tests/libclang_example_test.dart
new file mode 100644
index 0000000..761dddc
--- /dev/null
+++ b/test/example_tests/libclang_example_test.dart
@@ -0,0 +1,65 @@
+// 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 'package:ffigen/src/header_parser.dart';
+import 'package:logging/logging.dart';
+import 'package:yaml/yaml.dart';
+import 'package:ffigen/src/config_provider/config.dart';
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:test/test.dart';
+
+import '../test_utils.dart';
+
+void main() {
+  group('example_test', () {
+    setUpAll(() {
+      logWarnings(Level.SEVERE);
+    });
+    test('libclang-example', () {
+      final config = Config.fromYaml(loadYaml('''
+${strings.output}: 'generated_bindings.dart'
+${strings.sort}: true
+${strings.headers}:
+  ${strings.entryPoints}:
+    - third_party/libclang/include/clang-c/Index.h
+  ${strings.include}-directives:
+    - '**CXString.h'
+    - '**Index.h'
+
+${strings.compilerOpts}: '-I/usr/lib/llvm-9/include/ -I/usr/lib/llvm-10/include/ -IC:\Progra~1\LLVM\include -I/usr/local/opt/llvm/include/ -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ -Wno-nullability-completeness'
+${strings.functions}:
+  ${strings.include}:
+    ${strings.matches}:
+      - 'clang_.*'
+${strings.structs}:
+  ${strings.include}:
+      ${strings.matches}:
+        - 'CX.*'
+${strings.enums}:
+  ${strings.include}:
+    ${strings.names}:
+      - 'CXTypeKind'
+      - 'CXGlobalOptFlags'
+
+${strings.name}: 'LibClang'
+${strings.description}: 'Holds bindings to LibClang.'
+${strings.arrayWorkaround}: true
+${strings.preamble}: |
+  /// AUTO GENERATED FILE, DO NOT EDIT.
+  ///
+  /// Generated by `package:ffigen`.
+${strings.comments}:
+  ${strings.style}: ${strings.doxygen}
+  ${strings.length}: ${strings.full}
+''') as YamlMap);
+      final library = parse(config);
+
+      matchLibraryWithExpected(
+        library,
+        ['test', 'debug_generated', 'libclang-example.dart'],
+        ['example', 'libclang-example', config.output],
+      );
+    });
+  });
+}
diff --git a/test/example_tests/simple_example_test.dart b/test/example_tests/simple_example_test.dart
new file mode 100644
index 0000000..5b22de7
--- /dev/null
+++ b/test/example_tests/simple_example_test.dart
@@ -0,0 +1,38 @@
+// 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 'package:ffigen/src/header_parser.dart';
+import 'package:logging/logging.dart';
+import 'package:yaml/yaml.dart';
+import 'package:ffigen/src/config_provider/config.dart';
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:test/test.dart';
+
+import '../test_utils.dart';
+
+void main() {
+  group('simple_example_test', () {
+    setUpAll(() {
+      logWarnings(Level.SEVERE);
+    });
+
+    test('simple', () {
+      final config = Config.fromYaml(loadYaml('''
+${strings.name}: NativeLibrary
+${strings.description}: Bindings to `headers/example.h`.
+${strings.output}: 'generated_bindings.dart'
+${strings.headers}:
+  ${strings.entryPoints}:
+    - 'example/simple/headers/example.h'
+''') as YamlMap);
+      final library = parse(config);
+
+      matchLibraryWithExpected(
+        library,
+        ['test', 'debug_generated', 'simple.dart'],
+        ['example', 'simple', config.output],
+      );
+    });
+  });
+}
diff --git a/test/header_parser_tests/function_n_struct_test.dart b/test/header_parser_tests/function_n_struct_test.dart
index 872bf1b..b3da1d8 100644
--- a/test/header_parser_tests/function_n_struct_test.dart
+++ b/test/header_parser_tests/function_n_struct_test.dart
@@ -26,7 +26,8 @@
 ${strings.output}: 'unused'
 
 ${strings.headers}:
-  - 'test/header_parser_tests/function_n_struct.h'
+  ${strings.entryPoints}:
+    - 'test/header_parser_tests/function_n_struct.h'
         ''') as yaml.YamlMap),
       );
     });
diff --git a/test/header_parser_tests/functions_test.dart b/test/header_parser_tests/functions_test.dart
index c88fd55..aba98e6 100644
--- a/test/header_parser_tests/functions_test.dart
+++ b/test/header_parser_tests/functions_test.dart
@@ -25,10 +25,10 @@
 ${strings.output}: 'unused'
 
 ${strings.headers}:
-  - 'test/header_parser_tests/functions.h'
-${strings.headerFilter}:
-  ${strings.include}:
-    - 'functions.h'
+  ${strings.entryPoints}:
+    - 'test/header_parser_tests/functions.h'
+  ${strings.includeDirectives}:
+    - '**functions.h'
         ''') as yaml.YamlMap),
       );
     });
diff --git a/test/header_parser_tests/macros_test.dart b/test/header_parser_tests/macros_test.dart
index b912f78..74bbcfc 100644
--- a/test/header_parser_tests/macros_test.dart
+++ b/test/header_parser_tests/macros_test.dart
@@ -25,10 +25,8 @@
 ${strings.description}: 'Macros Test'
 ${strings.output}: 'unused'
 ${strings.headers}:
-  - 'test/header_parser_tests/macros.h'
-${strings.headerFilter}:
-  ${strings.include}:
-    - 'macros.h'
+  ${strings.entryPoints}:
+    - 'test/header_parser_tests/macros.h'
         ''') as yaml.YamlMap),
       );
     });
diff --git a/test/header_parser_tests/nested_parsing_test.dart b/test/header_parser_tests/nested_parsing_test.dart
index 0736231..ed1bacf 100644
--- a/test/header_parser_tests/nested_parsing_test.dart
+++ b/test/header_parser_tests/nested_parsing_test.dart
@@ -25,14 +25,12 @@
 ${strings.output}: 'unused'
 
 ${strings.headers}:
-  - 'test/header_parser_tests/nested_parsing.h'
-structs:
-  include:
-    names:
-      - Struct1
-${strings.headerFilter}:
+  ${strings.entryPoints}:
+    - 'test/header_parser_tests/nested_parsing.h'
+${strings.structs}:
   ${strings.include}:
-    - 'nested_parsing.h'
+    ${strings.names}:
+      - Struct1
         ''') as yaml.YamlMap),
       );
     });
diff --git a/test/header_parser_tests/unnamed_enums_test.dart b/test/header_parser_tests/unnamed_enums_test.dart
index 7c07602..a7ee4c4 100644
--- a/test/header_parser_tests/unnamed_enums_test.dart
+++ b/test/header_parser_tests/unnamed_enums_test.dart
@@ -24,10 +24,8 @@
 ${strings.description}: 'Unnamed Enums Test'
 ${strings.output}: 'unused'
 ${strings.headers}:
-  - 'test/header_parser_tests/unnamed_enums.h'
-${strings.headerFilter}:
-  ${strings.include}:
-    - 'unnamed_enums.h'
+  ${strings.entryPoints}:
+    - 'test/header_parser_tests/unnamed_enums.h'
 ${strings.enums}:
   ${strings.exclude}:
     ${strings.names}:
diff --git a/test/large_integration_tests/large_test.dart b/test/large_integration_tests/large_test.dart
index 542b3ce..b18f52b 100644
--- a/test/large_integration_tests/large_test.dart
+++ b/test/large_integration_tests/large_test.dart
@@ -2,8 +2,6 @@
 // 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/header_parser.dart';
 import 'package:logging/logging.dart';
 import 'package:yaml/yaml.dart';
@@ -30,36 +28,24 @@
   ${strings.style}: ${strings.doxygen}
   ${strings.length}: ${strings.brief}
 ${strings.headers}:
-  - third_party/libclang/include/clang-c/Index.h
-${strings.headerFilter}:
-  include:
-    - 'BuildSystem.h'
-    - 'CXCompilationDatabase.h'
-    - 'CXErrorCode.h'
-    - 'CXString.h'
-    - 'Documentation.h'
-    - 'FataErrorHandler.h'
-    - 'Index.h'
+  ${strings.entryPoints}:
+    - third_party/libclang/include/clang-c/Index.h
+  ${strings.includeDirectives}:
+    - '**BuildSystem.h'
+    - '**CXCompilationDatabase.h'
+    - '**CXErrorCode.h'
+    - '**CXString.h'
+    - '**Documentation.h'
+    - '**FataErrorHandler.h'
+    - '**Index.h'
       ''') as YamlMap);
       final library = parse(config);
-      final file = File(
-        path.join('test', 'debug_generated', 'large_test_libclang.dart'),
-      );
-      library.generateFile(file);
 
-      try {
-        final actual = file.readAsStringSync();
-        final expected = File(path.join('test', 'large_integration_tests',
-                '_expected_libclang_bindings.dart'))
-            .readAsStringSync();
-        expect(actual, expected);
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        print('Failed test: Debug generated file: ${file.absolute?.path}');
-        rethrow;
-      }
+      matchLibraryWithExpected(
+        library,
+        ['test', 'debug_generated', 'large_test_libclang.dart'],
+        ['test', 'large_integration_tests', '_expected_libclang_bindings.dart'],
+      );
     });
 
     test('CJSON test', () {
@@ -71,30 +57,18 @@
   ${strings.length}: ${strings.full}
 ${strings.arrayWorkaround}: true
 ${strings.headers}:
-  - third_party/cjson_library/cJSON.h
-${strings.headerFilter}:
-  include:
-    - 'cJSON.h'
+  ${strings.entryPoints}:
+    - third_party/cjson_library/cJSON.h
+  ${strings.includeDirectives}:
+    - '**cJSON.h'
       ''') as YamlMap);
       final library = parse(config);
-      final file = File(
-        path.join('test', 'debug_generated', 'large_test_cjson.dart'),
-      );
-      library.generateFile(file);
 
-      try {
-        final actual = file.readAsStringSync();
-        final expected = File(path.join('test', 'large_integration_tests',
-                '_expected_cjson_bindings.dart'))
-            .readAsStringSync();
-        expect(actual, expected);
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        print('Failed test: Debug generated file: ${file.absolute?.path}');
-        rethrow;
-      }
+      matchLibraryWithExpected(
+        library,
+        ['test', 'debug_generated', 'large_test_cjson.dart'],
+        ['test', 'large_integration_tests', '_expected_cjson_bindings.dart'],
+      );
     });
 
     test('SQLite test', () {
@@ -109,10 +83,10 @@
   ${strings.style}: ${strings.any}
   ${strings.length}: ${strings.full}
 ${strings.headers}:
-  - third_party/sqlite/sqlite3.h
-${strings.headerFilter}:
-  ${strings.include}:
-    - 'sqlite3.h'
+  ${strings.entryPoints}:
+    - third_party/sqlite/sqlite3.h
+  ${strings.includeDirectives}:
+    - '**sqlite3.h'
 ${strings.functions}:
   ${strings.exclude}:
     ${strings.names}:
@@ -121,24 +95,12 @@
       - sqlite3_str_vappendf
       ''') as YamlMap);
       final library = parse(config);
-      final file = File(
-        path.join('test', 'debug_generated', 'large_test_sqlite.dart'),
-      );
-      library.generateFile(file);
 
-      try {
-        final actual = file.readAsStringSync();
-        final expected = File(path.join('test', 'large_integration_tests',
-                '_expected_sqlite_bindings.dart'))
-            .readAsStringSync();
-        expect(actual, expected);
-        if (file.existsSync()) {
-          file.delete();
-        }
-      } catch (e) {
-        print('Failed test: Debug generated file: ${file.absolute?.path}');
-        rethrow;
-      }
+      matchLibraryWithExpected(
+        library,
+        ['test', 'debug_generated', 'large_test_sqlite.dart'],
+        ['test', 'large_integration_tests', '_expected_sqlite_bindings.dart'],
+      );
     });
   });
 }
diff --git a/test/native_test/config.yaml b/test/native_test/config.yaml
index 479988d..a81d932 100644
--- a/test/native_test/config.yaml
+++ b/test/native_test/config.yaml
@@ -3,14 +3,16 @@
 # BSD-style license that can be found in the LICENSE file.
 
 # =================== GENERATING TEST BINDINGS ==================
-#    dart ../../bin/ffigen.dart --config config.yaml
+#    pub run ffigen --config test/native_test/config.yaml
 # ===============================================================
 
-output: 'native_test_bindings.dart'
+name: NativeLibrary
+description: 'Native tests.'
+output: 'test/native_test/native_test_bindings.dart'
 sort: true
 headers:
-  - 'native_test.c'
-header-filter:
-  include:
-    - 'native_test.c'
+  entry-points:
+    - 'test/native_test/native_test.c'
+  include-directives:
+    - '**native_test.c'
 array-workaround: true
diff --git a/test/native_test/native_test.dart b/test/native_test/native_test.dart
index 0cec0cc..63d1824 100644
--- a/test/native_test/native_test.dart
+++ b/test/native_test/native_test.dart
@@ -6,12 +6,15 @@
 import 'dart:io';
 import 'dart:math';
 
+import 'package:ffigen/ffigen.dart';
+import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
-
+import 'package:yaml/yaml.dart';
 import '../test_utils.dart';
-import 'native_test_bindings.dart' as bindings;
+import 'native_test_bindings.dart';
 
 void main() {
+  NativeLibrary bindings;
   group('native_test', () {
     setUpAll(() {
       logWarnings();
@@ -21,9 +24,32 @@
       } else if (Platform.isWindows) {
         dylibName = r'test\native_test\native_test.dll';
       }
-      bindings.init(
+      bindings = NativeLibrary(
           DynamicLibrary.open(File(dylibName).absolute?.path ?? dylibName));
     });
+
+    test('generate_bindings', () {
+      final config = Config.fromYaml(loadYaml(
+          File(path.join('test', 'native_test', 'config.yaml'))
+              .readAsStringSync()) as YamlMap);
+      final library = parse(config);
+      final file = File(
+        path.join('test', 'debug_generated', 'native_test_bindings.dart'),
+      );
+      library.generateFile(file);
+
+      try {
+        final actual = file.readAsStringSync();
+        final expected = File(path.join(config.output)).readAsStringSync();
+        expect(actual, expected);
+        if (file.existsSync()) {
+          file.delete();
+        }
+      } catch (e) {
+        print('Failed test: Debug generated file: ${file.absolute?.path}');
+        rethrow;
+      }
+    });
     test('uint8_t', () {
       expect(bindings.Function1Uint8(pow(2, 8).toInt()), 42);
     });
diff --git a/test/native_test/native_test_bindings.dart b/test/native_test/native_test_bindings.dart
index a49d622..fe4c945 100644
--- a/test/native_test/native_test_bindings.dart
+++ b/test/native_test/native_test_bindings.dart
@@ -3,244 +3,181 @@
 /// Generated by `package:ffigen`.
 import 'dart:ffi' as ffi;
 
-/// Holds the Dynamic library.
-ffi.DynamicLibrary _dylib;
+/// Native tests.
+class NativeLibrary {
+  /// Holds the Dynamic library.
+  final ffi.DynamicLibrary _dylib;
 
-/// Initialises the Dynamic library.
-void init(ffi.DynamicLibrary dylib) {
-  _dylib = dylib;
+  /// The symbols are looked up in [dynamicLibrary].
+  NativeLibrary(ffi.DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary;
+
+  int Function1Uint8(
+    int x,
+  ) {
+    _Function1Uint8 ??=
+        _dylib.lookupFunction<_c_Function1Uint8, _dart_Function1Uint8>(
+            'Function1Uint8');
+    return _Function1Uint8(
+      x,
+    );
+  }
+
+  _dart_Function1Uint8 _Function1Uint8;
+
+  int Function1Uint16(
+    int x,
+  ) {
+    _Function1Uint16 ??=
+        _dylib.lookupFunction<_c_Function1Uint16, _dart_Function1Uint16>(
+            'Function1Uint16');
+    return _Function1Uint16(
+      x,
+    );
+  }
+
+  _dart_Function1Uint16 _Function1Uint16;
+
+  int Function1Uint32(
+    int x,
+  ) {
+    _Function1Uint32 ??=
+        _dylib.lookupFunction<_c_Function1Uint32, _dart_Function1Uint32>(
+            'Function1Uint32');
+    return _Function1Uint32(
+      x,
+    );
+  }
+
+  _dart_Function1Uint32 _Function1Uint32;
+
+  int Function1Uint64(
+    int x,
+  ) {
+    _Function1Uint64 ??=
+        _dylib.lookupFunction<_c_Function1Uint64, _dart_Function1Uint64>(
+            'Function1Uint64');
+    return _Function1Uint64(
+      x,
+    );
+  }
+
+  _dart_Function1Uint64 _Function1Uint64;
+
+  int Function1Int8(
+    int x,
+  ) {
+    _Function1Int8 ??= _dylib
+        .lookupFunction<_c_Function1Int8, _dart_Function1Int8>('Function1Int8');
+    return _Function1Int8(
+      x,
+    );
+  }
+
+  _dart_Function1Int8 _Function1Int8;
+
+  int Function1Int16(
+    int x,
+  ) {
+    _Function1Int16 ??=
+        _dylib.lookupFunction<_c_Function1Int16, _dart_Function1Int16>(
+            'Function1Int16');
+    return _Function1Int16(
+      x,
+    );
+  }
+
+  _dart_Function1Int16 _Function1Int16;
+
+  int Function1Int32(
+    int x,
+  ) {
+    _Function1Int32 ??=
+        _dylib.lookupFunction<_c_Function1Int32, _dart_Function1Int32>(
+            'Function1Int32');
+    return _Function1Int32(
+      x,
+    );
+  }
+
+  _dart_Function1Int32 _Function1Int32;
+
+  int Function1Int64(
+    int x,
+  ) {
+    _Function1Int64 ??=
+        _dylib.lookupFunction<_c_Function1Int64, _dart_Function1Int64>(
+            'Function1Int64');
+    return _Function1Int64(
+      x,
+    );
+  }
+
+  _dart_Function1Int64 _Function1Int64;
+
+  int Function1IntPtr(
+    int x,
+  ) {
+    _Function1IntPtr ??=
+        _dylib.lookupFunction<_c_Function1IntPtr, _dart_Function1IntPtr>(
+            'Function1IntPtr');
+    return _Function1IntPtr(
+      x,
+    );
+  }
+
+  _dart_Function1IntPtr _Function1IntPtr;
+
+  double Function1Float(
+    double x,
+  ) {
+    _Function1Float ??=
+        _dylib.lookupFunction<_c_Function1Float, _dart_Function1Float>(
+            'Function1Float');
+    return _Function1Float(
+      x,
+    );
+  }
+
+  _dart_Function1Float _Function1Float;
+
+  double Function1Double(
+    double x,
+  ) {
+    _Function1Double ??=
+        _dylib.lookupFunction<_c_Function1Double, _dart_Function1Double>(
+            'Function1Double');
+    return _Function1Double(
+      x,
+    );
+  }
+
+  _dart_Function1Double _Function1Double;
+
+  ffi.Pointer<Struct1> getStruct1() {
+    _getStruct1 ??=
+        _dylib.lookupFunction<_c_getStruct1, _dart_getStruct1>('getStruct1');
+    return _getStruct1();
+  }
+
+  _dart_getStruct1 _getStruct1;
 }
 
-double Function1Double(
-  double x,
-) {
-  return _Function1Double(
-    x,
-  );
-}
-
-final _dart_Function1Double _Function1Double =
-    _dylib.lookupFunction<_c_Function1Double, _dart_Function1Double>(
-        'Function1Double');
-
-typedef _c_Function1Double = ffi.Double Function(
-  ffi.Double x,
-);
-
-typedef _dart_Function1Double = double Function(
-  double x,
-);
-
-double Function1Float(
-  double x,
-) {
-  return _Function1Float(
-    x,
-  );
-}
-
-final _dart_Function1Float _Function1Float = _dylib
-    .lookupFunction<_c_Function1Float, _dart_Function1Float>('Function1Float');
-
-typedef _c_Function1Float = ffi.Float Function(
-  ffi.Float x,
-);
-
-typedef _dart_Function1Float = double Function(
-  double x,
-);
-
-int Function1Int16(
-  int x,
-) {
-  return _Function1Int16(
-    x,
-  );
-}
-
-final _dart_Function1Int16 _Function1Int16 = _dylib
-    .lookupFunction<_c_Function1Int16, _dart_Function1Int16>('Function1Int16');
-
-typedef _c_Function1Int16 = ffi.Int16 Function(
-  ffi.Int16 x,
-);
-
-typedef _dart_Function1Int16 = int Function(
-  int x,
-);
-
-int Function1Int32(
-  int x,
-) {
-  return _Function1Int32(
-    x,
-  );
-}
-
-final _dart_Function1Int32 _Function1Int32 = _dylib
-    .lookupFunction<_c_Function1Int32, _dart_Function1Int32>('Function1Int32');
-
-typedef _c_Function1Int32 = ffi.Int32 Function(
-  ffi.Int32 x,
-);
-
-typedef _dart_Function1Int32 = int Function(
-  int x,
-);
-
-int Function1Int64(
-  int x,
-) {
-  return _Function1Int64(
-    x,
-  );
-}
-
-final _dart_Function1Int64 _Function1Int64 = _dylib
-    .lookupFunction<_c_Function1Int64, _dart_Function1Int64>('Function1Int64');
-
-typedef _c_Function1Int64 = ffi.Int64 Function(
-  ffi.Int64 x,
-);
-
-typedef _dart_Function1Int64 = int Function(
-  int x,
-);
-
-int Function1Int8(
-  int x,
-) {
-  return _Function1Int8(
-    x,
-  );
-}
-
-final _dart_Function1Int8 _Function1Int8 = _dylib
-    .lookupFunction<_c_Function1Int8, _dart_Function1Int8>('Function1Int8');
-
-typedef _c_Function1Int8 = ffi.Int8 Function(
-  ffi.Int8 x,
-);
-
-typedef _dart_Function1Int8 = int Function(
-  int x,
-);
-
-int Function1IntPtr(
-  int x,
-) {
-  return _Function1IntPtr(
-    x,
-  );
-}
-
-final _dart_Function1IntPtr _Function1IntPtr =
-    _dylib.lookupFunction<_c_Function1IntPtr, _dart_Function1IntPtr>(
-        'Function1IntPtr');
-
-typedef _c_Function1IntPtr = ffi.IntPtr Function(
-  ffi.IntPtr x,
-);
-
-typedef _dart_Function1IntPtr = int Function(
-  int x,
-);
-
-int Function1Uint16(
-  int x,
-) {
-  return _Function1Uint16(
-    x,
-  );
-}
-
-final _dart_Function1Uint16 _Function1Uint16 =
-    _dylib.lookupFunction<_c_Function1Uint16, _dart_Function1Uint16>(
-        'Function1Uint16');
-
-typedef _c_Function1Uint16 = ffi.Uint16 Function(
-  ffi.Uint16 x,
-);
-
-typedef _dart_Function1Uint16 = int Function(
-  int x,
-);
-
-int Function1Uint32(
-  int x,
-) {
-  return _Function1Uint32(
-    x,
-  );
-}
-
-final _dart_Function1Uint32 _Function1Uint32 =
-    _dylib.lookupFunction<_c_Function1Uint32, _dart_Function1Uint32>(
-        'Function1Uint32');
-
-typedef _c_Function1Uint32 = ffi.Uint32 Function(
-  ffi.Uint32 x,
-);
-
-typedef _dart_Function1Uint32 = int Function(
-  int x,
-);
-
-int Function1Uint64(
-  int x,
-) {
-  return _Function1Uint64(
-    x,
-  );
-}
-
-final _dart_Function1Uint64 _Function1Uint64 =
-    _dylib.lookupFunction<_c_Function1Uint64, _dart_Function1Uint64>(
-        'Function1Uint64');
-
-typedef _c_Function1Uint64 = ffi.Uint64 Function(
-  ffi.Uint64 x,
-);
-
-typedef _dart_Function1Uint64 = int Function(
-  int x,
-);
-
-int Function1Uint8(
-  int x,
-) {
-  return _Function1Uint8(
-    x,
-  );
-}
-
-final _dart_Function1Uint8 _Function1Uint8 = _dylib
-    .lookupFunction<_c_Function1Uint8, _dart_Function1Uint8>('Function1Uint8');
-
-typedef _c_Function1Uint8 = ffi.Uint8 Function(
-  ffi.Uint8 x,
-);
-
-typedef _dart_Function1Uint8 = int Function(
-  int x,
-);
-
 class Struct1 extends ffi.Struct {
   @ffi.Int8()
   int a;
 
   @ffi.Int32()
-  int _data_item_0;
+  int _unique_data_item_0;
   @ffi.Int32()
-  int _data_item_1;
+  int _unique_data_item_1;
   @ffi.Int32()
-  int _data_item_2;
+  int _unique_data_item_2;
   @ffi.Int32()
-  int _data_item_3;
+  int _unique_data_item_3;
   @ffi.Int32()
-  int _data_item_4;
+  int _unique_data_item_4;
   @ffi.Int32()
-  int _data_item_5;
+  int _unique_data_item_5;
 
   /// Helper for array `data`.
   ArrayHelper_Struct1_data_level0 get data =>
@@ -321,17 +258,17 @@
     _checkBounds(index);
     switch (_absoluteIndex + index) {
       case 0:
-        return _struct._data_item_0;
+        return _struct._unique_data_item_0;
       case 1:
-        return _struct._data_item_1;
+        return _struct._unique_data_item_1;
       case 2:
-        return _struct._data_item_2;
+        return _struct._unique_data_item_2;
       case 3:
-        return _struct._data_item_3;
+        return _struct._unique_data_item_3;
       case 4:
-        return _struct._data_item_4;
+        return _struct._unique_data_item_4;
       case 5:
-        return _struct._data_item_5;
+        return _struct._unique_data_item_5;
       default:
         throw Exception('Invalid Array Helper generated.');
     }
@@ -341,22 +278,22 @@
     _checkBounds(index);
     switch (_absoluteIndex + index) {
       case 0:
-        _struct._data_item_0 = value;
+        _struct._unique_data_item_0 = value;
         break;
       case 1:
-        _struct._data_item_1 = value;
+        _struct._unique_data_item_1 = value;
         break;
       case 2:
-        _struct._data_item_2 = value;
+        _struct._unique_data_item_2 = value;
         break;
       case 3:
-        _struct._data_item_3 = value;
+        _struct._unique_data_item_3 = value;
         break;
       case 4:
-        _struct._data_item_4 = value;
+        _struct._unique_data_item_4 = value;
         break;
       case 5:
-        _struct._data_item_5 = value;
+        _struct._unique_data_item_5 = value;
         break;
       default:
         throw Exception('Invalid Array Helper generated.');
@@ -364,12 +301,93 @@
   }
 }
 
-ffi.Pointer<Struct1> getStruct1() {
-  return _getStruct1();
-}
+typedef _c_Function1Uint8 = ffi.Uint8 Function(
+  ffi.Uint8 x,
+);
 
-final _dart_getStruct1 _getStruct1 =
-    _dylib.lookupFunction<_c_getStruct1, _dart_getStruct1>('getStruct1');
+typedef _dart_Function1Uint8 = int Function(
+  int x,
+);
+
+typedef _c_Function1Uint16 = ffi.Uint16 Function(
+  ffi.Uint16 x,
+);
+
+typedef _dart_Function1Uint16 = int Function(
+  int x,
+);
+
+typedef _c_Function1Uint32 = ffi.Uint32 Function(
+  ffi.Uint32 x,
+);
+
+typedef _dart_Function1Uint32 = int Function(
+  int x,
+);
+
+typedef _c_Function1Uint64 = ffi.Uint64 Function(
+  ffi.Uint64 x,
+);
+
+typedef _dart_Function1Uint64 = int Function(
+  int x,
+);
+
+typedef _c_Function1Int8 = ffi.Int8 Function(
+  ffi.Int8 x,
+);
+
+typedef _dart_Function1Int8 = int Function(
+  int x,
+);
+
+typedef _c_Function1Int16 = ffi.Int16 Function(
+  ffi.Int16 x,
+);
+
+typedef _dart_Function1Int16 = int Function(
+  int x,
+);
+
+typedef _c_Function1Int32 = ffi.Int32 Function(
+  ffi.Int32 x,
+);
+
+typedef _dart_Function1Int32 = int Function(
+  int x,
+);
+
+typedef _c_Function1Int64 = ffi.Int64 Function(
+  ffi.Int64 x,
+);
+
+typedef _dart_Function1Int64 = int Function(
+  int x,
+);
+
+typedef _c_Function1IntPtr = ffi.IntPtr Function(
+  ffi.IntPtr x,
+);
+
+typedef _dart_Function1IntPtr = int Function(
+  int x,
+);
+
+typedef _c_Function1Float = ffi.Float Function(
+  ffi.Float x,
+);
+
+typedef _dart_Function1Float = double Function(
+  double x,
+);
+
+typedef _c_Function1Double = ffi.Double Function(
+  ffi.Double x,
+);
+
+typedef _dart_Function1Double = double Function(
+  double x,
+);
 
 typedef _c_getStruct1 = ffi.Pointer<Struct1> Function();
 
diff --git a/test/prefix_tests/prefix_test.dart b/test/prefix_tests/prefix_test.dart
index ba1e56d..55ebc8a 100644
--- a/test/prefix_tests/prefix_test.dart
+++ b/test/prefix_tests/prefix_test.dart
@@ -33,10 +33,8 @@
 ${strings.output}: 'unused'
 
 ${strings.headers}:
-  - 'test/prefix_tests/prefix.h'
-${strings.headerFilter}:
-  ${strings.include}:
-    - 'prefix.h'
+  ${strings.entryPoints}:
+    - 'test/prefix_tests/prefix.h'
 
 functions:
   ${strings.prefix}: $functionPrefix
diff --git a/test/test_utils.dart b/test/test_utils.dart
index 8d49c18..0d94d29 100644
--- a/test/test_utils.dart
+++ b/test/test_utils.dart
@@ -2,8 +2,12 @@
 // 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:logging/logging.dart';
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart';
 
 /// Extracts a binding's string from a library.
 
@@ -31,6 +35,29 @@
   }
 }
 
+/// Generates actual file using library and tests using [expect] with expected
+///
+/// This will not delete the actual debug file incase [expect] throws an error.
+void matchLibraryWithExpected(
+    Library library, List<String> pathForActual, List<String> pathToExpected) {
+  final file = File(
+    path.joinAll(pathForActual),
+  );
+  library.generateFile(file);
+
+  try {
+    final actual = file.readAsStringSync();
+    final expected = File(path.joinAll(pathToExpected)).readAsStringSync();
+    expect(actual, expected);
+    if (file.existsSync()) {
+      file.delete();
+    }
+  } catch (e) {
+    print('Failed test: Debug generated file: ${file.absolute?.path}');
+    rethrow;
+  }
+}
+
 class NotFoundException implements Exception {
   final String message;
   NotFoundException(this.message);
diff --git a/tool/libclang_config.yaml b/tool/libclang_config.yaml
index bc0754c..8526317 100644
--- a/tool/libclang_config.yaml
+++ b/tool/libclang_config.yaml
@@ -15,12 +15,12 @@
 sort: true
 compiler-opts: '-I/usr/lib/llvm-9/include/ -I/usr/lib/llvm-10/include/ -I/usr/local/opt/llvm/include/ -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ -Wno-nullability-completeness'
 headers:
-  - 'lib/src/clang_library/wrapper.c'
-header-filter:
-  include:
-    - wrapper.c
-    - Index.h
-    - CXString.h
+  entry-points:
+    - 'lib/src/clang_library/wrapper.c'
+  include-directives:
+    - '**wrapper.c'
+    - '**Index.h'
+    - '**CXString.h'
 
 array-workaround: true