test(config): add more test cases

This commit is contained in:
Aleksey Kulikov 2021-10-20 11:29:41 +03:00
parent 127849519d
commit 26812ffe9c
3 changed files with 128 additions and 94 deletions

View file

@ -1,43 +1,20 @@
// coverage:ignore-file
import 'dart:ffi'; import 'dart:ffi';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import '../error.dart'; import '../error.dart';
import 'libgit2_bindings.dart'; import 'libgit2_bindings.dart';
import '../util.dart'; import '../util.dart';
/// Allocate a new configuration object
///
/// This object is empty, so you have to add a file to it before you can do
/// anything with it.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_config> newConfig() {
final out = calloc<Pointer<git_config>>();
final error = libgit2.git_config_new(out);
if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Create a new config instance containing a single on-disk file /// Create a new config instance containing a single on-disk file
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_config> open(String path) { Pointer<git_config> open(String path) {
final out = calloc<Pointer<git_config>>(); final out = calloc<Pointer<git_config>>();
final pathC = path.toNativeUtf8().cast<Int8>(); final pathC = path.toNativeUtf8().cast<Int8>();
final error = libgit2.git_config_open_ondisk(out, pathC); libgit2.git_config_open_ondisk(out, pathC);
calloc.free(pathC); calloc.free(pathC);
if (error < 0) { return out.value;
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
} }
/// Open the global, XDG and system configuration files /// Open the global, XDG and system configuration files
@ -71,14 +48,14 @@ Pointer<git_config> openDefault() {
/// This method will not guess the path to the xdg compatible config file /// This method will not guess the path to the xdg compatible config file
/// (`.config/git/config`). /// (`.config/git/config`).
/// ///
/// Throws an error if file has not been found. /// Throws a [LibGit2Error] if error occured.
String findGlobal() { String findGlobal() {
final out = calloc<git_buf>(sizeOf<git_buf>()); final out = calloc<git_buf>(sizeOf<git_buf>());
final error = libgit2.git_config_find_global(out); final error = libgit2.git_config_find_global(out);
if (error != 0) { if (error < 0) {
calloc.free(out); calloc.free(out);
throw Error(); throw LibGit2Error(libgit2.git_error_last());
} else { } else {
final result = out.ref.ptr.cast<Utf8>().toDartString(); final result = out.ref.ptr.cast<Utf8>().toDartString();
calloc.free(out); calloc.free(out);
@ -130,18 +107,10 @@ String findXdg() {
/// Create a snapshot of the current state of a configuration, which allows you to look /// Create a snapshot of the current state of a configuration, which allows you to look
/// into a consistent view of the configuration for looking up complex values /// into a consistent view of the configuration for looking up complex values
/// (e.g. a remote, submodule). /// (e.g. a remote, submodule).
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_config> snapshot(Pointer<git_config> config) { Pointer<git_config> snapshot(Pointer<git_config> config) {
final out = calloc<Pointer<git_config>>(); final out = calloc<Pointer<git_config>>();
final error = libgit2.git_config_snapshot(out, config); libgit2.git_config_snapshot(out, config);
return out.value;
if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
} }
/// Get the config entry of a config variable. /// Get the config entry of a config variable.
@ -167,8 +136,6 @@ Pointer<git_config_entry> getEntry({
/// Set the value of a boolean config variable in the config file with the /// Set the value of a boolean config variable in the config file with the
/// highest level (usually the local one). /// highest level (usually the local one).
///
/// Throws a [LibGit2Error] if error occured.
void setBool({ void setBool({
required Pointer<git_config> configPointer, required Pointer<git_config> configPointer,
required String variable, required String variable,
@ -176,38 +143,24 @@ void setBool({
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final name = variable.toNativeUtf8().cast<Int8>();
final valueC = value ? 1 : 0; final valueC = value ? 1 : 0;
final error = libgit2.git_config_set_bool(configPointer, name, valueC); libgit2.git_config_set_bool(configPointer, name, valueC);
calloc.free(name); calloc.free(name);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
} }
/// Set the value of an integer config variable in the config file with the /// Set the value of an integer config variable in the config file with the
/// highest level (usually the local one). /// highest level (usually the local one).
///
/// Throws a [LibGit2Error] if error occured.
void setInt({ void setInt({
required Pointer<git_config> configPointer, required Pointer<git_config> configPointer,
required String variable, required String variable,
required int value, required int value,
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final name = variable.toNativeUtf8().cast<Int8>();
final error = libgit2.git_config_set_int64(configPointer, name, value); libgit2.git_config_set_int64(configPointer, name, value);
calloc.free(name); calloc.free(name);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
} }
/// Set the value of a string config variable in the config file with the /// Set the value of a string config variable in the config file with the
/// highest level (usually the local one). /// highest level (usually the local one).
///
/// Throws a [LibGit2Error] if error occured.
void setString({ void setString({
required Pointer<git_config> configPointer, required Pointer<git_config> configPointer,
required String variable, required String variable,
@ -215,14 +168,9 @@ void setString({
}) { }) {
final name = variable.toNativeUtf8().cast<Int8>(); final name = variable.toNativeUtf8().cast<Int8>();
final valueC = value.toNativeUtf8().cast<Int8>(); final valueC = value.toNativeUtf8().cast<Int8>();
final error = libgit2.git_config_set_string(configPointer, name, valueC); libgit2.git_config_set_string(configPointer, name, valueC);
calloc.free(name); calloc.free(name);
calloc.free(valueC); calloc.free(valueC);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
}
} }
/// Iterate over all the config variables. /// Iterate over all the config variables.
@ -330,13 +278,5 @@ void deleteMultivar({
calloc.free(regexpC); calloc.free(regexpC);
} }
/// Free a config iterator.
void iteratorFree(Pointer<git_config_iterator> iter) =>
libgit2.git_config_iterator_free(iter);
/// Free a config entry.
void entryFree(Pointer<git_config_entry> entry) =>
libgit2.git_config_entry_free(entry);
/// Free the configuration and its associated memory and files. /// Free the configuration and its associated memory and files.
void free(Pointer<git_config> cfg) => libgit2.git_config_free(cfg); void free(Pointer<git_config> cfg) => libgit2.git_config_free(cfg);

View file

@ -11,7 +11,7 @@ class Config with IterableMixin<ConfigEntry> {
/// Initializes a new instance of [Config] class from provided /// Initializes a new instance of [Config] class from provided
/// pointer to config object in memory. /// pointer to config object in memory.
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed to release allocated memory.
Config(this._configPointer); Config(this._configPointer);
/// Initializes a new instance of [Config] class from provided [path]. /// Initializes a new instance of [Config] class from provided [path].
@ -21,7 +21,9 @@ class Config with IterableMixin<ConfigEntry> {
/// [path] should point to single on-disk file; it's expected to be a native /// [path] should point to single on-disk file; it's expected to be a native
/// Git config file following the default Git config syntax (see man git-config). /// Git config file following the default Git config syntax (see man git-config).
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed to release allocated memory.
///
/// Throws an [Exception] if file not found at provided path.
Config.open([String? path]) { Config.open([String? path]) {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
@ -40,42 +42,39 @@ class Config with IterableMixin<ConfigEntry> {
/// ///
/// Opens the system configuration file. /// Opens the system configuration file.
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed to release allocated memory.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Config.system() { Config.system() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
final systemPath = bindings.findSystem(); _configPointer = bindings.open(bindings.findSystem());
_configPointer = bindings.open(systemPath);
} }
/// Initializes a new instance of [Config] class. /// Initializes a new instance of [Config] class.
/// ///
/// Opens the global configuration file. /// Opens the global configuration file.
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed to release allocated memory.
/// ///
/// Throws an error if file has not been found. /// Throws a [LibGit2Error] if error occured.
Config.global() { Config.global() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
final globalPath = bindings.findGlobal(); _configPointer = bindings.open(bindings.findGlobal());
_configPointer = bindings.open(globalPath);
} }
/// Initializes a new instance of [Config] class. /// Initializes a new instance of [Config] class.
/// ///
/// Opens the global XDG configuration file. /// Opens the global XDG configuration file.
/// ///
/// Should be freed with `free()` to release allocated memory. /// Should be freed to release allocated memory.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
Config.xdg() { Config.xdg() {
libgit2.git_libgit2_init(); libgit2.git_libgit2_init();
final xdgPath = bindings.findXdg(); _configPointer = bindings.open(bindings.findXdg());
_configPointer = bindings.open(xdgPath);
} }
/// Pointer to memory address for allocated config object. /// Pointer to memory address for allocated config object.
@ -86,8 +85,6 @@ class Config with IterableMixin<ConfigEntry> {
/// Create a snapshot of the current state of a configuration, which allows you to look /// Create a snapshot of the current state of a configuration, which allows you to look
/// into a consistent view of the configuration for looking up complex values /// into a consistent view of the configuration for looking up complex values
/// (e.g. a remote, submodule). /// (e.g. a remote, submodule).
///
/// Throws a [LibGit2Error] if error occured.
Config get snapshot => Config(bindings.snapshot(_configPointer)); Config get snapshot => Config(bindings.snapshot(_configPointer));
/// Returns the [ConfigEntry] of a [variable]. /// Returns the [ConfigEntry] of a [variable].
@ -206,7 +203,7 @@ class ConfigEntry {
} }
/// Releases memory allocated for config entry object. /// Releases memory allocated for config entry object.
void free() => bindings.entryFree(_configEntryPointer); void free() => calloc.free(_configEntryPointer);
@override @override
String toString() { String toString() {

View file

@ -15,6 +15,14 @@ void main() {
\turl = someurl \turl = someurl
'''; ''';
const expectedEntries = [
'core.repositoryformatversion',
'core.bare',
'core.gitproxy',
'core.gitproxy',
'remote.origin.url',
];
late Config config; late Config config;
setUp(() { setUp(() {
@ -28,16 +36,80 @@ void main() {
}); });
group('Config', () { group('Config', () {
test('opens file successfully with provided path', () { test('successfully opens file with provided path', () {
expect(config, isA<Config>()); expect(config, isA<Config>());
}); });
test(
'opens the global, XDG and system configuration files (if they are present) '
'if no path provided', () {
try {
final config = Config.open();
expect(config, isA<Config>());
config.free();
} catch (e) {
expect(() => Config.open(), throwsA(isA<LibGit2Error>()));
}
});
test('throws when trying to open non existing file', () {
expect(
() => Config.open('not.there'),
throwsA(
isA<Exception>().having(
(e) => e.toString(),
'error',
"Exception: File not found",
),
),
);
});
test('successfully opens system file or throws is there is none', () {
try {
final config = Config.system();
expect(config, isA<Config>());
config.free();
} catch (e) {
expect(() => Config.system(), throwsA(isA<LibGit2Error>()));
}
});
test('successfully opens global file or throws is there is none', () {
try {
final config = Config.global();
expect(config, isA<Config>());
config.free();
} catch (e) {
expect(() => Config.global(), throwsA(isA<LibGit2Error>()));
}
});
test('successfully opens xdg file or throws is there is none', () {
try {
final config = Config.xdg();
expect(config, isA<Config>());
config.free();
} catch (e) {
expect(() => Config.xdg(), throwsA(isA<LibGit2Error>()));
}
});
test('returns config snapshot', () {
final snapshot = config.snapshot;
expect(snapshot, isA<Config>());
snapshot.free();
});
test('returns config entries and their values', () { test('returns config entries and their values', () {
expect(config.length, 5); var i = 0;
expect(config.last.name, 'remote.origin.url'); for (final entry in config) {
expect(config.last.value, 'someurl'); expect(entry.name, expectedEntries[i]);
expect(config.last.includeDepth, 0); expect(entry.includeDepth, 0);
expect(config.last.level, GitConfigLevel.local); expect(entry.level, GitConfigLevel.local);
entry.free();
i++;
}
}); });
group('get value', () { group('get value', () {
@ -48,7 +120,13 @@ void main() {
test('throws when variable isn\'t found', () { test('throws when variable isn\'t found', () {
expect( expect(
() => config['not.there'], () => config['not.there'],
throwsA(isA<LibGit2Error>()), throwsA(
isA<LibGit2Error>().having(
(e) => e.toString(),
'error',
"config value 'not.there' was not found",
),
),
); );
}); });
}); });
@ -68,6 +146,19 @@ void main() {
config['remote.origin.url'] = 'updated'; config['remote.origin.url'] = 'updated';
expect(config['remote.origin.url'].value, 'updated'); expect(config['remote.origin.url'].value, 'updated');
}); });
test('throws when trying to set invalid value', () {
expect(
() => config['remote.origin.url'] = 0.1,
throwsA(
isA<ArgumentError>().having(
(e) => e.toString(),
'error',
'Invalid argument: "0.1 must be either bool, int or String"',
),
),
);
});
}); });
group('delete', () { group('delete', () {
@ -176,5 +267,11 @@ void main() {
expect(config.multivar(variable: 'core.gitproxy'), []); expect(config.multivar(variable: 'core.gitproxy'), []);
}); });
}); });
test('returns string representation of ConfigEntry object', () {
final entry = config.first;
expect(entry.toString(), contains('ConfigEntry{'));
entry.free();
});
}); });
} }