[ffigen] Provide a command to regenerate golden bindings. (#322)
diff --git a/pkgs/ffigen/README.md b/pkgs/ffigen/README.md index acf3b17..98c7f76 100644 --- a/pkgs/ffigen/README.md +++ b/pkgs/ffigen/README.md
@@ -509,11 +509,9 @@ 2. Run `dart run ffigen`. ## Running Tests -Dynamic library for some tests need to be built before running the examples. -Run `dart test/setup.dart` to build the libraries. -Run tests from the root of the package with `dart run test`. -> Note: If llvm is not installed in one of the default locations, tests may fail. +See [test/README.md](test/README.md) + ## FAQ ### Can ffigen be used for removing underscores or renaming declarations? Ffigen supports **regexp based renaming**, the regexp must be a
diff --git a/pkgs/ffigen/test/README.md b/pkgs/ffigen/test/README.md new file mode 100644 index 0000000..1a499a4 --- /dev/null +++ b/pkgs/ffigen/test/README.md
@@ -0,0 +1,64 @@ +[](https://github.com/dart-lang/ffigen/actions?query=workflow%3A"Dart+CI") +[](https://coveralls.io/github/dart-lang/ffigen?branch=master) + +# ffigen testing + +## Running Tests + +1. Some tests require that dynamic libraries be built before running the tests. + You can do so with: + + ```shell + dart run test/setup.dart + ``` + +2. Run the tests with: + + ```shell + dart test + ``` + +## Development + +Some tests verify that the generated Dart FFI bindings match a golden file. + +For example, the test +[`test/native_test/native_test.dart`](https://github.com/dart-lang/ffigen/blob/master/test/native_test/native_test.dart) +works by: + +1. Loading the dynamic library for + [`test/native_test/native_test.c`](https://github.com/dart-lang/ffigen/blob/master/test/native_test/native_test.c) + (which was generated by `dart run test/setup.dart`). + +2. Generating binding files for that dynamic library in the + `test/debug_generated` directory. + +3. Comparing the golden file (i.e. + [`test/native_test/native_test_bindings.dart`](https://github.com/dart-lang/ffigen/blob/master/test/native_test/native_test_bindings.dart)) + to the generated file and generating a test failure if they do not match. + +4. Using the *golden* bindings to excercise the dynamic library. + +If you modify any source for a dynamic library (e.g. any `.c`, `.h` or `.m` +files), then you should run: + +```shell + dart run test/setup.dart # Rebuild the dynamic libraries. + dart run test/regen.dart # Rebuild the golden FFI bindings. +``` + +If you modify any code that changes how FFI bindings are generated, then you +should run: + + ```shell + dart run test/regen.dart # Rebuild the golden FFI bindings. + ``` + +A conservative way to run tests is with: + + ```shell + dart run test/setup.dart && dart run test/regen.dart && dart test + ``` + +> Note: you should verify that the changes to the golden bindings are +> reasonable with `git diff`.
diff --git a/pkgs/ffigen/test/regen.dart b/pkgs/ffigen/test/regen.dart new file mode 100644 index 0000000..1bc2021 --- /dev/null +++ b/pkgs/ffigen/test/regen.dart
@@ -0,0 +1,91 @@ +// Copyright (c) 2022, 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:async'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:ffigen/ffigen.dart'; +import 'package:logging/logging.dart'; +import 'package:yaml/yaml.dart'; + +const usage = r'''Regenerates the Dart FFI bindings used in tests and examples. + +Use this command when developing features that change the generated bindings +e.g. with this command: + +$ dart run test/setup.dart && dart run test/regen.dart && dart test +'''; + +Future<void> _regenConfig(File yamlConfig, File bindingOutput, + {bool chDir = false}) async { + yamlConfig = yamlConfig.absolute; + bindingOutput = bindingOutput.absolute; + + Directory? oldDir; + var yaml = loadYaml(await yamlConfig.readAsString()) as YamlMap; + + if (chDir) { + oldDir = Directory.current; + Directory.current = yamlConfig.parent; + } + try { + if (yaml.containsKey("ffigen")) { + yaml = yaml["ffigen"] as YamlMap; + } + + final config = Config.fromYaml(yaml); + final library = parse(config); + library.generateFile(bindingOutput); + } finally { + if (oldDir != null) { + Directory.current = oldDir; + } + } +} + +Future<void> main(List<String> args) async { + final parser = ArgParser(); + parser.addSeparator(usage); + parser.addFlag( + 'help', + abbr: 'h', + help: 'Prints this usage', + negatable: false, + ); + + final parseArgs = parser.parse(args); + if (parseArgs.wasParsed('help')) { + print(parser.usage); + exit(0); + } else if (parseArgs.rest.isNotEmpty) { + print(parser.usage); + exit(1); + } + + Logger.root.level = Level.WARNING; + Logger.root.onRecord.listen((record) { + print('${record.level.name}: ${record.time}: ${record.message}'); + }); + + await _regenConfig( + File('test/native_objc_test/config.yaml'), + File('test/native_objc_test/native_objc_test_bindings.dart'), + ); + + await _regenConfig(File('test/native_test/config.yaml'), + File('test/native_test/native_test_bindings.dart')); + + await _regenConfig(File('example/libclang-example/pubspec.yaml'), + File('example/libclang-example/generated_bindings.dart'), + chDir: true); + + await _regenConfig(File('example/simple/pubspec.yaml'), + File('example/simple/generated_bindings.dart'), + chDir: true); + + await _regenConfig(File('example/c_json/pubspec.yaml'), + File('example/c_json/cjson_generated_bindings.dart'), + chDir: true); +}