diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index f7001fa..abd575f 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v2 - uses: subosito/flutter-action@v1 with: - channel: stable + channel: master - name: Install dependencies run: flutter pub get @@ -47,7 +47,7 @@ jobs: - uses: actions/checkout@v2 - uses: subosito/flutter-action@v1 with: - channel: stable + channel: master - name: Install dependencies run: flutter pub get diff --git a/analysis_options.yaml b/analysis_options.yaml index ee80c33..f9d5ecc 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,8 +4,6 @@ analyzer: language: strict-casts: true strict-raw-types: true - strong-mode: - implicit-casts: false exclude: - lib/src/bindings/libgit2_bindings.dart @@ -35,15 +33,12 @@ linter: - directives_ordering - eol_at_end_of_file - join_return_with_assignment - - library_private_types_in_public_api - lines_longer_than_80_chars - literal_only_boolean_expressions - missing_whitespace_between_adjacent_strings - no_default_cases - noop_primitive_operations - - null_check_on_nullable_type_parameter - omit_local_variable_types - - one_member_abstracts - parameter_assignments - prefer_asserts_in_initializer_lists - prefer_asserts_with_message @@ -70,3 +65,4 @@ linter: - use_raw_strings - use_setters_to_change_properties - use_string_buffers + - use_super_parameters diff --git a/lib/src/annotated.dart b/lib/src/annotated.dart index b5958f9..68c3817 100644 --- a/lib/src/annotated.dart +++ b/lib/src/annotated.dart @@ -14,20 +14,17 @@ class AnnotatedCommit { /// It is preferable to use [AnnotatedCommit.fromReference] instead of this /// one, for commit to contain more information about how it was looked up. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. AnnotatedCommit.lookup({required Repository repo, required Oid oid}) { _annotatedCommitPointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _annotatedCommitPointer, detach: this); } /// Creates an annotated commit from the given [reference]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. AnnotatedCommit.fromReference({ required Repository repo, @@ -37,6 +34,7 @@ class AnnotatedCommit { repoPointer: repo.pointer, referencePointer: reference.pointer, ); + _finalizer.attach(this, _annotatedCommitPointer, detach: this); } /// Creates an annotated commit from a revision string. @@ -44,8 +42,6 @@ class AnnotatedCommit { /// See `man gitrevisions`, or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions /// for information on the syntax accepted. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. AnnotatedCommit.fromRevSpec({ required Repository repo, @@ -55,6 +51,7 @@ class AnnotatedCommit { repoPointer: repo.pointer, revspec: spec, ); + _finalizer.attach(this, _annotatedCommitPointer, detach: this); } /// Creates an annotated commit from the given fetch head data. @@ -67,8 +64,6 @@ class AnnotatedCommit { /// /// [oid] is the commit object id of the remote branch. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. AnnotatedCommit.fromFetchHead({ required Repository repo, @@ -82,6 +77,7 @@ class AnnotatedCommit { remoteUrl: remoteUrl, oid: oid.pointer, ); + _finalizer.attach(this, _annotatedCommitPointer, detach: this); } late final Pointer _annotatedCommitPointer; @@ -98,5 +94,14 @@ class AnnotatedCommit { String get refName => bindings.refName(_annotatedCommitPointer); /// Releases memory allocated for commit object. - void free() => bindings.free(_annotatedCommitPointer); + void free() { + bindings.free(_annotatedCommitPointer); + _finalizer.detach(this); + } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/bindings/config.dart b/lib/src/bindings/config.dart index e74e304..b6e3f8a 100644 --- a/lib/src/bindings/config.dart +++ b/lib/src/bindings/config.dart @@ -281,3 +281,11 @@ void deleteMultivar({ /// Free the configuration and its associated memory and files. void free(Pointer cfg) => libgit2.git_config_free(cfg); + +/// Free a config entry. +void freeEntry(Pointer entry) => + libgit2.git_config_entry_free(entry); + +/// Free a config iterator. +void freeIterator(Pointer iter) => + libgit2.git_config_iterator_free(iter); diff --git a/lib/src/bindings/odb.dart b/lib/src/bindings/odb.dart index 1948b41..77f45f4 100644 --- a/lib/src/bindings/odb.dart +++ b/lib/src/bindings/odb.dart @@ -78,8 +78,7 @@ int _forEachCb( Pointer oid, Pointer payload, ) { - final _oid = oid_bindings.copy(oid); - _objects.add(Oid(_oid)); + _objects.add(Oid(oid_bindings.copy(oid))); return 0; } diff --git a/lib/src/bindings/remote_callbacks.dart b/lib/src/bindings/remote_callbacks.dart index 030d849..47cfeb5 100644 --- a/lib/src/bindings/remote_callbacks.dart +++ b/lib/src/bindings/remote_callbacks.dart @@ -5,6 +5,7 @@ import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/credentials.dart' as credentials_bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; +import 'package:libgit2dart/src/bindings/remote.dart' as remote_bindings; import 'package:libgit2dart/src/util.dart'; class RemoteCallbacks { @@ -65,11 +66,9 @@ class RemoteCallbacks { return 0; } - /// A function matching the - /// `Remote Function(Repository repo, String name, String url)` signature to - /// override the remote creation and customization process during a clone - /// operation. - static Remote Function(Repository, String, String)? remoteFunction; + /// Values used to override the remote creation and customization process + /// during a clone operation. + static RemoteCallback? remoteCbData; /// A callback used to create the git remote, prior to its being used to /// perform the clone operation. @@ -80,19 +79,31 @@ class RemoteCallbacks { Pointer url, Pointer payload, ) { - remote[0] = remoteFunction!( - Repository(repo), - name.cast().toDartString(), - url.cast().toDartString(), - ).pointer; + late final Pointer remotePointer; + + if (remoteCbData!.fetch == null) { + remotePointer = remote_bindings.create( + repoPointer: repo, + name: remoteCbData!.name, + url: remoteCbData!.url, + ); + } else { + remotePointer = remote_bindings.createWithFetchSpec( + repoPointer: repo, + name: remoteCbData!.name, + url: remoteCbData!.url, + fetch: remoteCbData!.fetch!, + ); + } + + remote[0] = remotePointer; return 0; } - /// A function matching the `Repository Function(String path, bool bare)` - /// signature to override the repository creation and customization process + /// Values used to override the repository creation and customization process /// during a clone operation. - static Repository Function(String, bool)? repositoryFunction; + static RepositoryCallback? repositoryCbData; /// A callback used to create the new repository into which to clone. static int repositoryCb( @@ -101,11 +112,20 @@ class RemoteCallbacks { int bare, Pointer payload, ) { - repo[0] = repositoryFunction!( - path.cast().toDartString(), - bare == 1 || false, + final repoPointer = Repository.init( + path: repositoryCbData!.path, + bare: repositoryCbData!.bare, + flags: repositoryCbData!.flags, + mode: repositoryCbData!.mode, + workdirPath: repositoryCbData!.workdirPath, + description: repositoryCbData!.description, + templatePath: repositoryCbData!.templatePath, + initialHead: repositoryCbData!.initialHead, + originUrl: repositoryCbData!.originUrl, ).pointer; + repo[0] = repoPointer; + return 0; } @@ -236,8 +256,8 @@ class RemoteCallbacks { sidebandProgress = null; updateTips = null; pushUpdateReference = null; - remoteFunction = null; - repositoryFunction = null; + remoteCbData = null; + repositoryCbData = null; credentials = null; } } diff --git a/lib/src/bindings/repository.dart b/lib/src/bindings/repository.dart index 770f657..222b9ee 100644 --- a/lib/src/bindings/repository.dart +++ b/lib/src/bindings/repository.dart @@ -113,8 +113,9 @@ Pointer clone({ required String url, required String localPath, required bool bare, - Remote Function(Repository, String, String)? remote, - Repository Function(String, bool)? repository, + RemoteCallback? remoteCallback, + // Repository Function(String, bool)? repository, + RepositoryCallback? repositoryCallback, String? checkoutBranch, required Callbacks callbacks, }) { @@ -138,14 +139,14 @@ Pointer clone({ const except = -1; git_remote_create_cb remoteCb = nullptr; - if (remote != null) { - RemoteCallbacks.remoteFunction = remote; + if (remoteCallback != null) { + RemoteCallbacks.remoteCbData = remoteCallback; remoteCb = Pointer.fromFunction(RemoteCallbacks.remoteCb, except); } git_repository_create_cb repositoryCb = nullptr; - if (repository != null) { - RemoteCallbacks.repositoryFunction = repository; + if (repositoryCallback != null) { + RemoteCallbacks.repositoryCbData = repositoryCallback; repositoryCb = Pointer.fromFunction(RemoteCallbacks.repositoryCb, except); } diff --git a/lib/src/bindings/signature.dart b/lib/src/bindings/signature.dart index f0678df..2f234ff 100644 --- a/lib/src/bindings/signature.dart +++ b/lib/src/bindings/signature.dart @@ -64,5 +64,14 @@ Pointer defaultSignature(Pointer repo) { return out.value; } +/// Create a copy of an existing signature. +Pointer duplicate(Pointer sig) { + final out = calloc>(); + libgit2.git_signature_dup(out, sig); + final result = out.value; + calloc.free(out); + return result; +} + /// Free an existing signature. void free(Pointer sig) => libgit2.git_signature_free(sig); diff --git a/lib/src/bindings/tag.dart b/lib/src/bindings/tag.dart index d177a64..429a335 100644 --- a/lib/src/bindings/tag.dart +++ b/lib/src/bindings/tag.dart @@ -77,8 +77,10 @@ String name(Pointer tag) => libgit2.git_tag_name(tag).cast().toDartString(); /// Get the message of a tag. -String message(Pointer tag) => - libgit2.git_tag_message(tag).cast().toDartString(); +String message(Pointer tag) { + final result = libgit2.git_tag_message(tag); + return result == nullptr ? '' : result.cast().toDartString(); +} /// Get the tagger (author) of a tag. Pointer tagger(Pointer tag) => diff --git a/lib/src/blame.dart b/lib/src/blame.dart index ad0a60b..fddf426 100644 --- a/lib/src/blame.dart +++ b/lib/src/blame.dart @@ -34,8 +34,6 @@ class Blame with IterableMixin { /// [maxLine] is the last line in the file to blame. The default is the last /// line of the file. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Blame.file({ required Repository repo, @@ -57,6 +55,7 @@ class Blame with IterableMixin { minLine: minLine, maxLine: maxLine, ); + _finalizer.attach(this, _blamePointer, detach: this); } /// Returns blame data for a file that has been modified in memory. The @@ -74,6 +73,7 @@ class Blame with IterableMixin { reference: reference._blamePointer, buffer: buffer, ); + _finalizer.attach(this, _blamePointer, detach: this); } /// Pointer to memory address for allocated blame object. @@ -105,12 +105,21 @@ class Blame with IterableMixin { } /// Releases memory allocated for blame object. - void free() => bindings.free(_blamePointer); + void free() { + bindings.free(_blamePointer); + _finalizer.detach(this); + } @override Iterator get iterator => _BlameIterator(_blamePointer); } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class BlameHunk { /// Initializes a new instance of the [BlameHunk] class from /// provided pointer to blame hunk object in memory. diff --git a/lib/src/blob.dart b/lib/src/blob.dart index 0d78ed5..2c1a202 100644 --- a/lib/src/blob.dart +++ b/lib/src/blob.dart @@ -7,18 +7,17 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; class Blob { /// Initializes a new instance of [Blob] class from provided pointer to /// blob object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Blob(this._blobPointer); + Blob(this._blobPointer) { + _finalizer.attach(this, _blobPointer, detach: this); + } /// Lookups a blob object for provided [oid] in a [repo]sitory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Blob.lookup({required Repository repo, required Oid oid}) { _blobPointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _blobPointer, detach: this); } late final Pointer _blobPointer; @@ -80,8 +79,6 @@ class Blob { int get size => bindings.size(_blobPointer); /// Creates an in-memory copy of a blob. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Blob duplicate() => Blob(bindings.duplicate(_blobPointer)); /// Returns filtered content of a blob. @@ -114,10 +111,19 @@ class Blob { } /// Releases memory allocated for blob object. - void free() => bindings.free(_blobPointer); + void free() { + bindings.free(_blobPointer); + _finalizer.detach(this); + } @override String toString() { return 'Blob{oid: $oid, isBinary: $isBinary, size: $size}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/branch.dart b/lib/src/branch.dart index 21657e5..c296c8b 100644 --- a/lib/src/branch.dart +++ b/lib/src/branch.dart @@ -8,9 +8,9 @@ import 'package:libgit2dart/src/bindings/reference.dart' as reference_bindings; class Branch { /// Initializes a new instance of [Branch] class from provided pointer to /// branch object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Branch(this._branchPointer); + Branch(this._branchPointer) { + _finalizer.attach(this, _branchPointer, detach: this); + } /// Creates a new branch pointing at a [target] commit. /// @@ -24,8 +24,6 @@ class Branch { /// [target] is the commit to which this branch should point. This object must /// belong to the given [repo]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Branch.create({ required Repository repo, @@ -39,6 +37,7 @@ class Branch { targetPointer: target.pointer, force: force, ); + _finalizer.attach(this, _branchPointer, detach: this); } /// Lookups a branch by its [name] and [type] in a [repo]sitory. Lookups in @@ -49,8 +48,6 @@ class Branch { /// If branch [type] is [GitBranch.remote] you must include the remote name /// in the [name] (e.g. "origin/master"). /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Branch.lookup({ required Repository repo, @@ -62,6 +59,7 @@ class Branch { branchName: name, branchType: type.value, ); + _finalizer.attach(this, _branchPointer, detach: this); } late final Pointer _branchPointer; @@ -72,9 +70,6 @@ class Branch { /// Returns a list of branches that can be found in a [repo]sitory for /// provided [type]. Default is all branches (local and remote). /// - /// **IMPORTANT**: Branches must be freed manually when no longer needed to - /// prevent memory leak. - /// /// Throws a [LibGit2Error] if error occured. static List list({ required Repository repo, @@ -94,7 +89,6 @@ class Branch { static void delete({required Repository repo, required String name}) { final branch = Branch.lookup(repo: repo, name: name); bindings.delete(branch.pointer); - branch.free(); } /// Renames an existing local branch reference with provided [oldName]. @@ -117,8 +111,6 @@ class Branch { newBranchName: newName, force: force, ); - - branch.free(); } /// [Oid] pointed to by a branch. @@ -164,8 +156,6 @@ class Branch { /// Upstream [Reference] of a local branch. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Reference get upstream => Reference(bindings.getUpstream(_branchPointer)); @@ -222,7 +212,10 @@ class Branch { } /// Releases memory allocated for branch object. - void free() => bindings.free(_branchPointer); + void free() { + bindings.free(_branchPointer); + _finalizer.detach(this); + } @override String toString() { @@ -230,3 +223,9 @@ class Branch { 'isCheckedOut: $isCheckedOut}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/checkout.dart b/lib/src/checkout.dart index e3b8d8f..249a400 100644 --- a/lib/src/checkout.dart +++ b/lib/src/checkout.dart @@ -116,7 +116,6 @@ class Checkout { ); object_bindings.free(treeish); - ref.free(); } /// Updates files in the working tree to match the content of the tree diff --git a/lib/src/commit.dart b/lib/src/commit.dart index 73fcead..3bce907 100644 --- a/lib/src/commit.dart +++ b/lib/src/commit.dart @@ -8,18 +8,17 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; class Commit { /// Initializes a new instance of [Commit] class from provided pointer to /// commit object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Commit(this._commitPointer); + Commit(this._commitPointer) { + _finalizer.attach(this, _commitPointer, detach: this); + } /// Lookups commit object for provided [oid] in the [repo]sitory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Commit.lookup({required Repository repo, required Oid oid}) { _commitPointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _commitPointer, detach: this); } late final Pointer _commitPointer; @@ -194,8 +193,6 @@ class Commit { /// /// [mainline] is parent of the commit if it is a merge (i.e. 1, 2). /// - /// **IMPORTANT**: produced index should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Index revertTo({ required Commit commit, @@ -221,7 +218,7 @@ class Commit { /// leading newlines. String get message => bindings.message(_commitPointer); - /// Returns the short "summary" of the git commit message. + /// Short "summary" of the git commit message. /// /// The returned message is the summary of the commit, comprising the first /// paragraph of the message with whitespace trimmed and squashed. @@ -229,7 +226,7 @@ class Commit { /// Throws a [LibGit2Error] if error occured. String get summary => bindings.summary(_commitPointer); - /// Returns the long "body" of the commit message. + /// Long "body" of the commit message. /// /// The returned message is the body of the commit, comprising everything but /// the first paragraph of the message. Leading and trailing whitespaces are @@ -272,8 +269,6 @@ class Commit { /// Returns the specified parent of the commit at provided 0-based [position]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Commit parent(int position) { return Commit( @@ -303,8 +298,6 @@ class Commit { /// Passing 0 as the generation number returns another instance of the base /// commit itself. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Commit nthGenAncestor(int n) { return Commit(bindings.nthGenAncestor(commitPointer: _commitPointer, n: n)); @@ -323,12 +316,13 @@ class Commit { } /// Creates an in-memory copy of a commit. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Commit duplicate() => Commit(bindings.duplicate(_commitPointer)); /// Releases memory allocated for commit object. - void free() => bindings.free(_commitPointer); + void free() { + bindings.free(_commitPointer); + _finalizer.detach(this); + } @override String toString() { @@ -337,3 +331,9 @@ class Commit { ' author: $author}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/config.dart b/lib/src/config.dart index 1b84f4c..4b35c0d 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -11,9 +11,9 @@ import 'package:libgit2dart/src/util.dart'; class Config with IterableMixin { /// Initializes a new instance of [Config] class from provided /// pointer to config object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Config(this._configPointer); + Config(this._configPointer) { + _finalizer.attach(this, _configPointer, detach: this); + } /// Opens config file at provided [path]. /// @@ -23,8 +23,6 @@ class Config with IterableMixin { /// Git config file following the default Git config syntax (see /// `man git-config`). /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws an [Exception] if file not found at provided path. Config.open([String? path]) { libgit2.git_libgit2_init(); @@ -38,39 +36,44 @@ class Config with IterableMixin { throw Exception('File not found'); } } + + _finalizer.attach(this, _configPointer, detach: this); } /// Opens the system configuration file. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Config.system() { libgit2.git_libgit2_init(); _configPointer = bindings.open(bindings.findSystem()); + // coverage:ignore-start + _finalizer.attach(this, _configPointer, detach: this); + // coverage:ignore-end } /// Opens the global configuration file. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Config.global() { libgit2.git_libgit2_init(); _configPointer = bindings.open(bindings.findGlobal()); + // coverage:ignore-start + _finalizer.attach(this, _configPointer, detach: this); + // coverage:ignore-end } /// Opens the global XDG configuration file. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Config.xdg() { libgit2.git_libgit2_init(); _configPointer = bindings.open(bindings.findXdg()); + // coverage:ignore-start + _finalizer.attach(this, _configPointer, detach: this); + // coverage:ignore-end } /// Pointer to memory address for allocated config object. @@ -83,11 +86,24 @@ class Config with IterableMixin { /// Returns the [ConfigEntry] of a [variable]. ConfigEntry operator [](String variable) { - return ConfigEntry( - bindings.getEntry( - configPointer: _configPointer, - variable: variable, - ), + final entryPointer = bindings.getEntry( + configPointer: _configPointer, + variable: variable, + ); + final name = entryPointer.ref.name.cast().toDartString(); + final value = entryPointer.ref.value.cast().toDartString(); + final includeDepth = entryPointer.ref.include_depth; + final level = GitConfigLevel.values.singleWhere( + (e) => entryPointer.ref.level == e.value, + ); + + bindings.freeEntry(entryPointer); + + return ConfigEntry._( + name: name, + value: value, + includeDepth: includeDepth, + level: level, ); } @@ -167,36 +183,41 @@ class Config with IterableMixin { } /// Releases memory allocated for config object. - void free() => bindings.free(_configPointer); + void free() { + bindings.free(_configPointer); + _finalizer.detach(this); + } @override Iterator get iterator => _ConfigIterator(bindings.iterator(_configPointer)); } -class ConfigEntry { - /// Initializes a new instance of [ConfigEntry] class from provided - /// pointer to config entry object in memory. - const ConfigEntry(this._configEntryPointer); +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end - /// Pointer to memory address for allocated config entry object. - final Pointer _configEntryPointer; +class ConfigEntry { + ConfigEntry._({ + required this.name, + required this.value, + required this.includeDepth, + required this.level, + }); /// Name of the entry (normalised). - String get name => _configEntryPointer.ref.name.cast().toDartString(); + final String name; /// Value of the entry. - String get value => _configEntryPointer.ref.value.cast().toDartString(); + final String value; /// Depth of includes where this variable was found - int get includeDepth => _configEntryPointer.ref.include_depth; + final int includeDepth; /// Which config file this was found in. - GitConfigLevel get level { - return GitConfigLevel.values.singleWhere( - (e) => _configEntryPointer.ref.level == e.value, - ); - } + final GitConfigLevel level; @override String toString() { @@ -206,7 +227,9 @@ class ConfigEntry { } class _ConfigIterator implements Iterator { - _ConfigIterator(this._iteratorPointer); + _ConfigIterator(this._iteratorPointer) { + _iteratorFinalizer.attach(this, _iteratorPointer); + } /// Pointer to memory address for allocated config iterator. final Pointer _iteratorPointer; @@ -225,7 +248,20 @@ class _ConfigIterator implements Iterator { } else { error = libgit2.git_config_next(entry, _iteratorPointer); if (error != -31) { - _currentEntry = ConfigEntry(entry.value); + final name = entry.value.ref.name.cast().toDartString(); + final value = entry.value.ref.value.cast().toDartString(); + final includeDepth = entry.value.ref.include_depth; + final level = GitConfigLevel.values.singleWhere( + (e) => entry.value.ref.level == e.value, + ); + + _currentEntry = ConfigEntry._( + name: name, + value: value, + includeDepth: includeDepth, + level: level, + ); + return true; } else { return false; @@ -233,3 +269,9 @@ class _ConfigIterator implements Iterator { } } } + +// coverage:ignore-start +final _iteratorFinalizer = Finalizer>( + (pointer) => bindings.freeIterator(pointer), +); +// coverage:ignore-end diff --git a/lib/src/diff.dart b/lib/src/diff.dart index 848e65a..4841f21 100644 --- a/lib/src/diff.dart +++ b/lib/src/diff.dart @@ -10,9 +10,9 @@ import 'package:libgit2dart/src/util.dart'; class Diff { /// Initializes a new instance of [Diff] class from provided /// pointer to diff object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Diff(this._diffPointer); + Diff(this._diffPointer) { + _finalizer.attach(this, _diffPointer, detach: this); + } /// Creates a diff between the [repo]sitory [index] and the workdir directory. /// @@ -30,8 +30,6 @@ class Diff { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Diff.indexToWorkdir({ required Repository repo, @@ -47,6 +45,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff between a [tree] and [repo]sitory [index]. @@ -83,6 +82,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff between a [tree] and the working directory. @@ -124,6 +124,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff between a [tree] and the working directory using index @@ -145,8 +146,6 @@ class Diff { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Diff.treeToWorkdirWithIndex({ required Repository repo, @@ -162,6 +161,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff with the difference between two [Tree] objects. @@ -204,6 +204,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Creates a diff with the difference between two [Index] objects. @@ -239,6 +240,7 @@ class Diff { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _diffPointer, detach: this); } /// Reads the [content]s of a git patch file into a git diff object. @@ -255,6 +257,7 @@ class Diff { Diff.parse(String content) { libgit2.git_libgit2_init(); _diffPointer = bindings.parse(content); + _finalizer.attach(this, _diffPointer, detach: this); } late final Pointer _diffPointer; @@ -441,7 +444,10 @@ class Diff { Oid get patchOid => Oid(bindings.patchOid(_diffPointer)); /// Releases memory allocated for diff object. - void free() => bindings.free(_diffPointer); + void free() { + bindings.free(_diffPointer); + _finalizer.detach(this); + } @override String toString() { @@ -449,6 +455,12 @@ class Diff { } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class DiffDelta { /// Initializes a new instance of [DiffDelta] class from provided /// pointer to diff delta object in memory. @@ -543,9 +555,9 @@ class DiffFile { class DiffStats { /// Initializes a new instance of [DiffStats] class from provided /// pointer to diff stats object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - const DiffStats(this._diffStatsPointer); + DiffStats(this._diffStatsPointer) { + _statsFinalizer.attach(this, _diffStatsPointer, detach: this); + } /// Pointer to memory address for allocated diff delta object. final Pointer _diffStatsPointer; @@ -573,7 +585,10 @@ class DiffStats { } /// Releases memory allocated for diff stats object. - void free() => bindings.statsFree(_diffStatsPointer); + void free() { + bindings.statsFree(_diffStatsPointer); + _statsFinalizer.detach(this); + } @override String toString() { @@ -582,6 +597,12 @@ class DiffStats { } } +// coverage:ignore-start +final _statsFinalizer = Finalizer>( + (pointer) => bindings.statsFree(pointer), +); +// coverage:ignore-end + class DiffHunk { /// Initializes a new instance of [DiffHunk] class from provided /// pointers to patch object and diff hunk object in memory and number of diff --git a/lib/src/index.dart b/lib/src/index.dart index dd94306..99f60e6 100644 --- a/lib/src/index.dart +++ b/lib/src/index.dart @@ -9,19 +9,20 @@ import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; class Index with IterableMixin { /// Initializes a new instance of [Index] class from provided /// pointer to index object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - const Index(this._indexPointer); + Index(this._indexPointer) { + _finalizer.attach(this, _indexPointer, detach: this); + } /// Creates an in-memory index object. /// /// This index object cannot be read/written to the filesystem, but may be /// used to perform in-memory index operations. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Index.newInMemory() : _indexPointer = bindings.newInMemory(); + Index.newInMemory() { + _indexPointer = bindings.newInMemory(); + _finalizer.attach(this, _indexPointer, detach: this); + } - final Pointer _indexPointer; + late final Pointer _indexPointer; /// Pointer to memory address for allocated index object. Pointer get pointer => _indexPointer; @@ -300,7 +301,10 @@ class Index with IterableMixin { bindings.removeAll(indexPointer: _indexPointer, pathspec: path); /// Releases memory allocated for index object. - void free() => bindings.free(_indexPointer); + void free() { + bindings.free(_indexPointer); + _finalizer.detach(this); + } @override String toString() => 'Index{hasConflicts: $hasConflicts}'; @@ -309,6 +313,12 @@ class Index with IterableMixin { Iterator get iterator => _IndexIterator(_indexPointer); } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class IndexEntry { /// Initializes a new instance of [IndexEntry] class. const IndexEntry(this._indexEntryPointer); diff --git a/lib/src/libgit2.dart b/lib/src/libgit2.dart index cbd611b..f0441f4 100644 --- a/lib/src/libgit2.dart +++ b/lib/src/libgit2.dart @@ -8,7 +8,7 @@ import 'package:libgit2dart/src/util.dart'; class Libgit2 { Libgit2._(); // coverage:ignore-line - /// Returns libgit2 version number. + /// Libgit2 version number. static String get version { libgit2.git_libgit2_init(); @@ -26,7 +26,7 @@ class Libgit2 { return version; } - /// Returns list of options libgit2 was compiled with. + /// Options libgit2 was compiled with. static Set get features { libgit2.git_libgit2_init(); final featuresInt = libgit2.git_libgit2_features(); @@ -35,15 +35,19 @@ class Libgit2 { .toSet(); } - /// Returns owner validation setting for repository directories. + /// Owner validation setting for repository directories. static bool get ownerValidation { libgit2.git_libgit2_init(); + final out = calloc(); libgit2.git_libgit2_opts( git_libgit2_opt_t.GIT_OPT_GET_OWNER_VALIDATION, out, ); - return out.value == 1 || false; + final result = out.value; + calloc.free(out); + + return result == 1 || false; } /// Sets owner validation setting for repository directories. diff --git a/lib/src/mailmap.dart b/lib/src/mailmap.dart index c0e51d9..6abd47a 100644 --- a/lib/src/mailmap.dart +++ b/lib/src/mailmap.dart @@ -9,21 +9,19 @@ class Mailmap { /// /// This object is empty, so you'll have to add a mailmap file before you can /// do anything with it. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Mailmap.empty() { libgit2.git_libgit2_init(); _mailmapPointer = bindings.init(); + _finalizer.attach(this, _mailmapPointer, detach: this); } /// Initializes a new instance of [Mailmap] class from provided buffer. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Mailmap.fromBuffer(String buffer) { libgit2.git_libgit2_init(); _mailmapPointer = bindings.fromBuffer(buffer); + _finalizer.attach(this, _mailmapPointer, detach: this); } /// Initializes a new instance of [Mailmap] class from a [repo]sitory, loading @@ -34,14 +32,13 @@ class Mailmap { /// 1. `.mailmap` in the root of the repository's working directory, if /// present. /// 2. The blob object identified by the `mailmap.blob` config entry, if set. - /// NOTE: `mailmap.blob` defaults to `HEAD:.mailmap` in bare repositories + /// NOTE: `mailmap.blob` defaults to `HEAD:.mailmap` in bare repositories. /// 3. The path in the `mailmap.file` config entry, if set. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Mailmap.fromRepository(Repository repo) { _mailmapPointer = bindings.fromRepository(repo.pointer); + _finalizer.attach(this, _mailmapPointer, detach: this); } /// Pointer to memory address for allocated mailmap object. @@ -94,5 +91,14 @@ class Mailmap { } /// Releases memory allocated for mailmap object. - void free() => bindings.free(_mailmapPointer); + void free() { + bindings.free(_mailmapPointer); + _finalizer.detach(this); + } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/merge.dart b/lib/src/merge.dart index 06d13fb..66d9cda 100644 --- a/lib/src/merge.dart +++ b/lib/src/merge.dart @@ -74,9 +74,6 @@ class Merge { (e) => analysisInt[1] == e.value, ); - head.free(); - ref.free(); - return [analysisSet, mergePreference]; } @@ -140,8 +137,6 @@ class Merge { /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to /// [GitMergeFileFlag.defaults]. /// - /// **IMPORTANT**: returned index should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. static Index commits({ required Repository repo, @@ -188,8 +183,6 @@ class Merge { /// [fileFlags] is a combination of [GitMergeFileFlag] flags. Defaults to /// [GitMergeFileFlag.defaults]. /// - /// **IMPORTANT**: returned index should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. static Index trees({ required Repository repo, diff --git a/lib/src/note.dart b/lib/src/note.dart index a8e0753..0461c96 100644 --- a/lib/src/note.dart +++ b/lib/src/note.dart @@ -6,7 +6,9 @@ import 'package:libgit2dart/src/bindings/note.dart' as bindings; class Note { /// Initializes a new instance of the [Note] class from provided /// pointer to note and annotatedOid objects in memory. - Note(this._notePointer, this._annotatedOidPointer); + Note(this._notePointer, this._annotatedOidPointer) { + _finalizer.attach(this, _notePointer, detach: this); + } /// Lookups the note for an [annotatedOid]. /// @@ -16,8 +18,6 @@ class Note { /// /// [notesRef] is the canonical name of the reference to use. Defaults to "refs/notes/commits". /// - /// **IMPORTANT**: Notes must be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Note.lookup({ required Repository repo, @@ -30,6 +30,7 @@ class Note { notesRef: notesRef, ); _annotatedOidPointer = annotatedOid.pointer; + _finalizer.attach(this, _notePointer, detach: this); } /// Pointer to memory address for allocated note object. @@ -107,8 +108,6 @@ class Note { /// Returns list of notes for [repo]sitory. /// - /// **IMPORTANT**: Notes must be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. static List list(Repository repo) { final notesPointers = bindings.list(repo.pointer); @@ -132,10 +131,19 @@ class Note { Oid get annotatedOid => Oid(_annotatedOidPointer); /// Releases memory allocated for note object. - void free() => bindings.free(_notePointer); + void free() { + bindings.free(_notePointer); + _finalizer.detach(this); + } @override String toString() { return 'Note{oid: $oid, message: $message, annotatedOid: $annotatedOid}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/odb.dart b/lib/src/odb.dart index 53b5466..a141e23 100644 --- a/lib/src/odb.dart +++ b/lib/src/odb.dart @@ -7,20 +7,19 @@ import 'package:libgit2dart/src/util.dart'; class Odb { /// Initializes a new instance of [Odb] class from provided /// pointer to Odb object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Odb(this._odbPointer); + Odb(this._odbPointer) { + _finalizer.attach(this, _odbPointer, detach: this); + } /// Creates a new object database with no backends. /// /// Before the ODB can be used for read/writing, a custom database backend must be /// manually added. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Odb.create() { libgit2.git_libgit2_init(); _odbPointer = bindings.create(); + _finalizer.attach(this, _odbPointer, detach: this); } late final Pointer _odbPointer; @@ -59,9 +58,6 @@ class Odb { /// This method queries all available ODB backends trying to read the given /// [oid]. /// - /// **IMPORTANT**: Returned object should be freed to release allocated - /// memory. - /// /// Throws a [LibGit2Error] if error occured. OdbObject read(Oid oid) { return OdbObject( @@ -97,13 +93,24 @@ class Odb { } /// Releases memory allocated for odb object. - void free() => bindings.free(_odbPointer); + void free() { + bindings.free(_odbPointer); + _finalizer.detach(this); + } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class OdbObject { /// Initializes a new instance of the [OdbObject] class from /// provided pointer to odbObject object in memory. - const OdbObject(this._odbObjectPointer); + OdbObject(this._odbObjectPointer) { + _objectfinalizer.attach(this, _odbObjectPointer, detach: this); + } /// Pointer to memory address for allocated odbObject object. final Pointer _odbObjectPointer; @@ -124,10 +131,19 @@ class OdbObject { int get size => bindings.objectSize(_odbObjectPointer); /// Releases memory allocated for odbObject object. - void free() => bindings.objectFree(_odbObjectPointer); + void free() { + bindings.objectFree(_odbObjectPointer); + _objectfinalizer.detach(this); + } @override String toString() { return 'OdbObject{oid: $oid, type: $type, size: $size}'; } } + +// coverage:ignore-start +final _objectfinalizer = Finalizer>( + (pointer) => bindings.objectFree(pointer), +); +// coverage:ignore-end diff --git a/lib/src/oid.dart b/lib/src/oid.dart index eeb4a62..c9f8efb 100644 --- a/lib/src/oid.dart +++ b/lib/src/oid.dart @@ -25,13 +25,11 @@ class Oid { if (sha.length == 40) { _oidPointer = bindings.fromSHA(sha); } else { - final odb = repo.odb; _oidPointer = odb_bindings.existsPrefix( - odbPointer: odb.pointer, + odbPointer: repo.odb.pointer, shortOidPointer: bindings.fromStrN(sha), length: sha.length, ); - odb.free(); } } else { throw ArgumentError.value('$sha is not a valid sha hex string'); diff --git a/lib/src/packbuilder.dart b/lib/src/packbuilder.dart index 40fdbb7..e585a16 100644 --- a/lib/src/packbuilder.dart +++ b/lib/src/packbuilder.dart @@ -6,11 +6,10 @@ import 'package:libgit2dart/src/bindings/packbuilder.dart' as bindings; class PackBuilder { /// Initializes a new instance of [PackBuilder] class. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. PackBuilder(Repository repo) { _packbuilderPointer = bindings.init(repo.pointer); + _finalizer.attach(this, _packbuilderPointer, detach: this); } /// Pointer to memory address for allocated packbuilder object. @@ -108,10 +107,19 @@ class PackBuilder { } /// Releases memory allocated for packbuilder object. - void free() => bindings.free(_packbuilderPointer); + void free() { + bindings.free(_packbuilderPointer); + _finalizer.detach(this); + } @override String toString() { return 'PackBuilder{length: $length, writtenLength: $writtenLength}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/patch.dart b/lib/src/patch.dart index f5f6005..1660aa9 100644 --- a/lib/src/patch.dart +++ b/lib/src/patch.dart @@ -8,9 +8,9 @@ import 'package:libgit2dart/src/util.dart'; class Patch { /// Initializes a new instance of [Patch] class from provided /// pointer to patch object in memory and pointers to old and new blobs/buffers. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Patch(this._patchPointer); + Patch(this._patchPointer) { + _finalizer.attach(this, _patchPointer, detach: this); + } /// Directly generates a [Patch] from the difference between two blobs. /// @@ -30,8 +30,6 @@ class Patch { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Patch.fromBlobs({ required Blob? oldBlob, @@ -51,6 +49,7 @@ class Patch { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _patchPointer, detach: this); } /// Directly generates a [Patch] from the difference between the blob and a @@ -72,8 +71,6 @@ class Patch { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Patch.fromBlobAndBuffer({ required Blob? blob, @@ -93,6 +90,7 @@ class Patch { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _patchPointer, detach: this); } /// Directly generates a [Patch] from the difference between two buffers @@ -113,8 +111,6 @@ class Patch { /// [interhunkLines] is the maximum number of unchanged lines between hunk /// boundaries before the hunks will be merged into one. Defaults to 0. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Patch.fromBuffers({ required String? oldBuffer, @@ -136,6 +132,7 @@ class Patch { contextLines: contextLines, interhunkLines: interhunkLines, ); + _finalizer.attach(this, _patchPointer, detach: this); } /// Creates a patch for an entry in the diff list. @@ -144,11 +141,10 @@ class Patch { /// /// [index] is the position of an entry in diff list. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Patch.fromDiff({required Diff diff, required int index}) { _patchPointer = bindings.fromDiff(diffPointer: diff.pointer, index: index); + _finalizer.attach(this, _patchPointer, detach: this); } late final Pointer _patchPointer; @@ -217,12 +213,21 @@ class Patch { } /// Releases memory allocated for patch object. - void free() => bindings.free(_patchPointer); + void free() { + bindings.free(_patchPointer); + _finalizer.detach(this); + } @override String toString() => 'Patch{size: ${size()}, delta: $delta}'; } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + /// Line counts of each type in a patch. class PatchStats { const PatchStats({ diff --git a/lib/src/rebase.dart b/lib/src/rebase.dart index f9fda60..d917849 100644 --- a/lib/src/rebase.dart +++ b/lib/src/rebase.dart @@ -17,8 +17,6 @@ class Rebase { /// [onto] is the branch to rebase onto, default is to rebase onto the given /// [upstream]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Rebase.init({ required Repository repo, @@ -32,16 +30,16 @@ class Rebase { upstreamPointer: upstream?.pointer, ontoPointer: onto?.pointer, ); + _finalizer.attach(this, _rebasePointer, detach: this); } /// Opens an existing rebase that was previously started by either an /// invocation of [Rebase.init] or by another client. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Rebase.open(Repository repo) { _rebasePointer = bindings.open(repo.pointer); + _finalizer.attach(this, _rebasePointer, detach: this); } /// Pointer to memory address for allocated rebase object. @@ -138,9 +136,18 @@ class Rebase { void abort() => bindings.abort(_rebasePointer); /// Releases memory allocated for rebase object. - void free() => bindings.free(_rebasePointer); + void free() { + bindings.free(_rebasePointer); + _finalizer.detach(this); + } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class RebaseOperation { /// Initializes a new instance of the [RebaseOperation] class from /// provided pointer to rebase operation object in memory. diff --git a/lib/src/reference.dart b/lib/src/reference.dart index c67b721..2a5cf73 100644 --- a/lib/src/reference.dart +++ b/lib/src/reference.dart @@ -10,17 +10,14 @@ import 'package:libgit2dart/src/bindings/repository.dart' class Reference { /// Initializes a new instance of the [Reference] class. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Reference(this._refPointer); + Reference(this._refPointer) { + _finalizer.attach(this, _refPointer, detach: this); + } /// Creates a new reference for provided [target]. /// /// The reference will be created in the [repo]sitory and written to the disk. /// - /// **IMPORTANT**: The generated [Reference] object should be freed to release - /// allocated memory. - /// /// Valid reference [name]s must follow one of two patterns: /// - Top-level names must contain only capital letters and underscores, and /// must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). @@ -65,17 +62,17 @@ class Reference { '$target must be either Oid or String reference name', ); } + _finalizer.attach(this, _refPointer, detach: this); } /// Lookups reference [name] in a [repo]sitory. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// The [name] will be checked for validity. /// /// Throws a [LibGit2Error] if error occured. Reference.lookup({required Repository repo, required String name}) { _refPointer = bindings.lookup(repoPointer: repo.pointer, name: name); + _finalizer.attach(this, _refPointer, detach: this); } late Pointer _refPointer; @@ -89,7 +86,6 @@ class Reference { static void delete({required Repository repo, required String name}) { final ref = Reference.lookup(repo: repo, name: name); bindings.delete(ref.pointer); - ref.free(); } /// Renames an existing reference with provided [oldName]. @@ -120,7 +116,6 @@ class Reference { force: force, logMessage: logMessage, ); - ref.free(); } /// List of all the references names that can be found in a [repo]sitory. @@ -153,8 +148,6 @@ class Reference { } /// Creates a copy of an existing reference. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Reference duplicate() => Reference(bindings.duplicate(_refPointer)); /// Type of the reference. @@ -192,7 +185,6 @@ class Reference { oidPointer: target.pointer, logMessage: logMessage, ); - free(); _refPointer = newPointer; } else if (target is String) { final newPointer = bindings.setTargetSymbolic( @@ -200,7 +192,6 @@ class Reference { target: target, logMessage: logMessage, ); - free(); _refPointer = newPointer; } else { throw ArgumentError.value( @@ -261,8 +252,6 @@ class Reference { } /// [RefLog] object. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. RefLog get log => RefLog(this); /// Whether reference is a local branch. @@ -292,7 +281,10 @@ class Reference { bool notEquals(Reference other) => !equals(other); /// Releases memory allocated for reference object. - void free() => bindings.free(_refPointer); + void free() { + bindings.free(_refPointer); + _finalizer.detach(this); + } @override String toString() { @@ -301,3 +293,9 @@ class Reference { 'isTag: $isTag}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/reflog.dart b/lib/src/reflog.dart index 89a50a2..d58dfbd 100644 --- a/lib/src/reflog.dart +++ b/lib/src/reflog.dart @@ -6,13 +6,12 @@ import 'package:libgit2dart/src/bindings/reflog.dart' as bindings; class RefLog with IterableMixin { /// Initializes a new instance of [RefLog] class from provided [Reference]. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. RefLog(Reference ref) { _reflogPointer = bindings.read( repoPointer: ref.owner.pointer, name: ref.name, ); + _finalizer.attach(this, _reflogPointer, detach: this); } /// Pointer to memory address for allocated reflog object. @@ -89,12 +88,21 @@ class RefLog with IterableMixin { void write() => bindings.write(_reflogPointer); /// Releases memory allocated for reflog object. - void free() => bindings.free(_reflogPointer); + void free() { + bindings.free(_reflogPointer); + _finalizer.detach(this); + } @override Iterator get iterator => _RefLogIterator(_reflogPointer); } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class RefLogEntry { /// Initializes a new instance of [RefLogEntry] class from provided /// pointer to RefLogEntry object in memory. diff --git a/lib/src/remote.dart b/lib/src/remote.dart index 26ec809..70238d1 100644 --- a/lib/src/remote.dart +++ b/lib/src/remote.dart @@ -8,18 +8,15 @@ class Remote { /// /// The [name] will be checked for validity. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Remote.lookup({required Repository repo, required String name}) { _remotePointer = bindings.lookup(repoPointer: repo.pointer, name: name); + _finalizer.attach(this, _remotePointer, detach: this); } /// Adds remote with provided [name] and [url] to the [repo]sitory's /// configuration with the default [fetch] refspec if none provided. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Remote.create({ required Repository repo, @@ -41,12 +38,11 @@ class Remote { fetch: fetch, ); } + _finalizer.attach(this, _remotePointer, detach: this); } - late final Pointer _remotePointer; - /// Pointer to memory address for allocated remote object. - Pointer get pointer => _remotePointer; + late final Pointer _remotePointer; /// Deletes an existing persisted remote with provided [name]. /// @@ -298,7 +294,10 @@ class Remote { } /// Releases memory allocated for remote object. - void free() => bindings.free(_remotePointer); + void free() { + bindings.free(_remotePointer); + _finalizer.detach(this); + } @override String toString() { @@ -307,6 +306,12 @@ class Remote { } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + /// Provides callers information about the progress of indexing a packfile, /// either directly or part of a fetch or clone that downloads a packfile. class TransferProgress { @@ -346,3 +351,21 @@ class TransferProgress { 'indexedDeltas: $indexedDeltas, receivedBytes: $receivedBytes}'; } } + +class RemoteCallback { + /// Values used to override the remote creation and customization process + /// during a repository clone operation. + /// + /// Remote will have provided [name] and [url] with the default [fetch] + /// refspec if none provided. + const RemoteCallback({required this.name, required this.url, this.fetch}); + + /// Remote's name. + final String name; + + /// Remote's url. + final String url; + + /// Remote's fetch refspec. + final String? fetch; +} diff --git a/lib/src/repository.dart b/lib/src/repository.dart index 0a47078..2822bc4 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -111,13 +111,11 @@ class Repository { /// /// [bare] whether cloned repo should be bare. /// - /// [remote] is the callback function with - /// `Remote Function(Repository repo, String name, String url)` signature. - /// The [Remote] it returns will be used instead of default one. + /// [remoteCallback] is the [RemoteCallback] object values that will be used + /// in creation and customization process of remote instead of default ones. /// - /// [repository] is the callback function matching the - /// `Repository Function(String path, bool bare)` signature. The [Repository] - /// it returns will be used instead of creating a new one. + /// [repositoryCallback] is the [RepositoryCallback] object values that will + /// be used in creation and customization process of repository. /// /// [checkoutBranch] is the name of the branch to checkout after the clone. /// Defaults to using the remote's default branch. @@ -132,8 +130,8 @@ class Repository { required String url, required String localPath, bool bare = false, - Remote Function(Repository, String, String)? remote, - Repository Function(String, bool)? repository, + RemoteCallback? remoteCallback, + RepositoryCallback? repositoryCallback, String? checkoutBranch, Callbacks callbacks = const Callbacks(), }) { @@ -143,8 +141,8 @@ class Repository { url: url, localPath: localPath, bare: bare, - remote: remote, - repository: repository, + remoteCallback: remoteCallback, + repositoryCallback: repositoryCallback, checkoutBranch: checkoutBranch, callbacks: callbacks, ); @@ -375,8 +373,6 @@ class Repository { /// If a configuration file has not been set, the default config set for the /// repository will be returned, including global and system configurations /// (if they are available). - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Config get config => Config(bindings.config(_repoPointer)); /// Snapshot of the repository's configuration. @@ -386,24 +382,16 @@ class Repository { /// /// The contents of this snapshot will not change, even if the underlying /// config files are modified. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Config get configSnapshot => Config(bindings.configSnapshot(_repoPointer)); /// Repository's head. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Reference get head => Reference(bindings.head(_repoPointer)); /// Index file for this repository. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Index get index => Index(bindings.index(_repoPointer)); /// ODB for this repository. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Odb get odb => Odb(bindings.odb(_repoPointer)); @@ -419,23 +407,17 @@ class Repository { /// List of all branches that can be found in a repository. /// - /// **IMPORTANT**: Branches should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. List get branches => Branch.list(repo: this); /// List of local branches that can be found in a repository. /// - /// **IMPORTANT**: Branches should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. List get branchesLocal => Branch.list(repo: this, type: GitBranch.local); /// List of remote branches that can be found in a repository. /// - /// **IMPORTANT**: Branches should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. List get branchesRemote => Branch.list(repo: this, type: GitBranch.remote); @@ -465,8 +447,6 @@ class Repository { /// /// If [sorting] isn't provided default will be used (reverse chronological /// order, like in git). - /// - /// **IMPORTANT**: Commits should be freed to release allocated memory. List log({ required Oid oid, Set sorting = const {GitSort.none}, @@ -477,8 +457,6 @@ class Repository { walker.push(oid); final result = walker.walk(); - walker.free(); - return result; } @@ -737,6 +715,7 @@ class Repository { } } + // ignore: no_leading_underscores_for_local_identifiers final _packDelegate = packDelegate ?? packAll; final packbuilder = PackBuilder(this); @@ -745,10 +724,79 @@ class Repository { } _packDelegate(packbuilder); packbuilder.write(path); - final result = packbuilder.writtenLength; - packbuilder.free(); - - return result; + return packbuilder.writtenLength; } } + +class RepositoryCallback { + /// Values used to override the repository creation and customization process + /// during a clone operation. + /// + /// [path] is the path to the repository. + /// + /// [bare] whether new repository should be bare. + /// + /// [flags] is a combination of [GitRepositoryInit] flags. Defaults to + /// [GitRepositoryInit.mkdir]. + /// + /// [mode] is the permissions for the folder. Default to 0 (permissions + /// configured by umask). + /// + /// [workdirPath] is the path to the working directory. Can be null for + /// default path. + /// + /// [description] if set will be used to initialize the "description" file in + /// the repository, instead of using the template content. + /// + /// [templatePath] is the the path to use for the template directory if + /// [GitRepositoryInit.externalTemplate] is set. Defaults to the config or + /// default directory options. + /// + /// [initialHead] is the name of the head to point HEAD at. If null, then + /// this will be treated as "master" and the HEAD ref will be set to + /// "refs/heads/master". If this begins with "refs/" it will be used + /// verbatim, otherwise "refs/heads/" will be prefixed. + /// + /// [originUrl] if set, then after the rest of the repository initialization + /// is completed, an "origin" remote will be added pointing to this URL. + const RepositoryCallback({ + required this.path, + this.bare = false, + this.flags = const {GitRepositoryInit.mkpath}, + this.mode = 0, + this.workdirPath, + this.description, + this.templatePath, + this.initialHead, + this.originUrl, + }); + + /// Path to the repository. + final String path; + + /// Whether repository is bare. + final bool bare; + + /// Combination of [GitRepositoryInit] flags. + final Set flags; + + /// Permissions for the repository folder. + final int mode; + + /// Path to the working directory. + final String? workdirPath; + + /// Description used to initialize the "description" file in the repository. + final String? description; + + /// Path used for the template directory. + final String? templatePath; + + /// Name of the head HEAD points at. + final String? initialHead; + + /// "origin" remote URL that will be added after the rest of the repository + /// initialization is completed. + final String? originUrl; +} diff --git a/lib/src/revparse.dart b/lib/src/revparse.dart index 0e2d617..713840f 100644 --- a/lib/src/revparse.dart +++ b/lib/src/revparse.dart @@ -15,8 +15,6 @@ class RevParse { /// point to an intermediate reference. When such expressions are being /// passed in, reference_out will be valued as well. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. RevParse.ext({required Repository repo, required String spec}) { final pointers = bindings.revParseExt( @@ -51,8 +49,6 @@ class RevParse { /// final tag = RevParse.single(repo: repo, spec: 'v1.0') as Tag; /// ``` /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. static Object single({required Repository repo, required String spec}) { final object = bindings.revParseSingle( @@ -102,13 +98,9 @@ class RevSpec { final Pointer _revSpecPointer; /// Left element of the revspec. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Commit get from => Commit(_revSpecPointer.ref.from.cast()); /// Right element of the revspec. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Commit? get to { return _revSpecPointer.ref.to == nullptr ? null diff --git a/lib/src/revwalk.dart b/lib/src/revwalk.dart index 29faf68..2c0f23a 100644 --- a/lib/src/revwalk.dart +++ b/lib/src/revwalk.dart @@ -5,10 +5,9 @@ import 'package:libgit2dart/src/bindings/revwalk.dart' as bindings; class RevWalk { /// Initializes a new instance of the [RevWalk] class. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. RevWalk(Repository repo) { _revWalkPointer = bindings.create(repo.pointer); + _finalizer.attach(this, _revWalkPointer, detach: this); } late final Pointer _revWalkPointer; @@ -150,5 +149,14 @@ class RevWalk { void simplifyFirstParent() => bindings.simplifyFirstParent(_revWalkPointer); /// Releases memory allocated for [RevWalk] object. - void free() => bindings.free(_revWalkPointer); + void free() { + bindings.free(_revWalkPointer); + _finalizer.detach(this); + } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/signature.dart b/lib/src/signature.dart index 4c821a7..2fe12e1 100644 --- a/lib/src/signature.dart +++ b/lib/src/signature.dart @@ -10,17 +10,16 @@ import 'package:meta/meta.dart'; class Signature { /// Initializes a new instance of [Signature] class from provided pointer to /// signature object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Signature(this._signaturePointer); + Signature(Pointer pointer) { + _signaturePointer = bindings.duplicate(pointer); + _finalizer.attach(this, _signaturePointer, detach: this); + } /// Creates new [Signature] from provided [name], [email], and optional [time] /// in seconds from epoch and [offset] in minutes. /// /// If [time] isn't provided [Signature] will be created with a timestamp of /// 'now'. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Signature.create({ required String name, required String email, @@ -39,6 +38,7 @@ class Signature { offset: offset, ); } + _finalizer.attach(this, _signaturePointer, detach: this); } /// Creates a new action signature with default user and now timestamp. @@ -48,6 +48,7 @@ class Signature { /// on that information. Signature.defaultSignature(Repository repo) { _signaturePointer = bindings.defaultSignature(repo.pointer); + _finalizer.attach(this, _signaturePointer, detach: this); } late final Pointer _signaturePointer; @@ -79,7 +80,10 @@ class Signature { } /// Releases memory allocated for signature object. - void free() => bindings.free(_signaturePointer); + void free() { + bindings.free(_signaturePointer); + _finalizer.detach(this); + } @override // coverage:ignore-line int get hashCode => @@ -91,3 +95,9 @@ class Signature { 'offset: $offset}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/submodule.dart b/lib/src/submodule.dart index c470fde..84d0679 100644 --- a/lib/src/submodule.dart +++ b/lib/src/submodule.dart @@ -7,14 +7,13 @@ class Submodule { /// Lookups submodule information by [name] or path (they are usually the /// same). /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Submodule.lookup({required Repository repo, required String name}) { _submodulePointer = bindings.lookup( repoPointer: repo.pointer, name: name, ); + _finalizer.attach(this, _submodulePointer, detach: this); } /// Adds a submodule to the index. @@ -29,8 +28,6 @@ class Submodule { /// [callbacks] is the combination of callback functions from [Callbacks] /// object. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Submodule.add({ required Repository repo, @@ -49,6 +46,8 @@ class Submodule { bindings.clone(submodule: _submodulePointer, callbacks: callbacks); bindings.addFinalize(_submodulePointer); + + _finalizer.attach(this, _submodulePointer, detach: this); } /// Pointer to memory address for allocated submodule object. @@ -264,7 +263,10 @@ class Submodule { } /// Releases memory allocated for submodule object. - void free() => bindings.free(_submodulePointer); + void free() { + bindings.free(_submodulePointer); + _finalizer.detach(this); + } @override String toString() { @@ -273,3 +275,9 @@ class Submodule { 'workdirOid: $workdirOid, ignore: $ignore, updateRule: $updateRule}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/tag.dart b/lib/src/tag.dart index fb1fec3..6b779be 100644 --- a/lib/src/tag.dart +++ b/lib/src/tag.dart @@ -8,18 +8,17 @@ import 'package:libgit2dart/src/bindings/tag.dart' as bindings; class Tag { /// Initializes a new instance of [Tag] class from provided pointer to /// tag object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Tag(this._tagPointer); + Tag(this._tagPointer) { + _finalizer.attach(this, _tagPointer, detach: this); + } /// Lookups tag object for provided [oid] in a [repo]sitory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Tag.lookup({required Repository repo, required Oid oid}) { _tagPointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _tagPointer, detach: this); } /// Pointer to memory address for allocated tag object. @@ -160,9 +159,6 @@ class Tag { /// Returned object should be explicitly downcasted to one of four of git /// object types. /// - /// **IMPORTANT**: returned object should be freed to release allocated - /// memory. - /// /// ```dart /// final commit = tag.target as Commit; /// final tree = tag.target as Tree; @@ -215,7 +211,10 @@ class Tag { } /// Releases memory allocated for tag object. - void free() => bindings.free(_tagPointer); + void free() { + bindings.free(_tagPointer); + _finalizer.detach(this); + } @override String toString() { @@ -223,3 +222,9 @@ class Tag { 'tagger: $tagger}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/tree.dart b/lib/src/tree.dart index a8035d8..b5a680e 100644 --- a/lib/src/tree.dart +++ b/lib/src/tree.dart @@ -7,18 +7,17 @@ import 'package:libgit2dart/src/bindings/tree.dart' as bindings; class Tree { /// Initializes a new instance of [Tree] class from provided pointer to /// tree object in memory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. - Tree(this._treePointer); + Tree(this._treePointer) { + _finalizer.attach(this, _treePointer, detach: this); + } /// Lookups a tree object for provided [oid] in a [repo]sitory. - /// - /// **IMPORTANT**: Should be freed to release allocated memory. Tree.lookup({required Repository repo, required Oid oid}) { _treePointer = bindings.lookup( repoPointer: repo.pointer, oidPointer: oid.pointer, ); + _finalizer.attach(this, _treePointer, detach: this); } late final Pointer _treePointer; @@ -63,7 +62,7 @@ class Tree { ), ); } else if (value is String && value.contains('/')) { - return TreeEntry( + return TreeEntry._byPath( bindings.getByPath( rootPointer: _treePointer, path: value, @@ -90,7 +89,10 @@ class Tree { int get length => bindings.entryCount(_treePointer); /// Releases memory allocated for tree object. - void free() => bindings.free(_treePointer); + void free() { + bindings.free(_treePointer); + _finalizer.detach(this); + } @override String toString() { @@ -98,10 +100,24 @@ class Tree { } } +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end + class TreeEntry { /// Initializes a new instance of [TreeEntry] class from provided pointer to /// tree entry object in memory. - const TreeEntry(this._treeEntryPointer); + TreeEntry(this._treeEntryPointer); + + /// Initializes a new instance of [TreeEntry] class from provided pointer to + /// tree entry object in memory. + /// + /// Unlike the other lookup methods, must be freed. + TreeEntry._byPath(this._treeEntryPointer) { + _entryFinalizer.attach(this, _treeEntryPointer, detach: this); + } /// Pointer to memory address for allocated tree entry object. final Pointer _treeEntryPointer; @@ -119,8 +135,19 @@ class TreeEntry { } /// Releases memory allocated for tree entry object. - void free() => bindings.entryFree(_treeEntryPointer); + /// + /// **IMPORTANT**: Only tree entries looked up by path should be freed. + void free() { + bindings.entryFree(_treeEntryPointer); + _entryFinalizer.detach(this); + } @override String toString() => 'TreeEntry{oid: $oid, name: $name, filemode: $filemode}'; } + +// coverage:ignore-start +final _entryFinalizer = Finalizer>( + (pointer) => bindings.entryFree(pointer), +); +// coverage:ignore-end diff --git a/lib/src/treebuilder.dart b/lib/src/treebuilder.dart index 21cef7c..89f9491 100644 --- a/lib/src/treebuilder.dart +++ b/lib/src/treebuilder.dart @@ -7,14 +7,13 @@ class TreeBuilder { /// Initializes a new instance of [TreeBuilder] class from provided /// [repo]sitory and optional [tree] objects. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. TreeBuilder({required Repository repo, Tree? tree}) { _treeBuilderPointer = bindings.create( repoPointer: repo.pointer, sourcePointer: tree?.pointer ?? nullptr, ); + _finalizer.attach(this, _treeBuilderPointer, detach: this); } /// Pointer to memory address for allocated tree builder object. @@ -31,9 +30,6 @@ class TreeBuilder { /// Returns an entry from the tree builder with provided [filename]. /// - /// **IMPORTANT**: the returned entry is owned by the tree builder and - /// should not be freed manually. - /// /// Throws [ArgumentError] if nothing found for provided [filename]. TreeEntry operator [](String filename) { return TreeEntry( @@ -83,10 +79,19 @@ class TreeBuilder { } /// Releases memory allocated for tree builder object and all the entries. - void free() => bindings.free(_treeBuilderPointer); + void free() { + bindings.free(_treeBuilderPointer); + _finalizer.detach(this); + } @override String toString() { return 'TreeBuilder{length: $length}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/lib/src/worktree.dart b/lib/src/worktree.dart index 2855576..bca7e3d 100644 --- a/lib/src/worktree.dart +++ b/lib/src/worktree.dart @@ -15,8 +15,6 @@ class Worktree { /// /// [path] is the path to create working tree at. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Worktree.create({ required Repository repo, @@ -30,15 +28,15 @@ class Worktree { path: path, refPointer: ref?.pointer, ); + _finalizer.attach(this, _worktreePointer, detach: this); } /// Lookups existing worktree in [repo] with provided [name]. /// - /// **IMPORTANT**: Should be freed to release allocated memory. - /// /// Throws a [LibGit2Error] if error occured. Worktree.lookup({required Repository repo, required String name}) { _worktreePointer = bindings.lookup(repoPointer: repo.pointer, name: name); + _finalizer.attach(this, _worktreePointer, detach: this); } /// Pointer to memory address for allocated branch object. @@ -88,10 +86,19 @@ class Worktree { bool get isValid => bindings.isValid(_worktreePointer); /// Releases memory allocated for worktree object. - void free() => bindings.free(_worktreePointer); + void free() { + bindings.free(_worktreePointer); + _finalizer.detach(this); + } @override String toString() { return 'Worktree{name: $name, path: $path}'; } } + +// coverage:ignore-start +final _finalizer = Finalizer>( + (pointer) => bindings.free(pointer), +); +// coverage:ignore-end diff --git a/pubspec.yaml b/pubspec.yaml index 969e2c3..5f92734 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,19 +3,20 @@ description: Dart bindings to libgit2 version: 0.0.1 environment: - sdk: ">=2.16.0 <3.0.0" - flutter: ">=2.10.0" + sdk: ">=2.17.0 <3.0.0" + flutter: ">=2.13.0-0.0.pre.578" dependencies: args: ^2.3.0 cli_util: ^0.3.5 ffi: ^1.1.2 + meta: ^1.7.0 + path: ^1.8.1 pub_cache: ^0.3.1 dev_dependencies: ffigen: ^4.1.2 - lints: ^1.0.1 - path: ^1.8.1 + lints: ^2.0.0 test: ^1.20.0 flutter: diff --git a/test/annotated_test.dart b/test/annotated_test.dart index c01441a..b40d2bf 100644 --- a/test/annotated_test.dart +++ b/test/annotated_test.dart @@ -29,8 +29,6 @@ void main() { expect(annotated.oid, tip); expect(annotated.refName, ''); - - annotated.free(); }); test('throws when trying to lookup annotated commit with invalid oid', () { @@ -49,25 +47,18 @@ void main() { expect(annotated.oid, reference.target); expect(annotated.refName, 'refs/heads/master'); - - annotated.free(); - reference.free(); }); test( 'throws when trying to create annotated commit from provided ' 'reference and error occurs', () { - final reference = Reference.lookup(repo: repo, name: 'refs/heads/master'); - expect( () => AnnotatedCommit.fromReference( repo: Repository(nullptr), - reference: reference, + reference: Reference.lookup(repo: repo, name: 'refs/heads/master'), ), throwsA(isA()), ); - - reference.free(); }); test('creates annotated commit from provided revspec', () { @@ -75,8 +66,6 @@ void main() { expect(annotated.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(annotated.refName, ''); - - annotated.free(); }); test('throws when trying to create annotated commit from invalid revspec', @@ -98,8 +87,6 @@ void main() { expect(annotated.oid, oid); expect(annotated.refName, 'master'); - - annotated.free(); }); test( @@ -115,5 +102,10 @@ void main() { throwsA(isA()), ); }); + + test('manually releases allocated memory', () { + final annotated = AnnotatedCommit.lookup(repo: repo, oid: tip); + expect(() => annotated.free(), returnsNormally); + }); }); } diff --git a/test/blame_test.dart b/test/blame_test.dart index 2aa2362..dea5099 100644 --- a/test/blame_test.dart +++ b/test/blame_test.dart @@ -52,8 +52,6 @@ void main() { }); tearDown(() { - sig1.free(); - sig2.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -82,8 +80,6 @@ void main() { expect(blame[i].isBoundary, hunks[i]['isBoundary']); expect(blame[i].originPath, 'feature_file'); } - - blame.free(); }); test('throws when provided file path is invalid', () { @@ -104,9 +100,6 @@ void main() { expect(blameHunk.originCommitter, null); expect(blameHunk.finalCommitOid.sha, '0' * 40); expect(blameHunk.finalCommitter, null); - - bufferBlame.free(); - blame.free(); }); test('throws when trying to get blame for empty buffer', () { @@ -115,7 +108,6 @@ void main() { () => Blame.buffer(reference: blame, buffer: ''), throwsA(isA()), ); - blame.free(); }); test('returns the blame for provided file with minMatchCharacters set', () { @@ -127,8 +119,6 @@ void main() { ); expect(blame.length, 2); - - blame.free(); }); test('returns the blame for provided line', () { @@ -148,22 +138,16 @@ void main() { expect(hunk.originCommitter, hunks[0]['originCommitter']); expect(hunk.isBoundary, hunks[0]['isBoundary']); expect(hunk.originPath, 'feature_file'); - - blame.free(); }); test('throws when provided index for hunk is invalid', () { final blame = Blame.file(repo: repo, path: 'feature_file'); expect(() => blame[10], throwsA(isA())); - - blame.free(); }); test('throws when provided line number for hunk is invalid', () { final blame = Blame.file(repo: repo, path: 'feature_file'); expect(() => blame.forLine(10), throwsA(isA())); - - blame.free(); }); test('returns the blame for provided file with newestCommit argument', () { @@ -190,8 +174,6 @@ void main() { expect(hunk.originCommitter, hunks[0]['originCommitter']); expect(hunk.isBoundary, hunks[0]['isBoundary']); expect(hunk.originPath, 'feature_file'); - - blame.free(); }); test('returns the blame for provided file with minLine and maxLine set', @@ -219,14 +201,16 @@ void main() { expect(blame[i].isBoundary, hunks[i]['isBoundary']); expect(blame[i].originPath, 'feature_file'); } + }); - blame.free(); + test('manually releases allocated memory', () { + final blame = Blame.file(repo: repo, path: 'feature_file'); + expect(() => blame.free(), returnsNormally); }); test('returns string representation of BlameHunk object', () { final blame = Blame.file(repo: repo, path: 'feature_file'); expect(blame.toString(), contains('BlameHunk{')); - blame.free(); }); }); } diff --git a/test/blob_test.dart b/test/blob_test.dart index 0fdbe4d..c022eeb 100644 --- a/test/blob_test.dart +++ b/test/blob_test.dart @@ -26,9 +26,7 @@ void main() { group('Blob', () { test('lookups blob with provided oid', () { - final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]); - expect(blob, isA()); - blob.free(); + expect(Blob.lookup(repo: repo, oid: repo[blobSHA]), isA()); }); test('throws when trying to lookup with invalid oid', () { @@ -45,8 +43,6 @@ void main() { expect(blob.isBinary, false); expect(blob.size, 13); expect(blob.content, blobContent); - - blob.free(); }); test('creates new blob with provided content', () { @@ -57,14 +53,11 @@ void main() { expect(newBlob.isBinary, false); expect(newBlob.size, 9); expect(newBlob.content, newBlobContent); - - newBlob.free(); }); test('throws when trying to create new blob and error occurs', () { - final nullRepo = Repository(nullptr); expect( - () => Blob.create(repo: nullRepo, content: ''), + () => Blob.create(repo: Repository(nullptr), content: ''), throwsA(isA()), ); }); @@ -80,8 +73,6 @@ void main() { expect(newBlob.isBinary, false); expect(newBlob.size, 13); expect(newBlob.content, blobContent); - - newBlob.free(); }); test('throws when creating new blob from invalid path', () { @@ -103,8 +94,6 @@ void main() { expect(newBlob, isA()); expect(newBlob.isBinary, false); - - newBlob.free(); }); test('throws when trying to create from invalid path', () { @@ -119,9 +108,6 @@ void main() { final dupBlob = blob.duplicate(); expect(blob.oid.sha, dupBlob.oid.sha); - - dupBlob.free(); - blob.free(); }); test('filters content of a blob', () { @@ -129,8 +115,6 @@ void main() { final blob = Blob.lookup(repo: repo, oid: blobOid); expect(blob.filterContent(asPath: 'file.crlf'), 'clrf\r\nclrf\r\n'); - - blob.free(); }); test('filters content of a blob with provided commit for attributes', () { @@ -152,9 +136,6 @@ void main() { ), 'clrf\r\nclrf\r\n', ); - - commit.free(); - blob.free(); }); test('throws when trying to filter content of a blob and error occurs', () { @@ -164,10 +145,14 @@ void main() { ); }); + test('manually releases allocated memory', () { + final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]); + expect(() => blob.free(), returnsNormally); + }); + test('returns string representation of Blob object', () { final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]); expect(blob.toString(), contains('Blob{')); - blob.free(); }); }); } diff --git a/test/branch_test.dart b/test/branch_test.dart index d8bdbd1..31fc2c7 100644 --- a/test/branch_test.dart +++ b/test/branch_test.dart @@ -34,8 +34,6 @@ void main() { for (var i = 0; i < branches.length; i++) { expect(branches[i].name, branchesExpected[i]); expect(aliasBranches[i].name, branchesExpected[i]); - branches[i].free(); - aliasBranches[i].free(); } }); @@ -47,8 +45,6 @@ void main() { for (var i = 0; i < branches.length; i++) { expect(branches[i].name, branchesExpected[i]); expect(aliasBranches[i].name, branchesExpected[i]); - branches[i].free(); - aliasBranches[i].free(); } }); @@ -60,8 +56,6 @@ void main() { for (var i = 0; i < branches.length; i++) { expect(branches[i].name, branchesExpected[i]); expect(aliasBranches[i].name, branchesExpected[i]); - branches[i].free(); - aliasBranches[i].free(); } }); @@ -75,7 +69,6 @@ void main() { test('returns a branch with provided name', () { final branch = Branch.lookup(repo: repo, name: 'master'); expect(branch.target.sha, lastCommit.sha); - branch.free(); }); test('throws when provided name not found', () { @@ -100,15 +93,11 @@ void main() { expect(masterBranch.isHead, true); expect(featureBranch.isHead, false); - - masterBranch.free(); - featureBranch.free(); }); test('throws when checking if branch is current head and error occurs', () { - final nullBranch = Branch(nullptr); expect( - () => nullBranch.isHead, + () => Branch(nullptr).isHead, throwsA(isA()), ); }); @@ -119,15 +108,11 @@ void main() { expect(masterBranch.isCheckedOut, true); expect(featureBranch.isCheckedOut, false); - - masterBranch.free(); - featureBranch.free(); }); test('throws when checking if branch is checked out and error occurs', () { - final nullBranch = Branch(nullptr); expect( - () => nullBranch.isCheckedOut, + () => Branch(nullptr).isCheckedOut, throwsA(isA()), ); }); @@ -135,18 +120,15 @@ void main() { test('returns name', () { final branch = Branch.lookup(repo: repo, name: 'master'); expect(branch.name, 'master'); - branch.free(); }); test('throws when getting name and error occurs', () { - final nullBranch = Branch(nullptr); - expect(() => nullBranch.name, throwsA(isA())); + expect(() => Branch(nullptr).name, throwsA(isA())); }); test('returns remote name of a remote-tracking branch', () { final branch = Branch.list(repo: repo, type: GitBranch.remote).first; expect(branch.remoteName, 'origin'); - branch.free(); }); test( @@ -162,21 +144,16 @@ void main() { expect(upstream.isRemote, true); expect(upstream.name, 'refs/remotes/origin/master'); - - upstream.free(); - branch.free(); }); test('throws when trying to get upstream of a remote branch', () { final branch = Branch.list(repo: repo, type: GitBranch.remote).first; expect(() => branch.upstream, throwsA(isA())); - branch.free(); }); test('sets upstream of a branch', () { final branch = Branch.lookup(repo: repo, name: 'master'); - var upstream = branch.upstream; - expect(upstream.name, 'refs/remotes/origin/master'); + expect(branch.upstream.name, 'refs/remotes/origin/master'); final ref = Reference.create( repo: repo, @@ -185,24 +162,15 @@ void main() { ); branch.setUpstream(ref.shorthand); - upstream = branch.upstream; - expect(upstream.name, 'refs/remotes/origin/new'); - - ref.free(); - upstream.free(); - branch.free(); + expect(branch.upstream.name, 'refs/remotes/origin/new'); }); test('unsets upstream of a branch', () { final branch = Branch.lookup(repo: repo, name: 'master'); - final upstream = branch.upstream; - expect(upstream.name, 'refs/remotes/origin/master'); + expect(branch.upstream.name, 'refs/remotes/origin/master'); branch.setUpstream(null); expect(() => branch.upstream, throwsA(isA())); - - upstream.free(); - branch.free(); }); test('throws when trying to set upstream of a branch and error occurs', () { @@ -211,64 +179,50 @@ void main() { () => branch.setUpstream('some/upstream'), throwsA(isA()), ); - branch.free(); }); test('returns upstream name of a local branch', () { final branch = Branch.lookup(repo: repo, name: 'master'); expect(branch.upstreamName, 'refs/remotes/origin/master'); - branch.free(); }); test('throws when trying to get upstream name of a branch and error occurs', () { final branch = Branch.lookup(repo: repo, name: 'feature'); expect(() => branch.upstreamName, throwsA(isA())); - branch.free(); }); test('returns upstream remote of a local branch', () { final branch = Branch.lookup(repo: repo, name: 'master'); expect(branch.upstreamRemote, 'origin'); - branch.free(); }); test('throws when trying to get upstream remote of a remote branch', () { final branch = Branch.list(repo: repo, type: GitBranch.remote).first; expect(() => branch.upstreamRemote, throwsA(isA())); - branch.free(); }); test('returns upstream merge of a local branch', () { final branch = Branch.lookup(repo: repo, name: 'master'); expect(branch.upstreamMerge, 'refs/heads/master'); - branch.free(); }); test('throws when trying to get upstream merge of a remote branch', () { final branch = Branch.list(repo: repo, type: GitBranch.remote).first; expect(() => branch.upstreamMerge, throwsA(isA())); - branch.free(); }); group('create()', () { test('creates branch', () { - final commit = Commit.lookup(repo: repo, oid: lastCommit); final branch = Branch.create( repo: repo, name: 'testing', - target: commit, + target: Commit.lookup(repo: repo, oid: lastCommit), ); final branches = Branch.list(repo: repo); expect(branches.length, 4); expect(branch.target, lastCommit); - - for (final branch in branches) { - branch.free(); - } - branch.free(); - commit.free(); }); test('throws when name already exists', () { @@ -278,28 +232,19 @@ void main() { () => Branch.create(repo: repo, name: 'feature', target: commit), throwsA(isA()), ); - - commit.free(); }); test('creates branch with force flag when name already exists', () { - final commit = Commit.lookup(repo: repo, oid: lastCommit); final branch = Branch.create( repo: repo, name: 'feature', - target: commit, + target: Commit.lookup(repo: repo, oid: lastCommit), force: true, ); final localBranches = Branch.list(repo: repo, type: GitBranch.local); expect(localBranches.length, 2); expect(branch.target, lastCommit); - - for (final branch in localBranches) { - branch.free(); - } - branch.free(); - commit.free(); }); }); @@ -333,11 +278,6 @@ void main() { throwsA(isA()), ); expect(branch.target, featureCommit); - - for (final branch in branches) { - branch.free(); - } - branch.free(); }); test('throws when name already exists', () { @@ -361,8 +301,6 @@ void main() { final branch = Branch.lookup(repo: repo, name: 'feature'); expect(branch.target, lastCommit); - - branch.free(); }); test('throws when name is invalid', () { @@ -377,10 +315,14 @@ void main() { }); }); + test('manually releases allocated memory', () { + final branch = Branch.lookup(repo: repo, name: 'master'); + expect(() => branch.free(), returnsNormally); + }); + test('returns string representation of Branch object', () { final branch = Branch.lookup(repo: repo, name: 'master'); expect(branch.toString(), contains('Branch{')); - branch.free(); }); }); } diff --git a/test/checkout_test.dart b/test/checkout_test.dart index 4c6b318..3963c41 100644 --- a/test/checkout_test.dart +++ b/test/checkout_test.dart @@ -89,12 +89,6 @@ void main() { featureTree.entries.any((e) => e.name == 'another_feature_file'), true, ); - - repoHead.free(); - featureTree.free(); - featureHead.free(); - masterTree.free(); - masterHead.free(); }); test( @@ -121,10 +115,6 @@ void main() { // does not change HEAD expect(repoHead.target, isNot(featureHead.oid)); expect(index.find('another_feature_file'), equals(true)); - - repoHead.free(); - featureHead.free(); - index.free(); }); test('checkouts commit with provided path', () { @@ -144,26 +134,19 @@ void main() { 'another_feature_file': {GitStatus.indexNew} }, ); - - repoHead.free(); - featureHead.free(); }); test( 'throws when trying to checkout commit with invalid alternative ' 'directory', () { - final commit = Commit.lookup(repo: repo, oid: repo['5aecfa0']); - expect( () => Checkout.commit( repo: repo, - commit: commit, + commit: Commit.lookup(repo: repo, oid: repo['5aecfa0']), directory: 'not/there', ), throwsA(isA()), ); - - commit.free(); }); test('checkouts with alrenative directory', () { @@ -220,8 +203,6 @@ void main() { ); expect(index.length, 4); expect(file.existsSync(), false); - - index.free(); }); }); } diff --git a/test/commit_test.dart b/test/commit_test.dart index 4d77b58..e309def 100644 --- a/test/commit_test.dart +++ b/test/commit_test.dart @@ -34,18 +34,13 @@ void main() { }); tearDown(() { - author.free(); - committer.free(); - tree.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); group('Commit', () { test('lookups commit for provided oid', () { - final commit = Commit.lookup(repo: repo, oid: tip); - expect(commit, isA()); - commit.free(); + expect(Commit.lookup(repo: repo, oid: tip), isA()); }); test('throws when trying to lookup with invalid oid', () { @@ -72,9 +67,6 @@ void main() { expect(index.find('dir/dir_file.txt'), false); expect(file.existsSync(), false); - - index.free(); - commit.free(); }); test('throws when trying to revert and error occurs', () { @@ -92,11 +84,6 @@ void main() { final revertIndex = from.revertTo(commit: to); expect(revertIndex.find('dir/dir_file.txt'), false); expect(file.existsSync(), true); - - revertIndex.free(); - index.free(); - to.free(); - from.free(); }); test('throws when trying to revert commit and error occurs', () { @@ -114,9 +101,6 @@ void main() { expect(commit1.descendantOf(commit2.oid), true); expect(commit1.descendantOf(commit1.oid), false); expect(commit2.descendantOf(commit1.oid), false); - - commit1.free(); - commit2.free(); }); test('creates commit', () { @@ -146,13 +130,9 @@ void main() { expect(commit.treeOid, tree.oid); expect(commit.parents.length, 1); expect(commit.parents[0], tip); - - commit.free(); - parent.free(); }); test('writes commit without parents into the buffer', () { - final parent = Commit.lookup(repo: repo, oid: tip); final commit = Commit.createBuffer( repo: repo, updateRef: 'HEAD', @@ -174,8 +154,6 @@ Some description. """; expect(commit, expected); - - parent.free(); }); test('writes commit into the buffer', () { @@ -202,8 +180,6 @@ Some description. """; expect(commit, expected); - - parent.free(); }); test('creates commit without parents', () { @@ -227,8 +203,6 @@ Some description. expect(commit.time, 124); expect(commit.treeOid, tree.oid); expect(commit.parents.length, 0); - - commit.free(); }); test('creates commit with 2 parents', () { @@ -257,57 +231,42 @@ Some description. expect(commit.parents.length, 2); expect(commit.parents[0], tip); expect(commit.parents[1], parent2.oid); - - parent1.free(); - parent2.free(); - commit.free(); }); test('throws when trying to create commit and error occurs', () { - final parent = Commit.lookup(repo: repo, oid: tip); - final nullRepo = Repository(nullptr); - expect( () => Commit.create( - repo: nullRepo, + repo: Repository(nullptr), updateRef: 'HEAD', message: message, author: author, committer: committer, tree: tree, - parents: [parent], + parents: [Commit.lookup(repo: repo, oid: tip)], ), throwsA(isA()), ); - - parent.free(); }); test('throws when trying to write commit into a buffer and error occurs', () { - final parent = Commit.lookup(repo: repo, oid: tip); - final nullRepo = Repository(nullptr); - expect( () => Commit.createBuffer( - repo: nullRepo, + repo: Repository(nullptr), updateRef: 'HEAD', message: message, author: author, committer: committer, tree: tree, - parents: [parent], + parents: [Commit.lookup(repo: repo, oid: tip)], ), throwsA(isA()), ); - - parent.free(); }); test('amends commit with default arguments', () { - final oldHead = repo.head; final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']); - expect(commit.oid, oldHead.target); + expect(commit.oid, repo.head.target); final amendedOid = Commit.amend( repo: repo, @@ -316,25 +275,18 @@ Some description. updateRef: 'HEAD', ); final amendedCommit = Commit.lookup(repo: repo, oid: amendedOid); - final newHead = repo.head; - expect(amendedCommit.oid, newHead.target); + expect(amendedCommit.oid, repo.head.target); expect(amendedCommit.message, 'amended commit\n'); expect(amendedCommit.author, commit.author); expect(amendedCommit.committer, commit.committer); expect(amendedCommit.treeOid, commit.treeOid); expect(amendedCommit.parents, commit.parents); - - amendedCommit.free(); - commit.free(); - newHead.free(); - oldHead.free(); }); test('amends commit with provided arguments', () { - final oldHead = repo.head; final commit = Commit.lookup(repo: repo, oid: repo['821ed6e']); - expect(commit.oid, oldHead.target); + expect(commit.oid, repo.head.target); final amendedOid = Commit.amend( repo: repo, @@ -346,25 +298,18 @@ Some description. tree: tree, ); final amendedCommit = Commit.lookup(repo: repo, oid: amendedOid); - final newHead = repo.head; - expect(amendedCommit.oid, newHead.target); + expect(amendedCommit.oid, repo.head.target); expect(amendedCommit.message, 'amended commit\n'); expect(amendedCommit.author, author); expect(amendedCommit.committer, committer); expect(amendedCommit.treeOid, tree.oid); expect(amendedCommit.parents, commit.parents); - - amendedCommit.free(); - commit.free(); - newHead.free(); - oldHead.free(); }); test('amends commit that is not the tip of the branch', () { - final head = repo.head; final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']); - expect(commit.oid, isNot(head.target)); + expect(commit.oid, isNot(repo.head.target)); expect( () => Commit.amend( @@ -375,17 +320,13 @@ Some description. ), returnsNormally, ); - - commit.free(); - head.free(); }); test( 'throws when trying to amend commit that is not the tip of the branch ' 'with HEAD provided as update reference', () { - final head = repo.head; final commit = Commit.lookup(repo: repo, oid: repo['78b8bf1']); - expect(commit.oid, isNot(head.target)); + expect(commit.oid, isNot(repo.head.target)); expect( () => Commit.amend( @@ -396,9 +337,6 @@ Some description. ), throwsA(isA()), ); - - commit.free(); - head.free(); }); test('creates an in-memory copy of a commit', () { @@ -406,9 +344,6 @@ Some description. final dupCommit = commit.duplicate(); expect(dupCommit.oid, commit.oid); - - dupCommit.free(); - commit.free(); }); test('returns header field', () { @@ -417,7 +352,6 @@ Some description. commit.headerField('parent'), '78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8', ); - commit.free(); }); test('throws when header field not found', () { @@ -426,7 +360,6 @@ Some description. () => commit.headerField('not-there'), throwsA(isA()), ); - commit.free(); }); test('returns nth generation ancestor commit', () { @@ -434,16 +367,12 @@ Some description. final ancestor = commit.nthGenAncestor(3); expect(ancestor.oid.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521'); - - ancestor.free(); - commit.free(); }); test('throws when trying to get nth generation ancestor and none exists', () { final commit = Commit.lookup(repo: repo, oid: tip); expect(() => commit.nthGenAncestor(10), throwsA(isA())); - commit.free(); }); test('returns parent at specified position', () { @@ -453,22 +382,21 @@ Some description. expect(firstParent.oid.sha, 'c68ff54aabf660fcdd9a2838d401583fe31249e3'); expect(secondParent.oid.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); - - secondParent.free(); - firstParent.free(); - commit.free(); }); test('throws when trying to get the parent at invalid position', () { final commit = Commit.lookup(repo: repo, oid: tip); expect(() => commit.parent(10), throwsA(isA())); - commit.free(); + }); + + test('manually releases allocated memory', () { + final commit = Commit.lookup(repo: repo, oid: tip); + expect(() => commit.free(), returnsNormally); }); test('returns string representation of Commit object', () { final commit = Commit.lookup(repo: repo, oid: tip); expect(commit.toString(), contains('Commit{')); - commit.free(); }); }); } diff --git a/test/config_test.dart b/test/config_test.dart index 77544c2..b14a8b7 100644 --- a/test/config_test.dart +++ b/test/config_test.dart @@ -32,7 +32,6 @@ void main() { }); tearDown(() { - config.free(); File(filePath).deleteSync(); }); @@ -45,9 +44,7 @@ void main() { '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.free(); + expect(Config.open(), isA()); } catch (e) { expect(() => Config.open(), throwsA(isA())); } @@ -59,9 +56,7 @@ void main() { test('opens system file or throws is there is none', () { try { - final config = Config.system(); - expect(config, isA()); - config.free(); + expect(Config.system(), isA()); } catch (e) { expect(() => Config.system(), throwsA(isA())); } @@ -69,9 +64,7 @@ void main() { test('opens global file or throws is there is none', () { try { - final config = Config.global(); - expect(config, isA()); - config.free(); + expect(Config.global(), isA()); } catch (e) { expect(() => Config.global(), throwsA(isA())); } @@ -79,18 +72,14 @@ void main() { test('opens xdg file or throws is there is none', () { try { - final config = Config.xdg(); - expect(config, isA()); - config.free(); + expect(Config.xdg(), isA()); } catch (e) { expect(() => Config.xdg(), throwsA(isA())); } }); test('returns config snapshot', () { - final snapshot = config.snapshot; - expect(snapshot, isA()); - snapshot.free(); + expect(config.snapshot, isA()); }); test('returns config entries and their values', () { @@ -219,6 +208,11 @@ void main() { }); }); + test('manually releases allocated memory', () { + final config = Config.open(filePath); + expect(() => config.free(), returnsNormally); + }); + test('returns string representation of ConfigEntry object', () { final entry = config.first; expect(entry.toString(), contains('ConfigEntry{')); diff --git a/test/describe_test.dart b/test/describe_test.dart index 16d4697..05c798c 100644 --- a/test/describe_test.dart +++ b/test/describe_test.dart @@ -43,7 +43,6 @@ void main() { test('throws when trying to describe and no reference found', () { final commit = Commit.lookup(repo: repo, oid: repo['f17d0d4']); expect(() => repo.describe(commit: commit), throwsA(isA())); - commit.free(); }); test('returns oid when fallback argument is provided', () { @@ -52,7 +51,6 @@ void main() { repo.describe(commit: commit, showCommitOidAsFallback: true), 'f17d0d4', ); - commit.free(); }); test('describes with provided strategy', () { @@ -64,7 +62,6 @@ void main() { ), 'heads/feature', ); - commit.free(); }); test('describes with provided pattern', () { @@ -87,9 +84,6 @@ void main() { repo.describe(commit: commit, pattern: 'test/*'), 'test/tag1-2-gfc38877', ); - - commit.free(); - signature.free(); }); test('describes and follows first parent only', () { @@ -104,8 +98,6 @@ void main() { ), 'v0.1-1-g821ed6e', ); - - commit.free(); }); test('describes with provided abbreviated size', () { @@ -129,8 +121,6 @@ void main() { ), 'v0.1', ); - - commit.free(); }); test('describes with long format', () { @@ -138,21 +128,13 @@ void main() { }); test('describes and appends dirty suffix', () { - final index = repo.index; - index.clear(); - + repo.index.clear(); expect(repo.describe(dirtySuffix: '-dirty'), 'v0.2-dirty'); - - index.free(); }); test('describes with max candidates tags flag set', () { - final index = repo.index; - index.clear(); - + repo.index.clear(); expect(repo.describe(maxCandidatesTags: 0), 'v0.2'); - - index.free(); }); }); } diff --git a/test/diff_test.dart b/test/diff_test.dart index 0c7b30f..9cfd8b5 100644 --- a/test/diff_test.dart +++ b/test/diff_test.dart @@ -133,71 +133,45 @@ index e69de29..c217c63 100644 group('Diff', () { test('returns diff between index and workdir', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(diff.length, 8); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, indexToWorkdir[i]); } - - diff.free(); - index.free(); }); test('returns diff between index and tree', () { - final index = repo.index; - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - final diff = Diff.treeToIndex(repo: repo, tree: tree, index: index); + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final diff = Diff.treeToIndex( + repo: repo, + tree: commit.tree, + index: repo.index, + ); expect(diff.length, 8); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, indexToTree[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); - index.free(); }); test('returns diff between index and empty tree', () { - final index = repo.index; - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - final diff = Diff.treeToIndex(repo: repo, tree: null, index: index); + final diff = Diff.treeToIndex(repo: repo, tree: null, index: repo.index); expect(diff.length, 12); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, indexToIndex[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); - index.free(); }); test('returns diff between tree and workdir', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - final diff = Diff.treeToWorkdir(repo: repo, tree: tree); + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final diff = Diff.treeToWorkdir(repo: repo, tree: commit.tree); expect(diff.length, 9); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToWorkdir[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); }); test('throws when trying to diff between tree and workdir and error occurs', @@ -209,20 +183,13 @@ index e69de29..c217c63 100644 }); test('returns diff between tree and workdir with index', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final diff = Diff.treeToWorkdirWithIndex(repo: repo, tree: commit.tree); - final diff = Diff.treeToWorkdirWithIndex(repo: repo, tree: tree); expect(diff.length, 11); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToWorkdirWithIndex[i]); } - - diff.free(); - tree.free(); - commit.free(); - head.free(); }); test( @@ -238,68 +205,55 @@ index e69de29..c217c63 100644 }); test('returns diff between tree and tree', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree1 = commit.tree; - final tree2 = Tree.lookup(repo: repo, oid: repo['b85d53c']); - final diff = Diff.treeToTree(repo: repo, oldTree: tree1, newTree: tree2); + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final newTree = Tree.lookup(repo: repo, oid: repo['b85d53c']); + final diff = Diff.treeToTree( + repo: repo, + oldTree: commit.tree, + newTree: newTree, + ); expect(diff.length, 10); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToTree[i]); } - - commit.free(); - head.free(); - tree1.free(); - tree2.free(); - diff.free(); }); test('returns diff between tree and empty tree', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - - final diff = Diff.treeToTree(repo: repo, oldTree: tree, newTree: null); + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final diff = Diff.treeToTree( + repo: repo, + oldTree: commit.tree, + newTree: null, + ); expect(diff.length, 11); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToEmptyTree[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); }); test('returns diff between empty tree and tree', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - - final diff = Diff.treeToTree(repo: repo, oldTree: null, newTree: tree); + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final diff = Diff.treeToTree( + repo: repo, + oldTree: null, + newTree: commit.tree, + ); expect(diff.length, 11); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, treeToEmptyTree[i]); } - - commit.free(); - head.free(); - tree.free(); - diff.free(); }); test('throws when trying to diff between tree and tree and error occurs', () { - final nullTree = Tree(nullptr); expect( () => Diff.treeToTree( repo: Repository(nullptr), - oldTree: nullTree, - newTree: nullTree, + oldTree: Tree(nullptr), + newTree: Tree(nullptr), ), throwsA(isA()), ); @@ -313,60 +267,45 @@ index e69de29..c217c63 100644 }); test('returns diff between index and index', () { - final index = repo.index; - final emptyIndex = Index.newInMemory(); - final diff = Diff.indexToIndex( repo: repo, - oldIndex: index, - newIndex: emptyIndex, + oldIndex: repo.index, + newIndex: Index.newInMemory(), ); expect(diff.length, 12); for (var i = 0; i < diff.deltas.length; i++) { expect(diff.deltas[i].newFile.path, indexToIndex[i]); } - - index.free(); - emptyIndex.free(); }); test('throws when trying to diff between index and index and error occurs', () { - final index = repo.index; - expect( () => Diff.indexToIndex( repo: repo, - oldIndex: index, + oldIndex: repo.index, newIndex: Index(nullptr), ), throwsA(isA()), ); - - index.free(); }); test('merges diffs', () { - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree1 = commit.tree; - final tree2 = Tree.lookup(repo: repo, oid: repo['b85d53c']); - final diff1 = Diff.treeToTree(repo: repo, oldTree: tree1, newTree: tree2); - final diff2 = Diff.treeToWorkdir(repo: repo, tree: tree1); + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final newTree = Tree.lookup(repo: repo, oid: repo['b85d53c']); + final diff1 = Diff.treeToTree( + repo: repo, + oldTree: commit.tree, + newTree: newTree, + ); + final diff2 = Diff.treeToWorkdir(repo: repo, tree: commit.tree); expect(diff1.length, 10); expect(diff2.length, 9); diff1.merge(diff2); expect(diff1.length, 11); - - commit.free(); - head.free(); - tree1.free(); - tree2.free(); - diff1.free(); - diff2.free(); }); test('parses provided diff', () { @@ -377,15 +316,11 @@ index e69de29..c217c63 100644 expect(stats.filesChanged, 1); expect(stats.insertions, 1); expect(diff.patchOid.sha, '699556913185bc38632ae20a49d5c18b9233335e'); - - stats.free(); - diff.free(); }); group('apply', () { test('checks if diff can be applied to repository', () { - final index = repo.index; - final diff1 = Diff.indexToWorkdir(repo: repo, index: index); + final diff1 = Diff.indexToWorkdir(repo: repo, index: repo.index); expect( diff1.applies(repo: repo, location: GitApplyLocation.both), false, @@ -397,16 +332,11 @@ index e69de29..c217c63 100644 diff2.applies(repo: repo, location: GitApplyLocation.both), true, ); - - diff1.free(); - diff2.free(); - index.free(); }); test('checks if hunk with provided index can be applied to repository', () { - final index = repo.index; - final diff1 = Diff.indexToWorkdir(repo: repo, index: index); + final diff1 = Diff.indexToWorkdir(repo: repo, index: repo.index); expect( diff1.applies(repo: repo, location: GitApplyLocation.both), false, @@ -423,10 +353,6 @@ index e69de29..c217c63 100644 ), true, ); - - diff1.free(); - diff2.free(); - index.free(); }); test('applies diff to repository', () { @@ -438,8 +364,6 @@ index e69de29..c217c63 100644 diff.apply(repo: repo); expect(file.readAsStringSync(), 'Modified content\n'); - - diff.free(); }); test('throws when trying to apply diff and error occurs', () { @@ -455,9 +379,6 @@ index e69de29..c217c63 100644 expect(diff.length, 1); expect(patch.text, patchText); - - patch.free(); - diff.free(); }); test('applies hunk with provided index to repository', () { @@ -470,8 +391,6 @@ index e69de29..c217c63 100644 diff.apply(repo: repo, hunkIndex: hunk.index); expect(file.readAsStringSync(), 'Modified content\n'); - - diff.free(); }); test('does not apply hunk with non existing index', () { @@ -483,61 +402,44 @@ index e69de29..c217c63 100644 diff.apply(repo: repo, hunkIndex: 10); expect(file.readAsStringSync(), ''); - - diff.free(); }); test('applies diff to tree', () { - final diff = Diff.parse(patchText); - Checkout.head(repo: repo, strategy: {GitCheckout.force}); - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - final oldIndex = repo.index; final oldBlob = Blob.lookup( repo: repo, - oid: oldIndex['subdir/modified_file'].oid, + oid: repo.index['subdir/modified_file'].oid, ); expect(oldBlob.content, ''); - final newIndex = diff.applyToTree(repo: repo, tree: tree); + final diff = Diff.parse(patchText); + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + + final newIndex = diff.applyToTree(repo: repo, tree: commit.tree); final newBlob = Blob.lookup( repo: repo, oid: newIndex['subdir/modified_file'].oid, ); expect(newBlob.content, 'Modified content\n'); - - oldBlob.free(); - newBlob.free(); - oldIndex.free(); - newIndex.free(); - tree.free(); - commit.free(); - head.free(); - diff.free(); }); test('applies hunk with provided index to tree', () { - final diff = Diff.parse(patchText); - final hunk = diff.patches.first.hunks.first; - Checkout.head(repo: repo, strategy: {GitCheckout.force}); - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final tree = commit.tree; - final oldIndex = repo.index; final oldBlob = Blob.lookup( repo: repo, - oid: oldIndex['subdir/modified_file'].oid, + oid: repo.index['subdir/modified_file'].oid, ); expect(oldBlob.content, ''); + final diff = Diff.parse(patchText); + final hunk = diff.patches.first.hunks.first; + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final newIndex = diff.applyToTree( repo: repo, - tree: tree, + tree: commit.tree, hunkIndex: hunk.index, ); final newBlob = Blob.lookup( @@ -545,15 +447,6 @@ index e69de29..c217c63 100644 oid: newIndex['subdir/modified_file'].oid, ); expect(newBlob.content, 'Modified content\n'); - - oldBlob.free(); - newBlob.free(); - oldIndex.free(); - newIndex.free(); - tree.free(); - commit.free(); - head.free(); - diff.free(); }); test('throws when trying to apply diff to tree and error occurs', () { @@ -566,15 +459,12 @@ index e69de29..c217c63 100644 }); test('finds similar entries', () { - final index = repo.index; - final head = repo.head; - final commit = Commit.lookup(repo: repo, oid: head.target); - final oldTree = commit.tree; - final newTree = Tree.lookup(repo: repo, oid: index.writeTree()); + final commit = Commit.lookup(repo: repo, oid: repo.head.target); + final newTree = Tree.lookup(repo: repo, oid: repo.index.writeTree()); final diff = Diff.treeToTree( repo: repo, - oldTree: oldTree, + oldTree: commit.tree, newTree: newTree, ); expect( @@ -587,28 +477,18 @@ index e69de29..c217c63 100644 diff.deltas.singleWhere((e) => e.newFile.path == 'staged_new').status, GitDelta.renamed, ); - - commit.free(); - head.free(); - diff.free(); - index.free(); - oldTree.free(); - newTree.free(); }); test('throws when trying to find similar entries and error occurs', () { - final nullDiff = Diff(nullptr); - expect(() => nullDiff.findSimilar(), throwsA(isA())); + expect(() => Diff(nullptr).findSimilar(), throwsA(isA())); }); test('throws when trying to get patch Oid and error occurs', () { - final nullDiff = Diff(nullptr); - expect(() => nullDiff.patchOid, throwsA(isA())); + expect(() => Diff(nullptr).patchOid, throwsA(isA())); }); test('returns deltas', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(diff.deltas[0].numberOfFiles, 1); expect(diff.deltas[0].status, GitDelta.deleted); @@ -635,70 +515,45 @@ index e69de29..c217c63 100644 ); expect(diff.deltas[0].oldFile.mode, GitFilemode.blob); - - diff.free(); - index.free(); }); test('throws when trying to get delta with invalid index', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); - + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(() => diff.deltas[-1], throwsA(isA())); - - diff.free(); - index.free(); }); test('returns patches', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); final patches = diff.patches; expect(patches.length, 8); expect(patches.first.delta.status, GitDelta.deleted); - - for (final p in patches) { - p.free(); - } - diff.free(); - index.free(); }); test('returns stats', () { - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); final stats = diff.stats; expect(stats.insertions, 4); expect(stats.deletions, 2); expect(stats.filesChanged, 8); expect(stats.print(format: {GitDiffStats.full}, width: 80), statsPrint); - - stats.free(); - diff.free(); - index.free(); }); test('throws when trying to get stats and error occurs', () { - final nullDiff = Diff(nullptr); - expect(() => nullDiff.stats, throwsA(isA())); + expect(() => Diff(nullptr).stats, throwsA(isA())); }); test('throws when trying to print stats and error occurs', () { - final nullStats = DiffStats(nullptr); expect( - () => nullStats.print(format: {GitDiffStats.full}, width: 80), + () => DiffStats(nullptr).print(format: {GitDiffStats.full}, width: 80), throwsA(isA()), ); }); test('returns patch diff string', () { final diff = Diff.parse(patchText); - expect(diff.patch, patchText); - - diff.free(); }); test('returns hunks in a patch', () { @@ -713,9 +568,6 @@ index e69de29..c217c63 100644 expect(hunk.newStart, 1); expect(hunk.newLines, 1); expect(hunk.header, '@@ -0,0 +1 @@\n'); - - patch.free(); - diff.free(); }); test('returns lines in a hunk', () { @@ -731,9 +583,16 @@ index e69de29..c217c63 100644 expect(line.numLines, 1); expect(line.contentOffset, 155); expect(line.content, 'Modified content\n'); + }); - patch.free(); - diff.free(); + test('manually releases allocated memory', () { + final diff = Diff.parse(patchText); + expect(() => diff.free(), returnsNormally); + }); + + test('manually releases allocated memory for DiffStats object', () { + final stats = Diff.parse(patchText).stats; + expect(() => stats.free(), returnsNormally); }); test( @@ -749,10 +608,6 @@ index e69de29..c217c63 100644 expect(patch.hunks[0].toString(), contains('DiffHunk{')); expect(patch.hunks[0].lines[0].toString(), contains('DiffLine{')); expect(stats.toString(), contains('DiffStats{')); - - stats.free(); - patch.free(); - diff.free(); }); }); } diff --git a/test/index_test.dart b/test/index_test.dart index 1834251..409023d 100644 --- a/test/index_test.dart +++ b/test/index_test.dart @@ -20,7 +20,6 @@ void main() { }); tearDown(() { - index.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -152,11 +151,8 @@ void main() { final bare = Repository.open( p.join('test', 'assets', 'empty_bare.git'), ); - final bareIndex = bare.index; + expect(() => bare.index.add('config'), throwsA(isA())); - expect(() => bareIndex.add('config'), throwsA(isA())); - - bareIndex.free(); bare.free(); }); }); @@ -173,9 +169,8 @@ void main() { }); test('throws when trying to update entry and error occurs', () { - final nullEntry = IndexEntry(nullptr); expect( - () => index.addFromBuffer(entry: nullEntry, buffer: ''), + () => index.addFromBuffer(entry: IndexEntry(nullptr), buffer: ''), throwsA(isA()), ); }); @@ -208,11 +203,8 @@ void main() { final bare = Repository.open( p.join('test', 'assets', 'empty_bare.git'), ); - final bareIndex = bare.index; + expect(() => bare.index.addAll([]), throwsA(isA())); - expect(() => bareIndex.addAll([]), throwsA(isA())); - - bareIndex.free(); bare.free(); }); }); @@ -234,14 +226,11 @@ void main() { final bare = Repository.open( p.join('test', 'assets', 'empty_bare.git'), ); - final bareIndex = bare.index; - expect( - () => bareIndex.updateAll(['not_there']), + () => bare.index.updateAll(['not_there']), throwsA(isA()), ); - bareIndex.free(); bare.free(); }); }); @@ -321,7 +310,6 @@ void main() { final repo = Repository.open(tmpDir.path); final conflictBranch = Branch.lookup(repo: repo, name: 'conflict-branch'); - final index = repo.index; final commit = AnnotatedCommit.lookup( repo: repo, oid: conflictBranch.target, @@ -329,11 +317,8 @@ void main() { Merge.commit(repo: repo, commit: commit); - expect(() => index.writeTree(), throwsA(isA())); + expect(() => repo.index.writeTree(), throwsA(isA())); - commit.free(); - conflictBranch.free(); - index.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -370,16 +355,12 @@ void main() { Merge.commit(repo: conflictRepo, commit: commit); - final index = conflictRepo.index; - final conflictedFile = index.conflicts['feature_file']!; + final conflictedFile = conflictRepo.index.conflicts['feature_file']!; expect(conflictedFile.ancestor?.path, 'feature_file'); expect(conflictedFile.our?.path, 'feature_file'); expect(conflictedFile.their?.path, 'feature_file'); expect(conflictedFile.toString(), contains('ConflictEntry{')); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -399,16 +380,12 @@ void main() { Merge.commit(repo: conflictRepo, commit: commit); - final index = conflictRepo.index; - final conflictedFile = index.conflicts['conflict_file']!; + final conflictedFile = conflictRepo.index.conflicts['conflict_file']!; expect(conflictedFile.ancestor?.path, null); expect(conflictedFile.our?.path, 'conflict_file'); expect(conflictedFile.their?.path, 'conflict_file'); expect(conflictedFile.toString(), contains('ConflictEntry{')); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -431,16 +408,12 @@ void main() { Merge.commit(repo: conflictRepo, commit: commit); - final index = conflictRepo.index; - final conflictedFile = index.conflicts['feature_file']!; + final conflictedFile = conflictRepo.index.conflicts['feature_file']!; expect(conflictedFile.ancestor?.path, 'feature_file'); expect(conflictedFile.our?.path, null); expect(conflictedFile.their?.path, 'feature_file'); expect(conflictedFile.toString(), contains('ConflictEntry{')); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -463,16 +436,12 @@ void main() { Merge.commit(repo: conflictRepo, commit: commit); - final index = conflictRepo.index; - final conflictedFile = index.conflicts['feature_file']!; + final conflictedFile = conflictRepo.index.conflicts['feature_file']!; expect(conflictedFile.ancestor?.path, 'feature_file'); expect(conflictedFile.our?.path, 'feature_file'); expect(conflictedFile.their?.path, null); expect(conflictedFile.toString(), contains('ConflictEntry{')); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -492,6 +461,7 @@ void main() { final index = conflictRepo.index; Merge.commit(repo: conflictRepo, commit: commit); + expect(index.hasConflicts, true); expect(index['.gitignore'].isConflict, false); expect(index.conflicts['conflict_file']!.our!.isConflict, true); @@ -499,13 +469,11 @@ void main() { final conflictedFile = index.conflicts['conflict_file']!; conflictedFile.remove(); + expect(index.hasConflicts, false); expect(index.conflicts, isEmpty); expect(index.conflicts['conflict_file'], null); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -533,16 +501,15 @@ void main() { final index = conflictRepo.index; Merge.commit(repo: conflictRepo, commit: commit); + expect(index.hasConflicts, true); expect(index.conflicts.length, 1); index.cleanupConflict(); + expect(index.hasConflicts, false); expect(index.conflicts, isEmpty); - index.free(); - commit.free(); - conflictBranch.free(); conflictRepo.free(); repoDir.deleteSync(recursive: true); }); @@ -554,13 +521,15 @@ void main() { ); }); + test('manually releases allocated memory', () { + expect(() => repo.index.free(), returnsNormally); + }); + test('returns string representation of Index and IndexEntry objects', () { final index = repo.index; expect(index.toString(), contains('Index{')); expect(index['file'].toString(), contains('IndexEntry{')); - - index.free(); }); }); } diff --git a/test/mailmap_test.dart b/test/mailmap_test.dart index 4a0830f..51ed458 100644 --- a/test/mailmap_test.dart +++ b/test/mailmap_test.dart @@ -142,10 +142,7 @@ Santa Claus group('Mailmap', () { test('initializes empty mailmap object', () { - final empty = Mailmap.empty(); - expect(empty, isA()); - - empty.free(); + expect(Mailmap.empty(), isA()); }); test('initializes from provided buffer', () { @@ -158,8 +155,6 @@ Santa Claus [entry['realName'], entry['realEmail']], ); } - - mailmap.free(); }); test('initializes from repository', () { @@ -172,8 +167,6 @@ Santa Claus [entry['realName'], entry['realEmail']], ); } - - mailmap.free(); }); test('throws when initializing from repository and error occurs', () { @@ -192,8 +185,6 @@ Santa Claus [entry['name'], entry['email']], ); } - - mailmap.free(); }); test('adds entries and resolves them', () { @@ -214,21 +205,15 @@ Santa Claus [entry['realName'], entry['realEmail']], ); } - - mailmap.free(); }); test('throws when trying to add entry with empty replace email', () { - final mailmap = Mailmap.empty(); - expect( - () => mailmap.addEntry( + () => Mailmap.empty().addEntry( replaceEmail: ' ', ), throwsA(isA()), ); - - mailmap.free(); }); test('resolves signature', () { @@ -249,8 +234,10 @@ Santa Claus ); expect(mailmap.resolveSignature(signature), realSignature); + }); - mailmap.free(); + test('manually releases allocated memory', () { + expect(() => Mailmap.empty().free(), returnsNormally); }); }); } diff --git a/test/merge_test.dart b/test/merge_test.dart index 7de6a58..db98711 100644 --- a/test/merge_test.dart +++ b/test/merge_test.dart @@ -62,9 +62,6 @@ void main() { {GitMergeAnalysis.fastForward, GitMergeAnalysis.normal}, ); expect(repo.status, isEmpty); - - ffBranch.free(); - ffCommit.free(); }); test('is not fast forward and there is no conflicts', () { @@ -114,10 +111,6 @@ void main() { 'conflict_file': {GitStatus.indexModified} }, ); - - index.free(); - commit.free(); - conflictBranch.free(); }); group('merge file from index', () { @@ -150,10 +143,6 @@ conflict branch edit ); expect(diff, diffExpected); - - index.free(); - commit.free(); - conflictBranch.free(); }); test('merges with ancestor', () { @@ -187,10 +176,6 @@ Another feature edit ); expect(diff, diffExpected); - - index.free(); - commit.free(); - conflictBranch.free(); }); test('merges with provided merge flags and file flags', () { @@ -227,10 +212,6 @@ conflict branch edit ); expect(diff, diffExpected); - - index.free(); - commit.free(); - conflictBranch.free(); }); test('merges with provided merge favor', () { @@ -251,10 +232,6 @@ conflict branch edit File(p.join(repo.workdir, 'conflict_file')).readAsStringSync(), 'master conflict edit\n', ); - - index.free(); - commit.free(); - conflictBranch.free(); }); test('throws when error occurs', () { @@ -348,12 +325,6 @@ theirs content final mergeTree = index.writeTree(); expect(mergeCommitsTree == mergeTree, true); - - index.free(); - mergeIndex.free(); - ourCommit.free(); - theirCommitAnnotated.free(); - theirCommit.free(); }); test('merges with provided favor', () { @@ -367,10 +338,6 @@ theirs content favor: GitMergeFileFavor.ours, ); expect(mergeIndex.conflicts, isEmpty); - - mergeIndex.free(); - ourCommit.free(); - theirCommit.free(); }); test('merges with provided merge and file flags', () { @@ -392,10 +359,6 @@ theirs content }, ); expect(mergeIndex.conflicts, isEmpty); - - mergeIndex.free(); - ourCommit.free(); - theirCommit.free(); }); test('throws when error occurs', () { @@ -489,15 +452,12 @@ theirs content commits: [ourCommit.oid, theirCommit.oid], ), ); - final theirTree = theirCommit.tree; - final ourTree = ourCommit.tree; - final ancestorTree = baseCommit.tree; final mergeIndex = Merge.trees( repo: repo, - ancestorTree: ancestorTree, - ourTree: ourTree, - theirTree: theirTree, + ancestorTree: baseCommit.tree, + ourTree: ourCommit.tree, + theirTree: theirCommit.tree, ); expect(mergeIndex.conflicts, isEmpty); final mergeTreesTree = mergeIndex.writeTree(repo); @@ -509,16 +469,6 @@ theirs content final mergeTree = index.writeTree(); expect(mergeTreesTree == mergeTree, true); - - index.free(); - mergeIndex.free(); - ancestorTree.free(); - ourTree.free(); - theirTree.free(); - baseCommit.free(); - ourCommit.free(); - theirCommitAnnotated.free(); - theirCommit.free(); }); test('merges with provided favor', () { @@ -531,26 +481,15 @@ theirs content commits: [ourCommit.oid, theirCommit.oid], ), ); - final theirTree = theirCommit.tree; - final ourTree = ourCommit.tree; - final ancestorTree = baseCommit.tree; final mergeIndex = Merge.trees( repo: repo, - ancestorTree: ancestorTree, - ourTree: ourTree, - theirTree: theirTree, + ancestorTree: baseCommit.tree, + ourTree: ourCommit.tree, + theirTree: theirCommit.tree, favor: GitMergeFileFavor.ours, ); expect(mergeIndex.conflicts, isEmpty); - - mergeIndex.free(); - ancestorTree.free(); - ourTree.free(); - theirTree.free(); - baseCommit.free(); - ourCommit.free(); - theirCommit.free(); }); test('throws when error occurs', () { @@ -580,8 +519,6 @@ theirs content () => repo.message, throwsA(isA()), ); - - index.free(); }); test('throws when error occurs', () { diff --git a/test/note_test.dart b/test/note_test.dart index 7ad268a..ff749c1 100644 --- a/test/note_test.dart +++ b/test/note_test.dart @@ -43,7 +43,6 @@ void main() { expect(notes[i].oid.sha, notesExpected[i]['oid']); expect(notes[i].message, notesExpected[i]['message']); expect(notes[i].annotatedOid.sha, notesExpected[i]['annotatedOid']); - notes[i].free(); } }); @@ -53,15 +52,11 @@ void main() { }); test('lookups note', () { - final head = repo.head; - final note = Note.lookup(repo: repo, annotatedOid: head.target); + final note = Note.lookup(repo: repo, annotatedOid: repo.head.target); expect(note.oid.sha, notesExpected[1]['oid']); expect(note.message, notesExpected[1]['message']); expect(note.annotatedOid.sha, notesExpected[1]['annotatedOid']); - - note.free(); - head.free(); }); test('creates note', () { @@ -70,12 +65,11 @@ void main() { email: 'author@email.com', time: 1234, ); - final head = repo.head; final noteOid = Note.create( repo: repo, author: signature, committer: signature, - annotatedOid: head.target, + annotatedOid: repo.head.target, note: 'New note for HEAD', force: true, ); @@ -83,10 +77,6 @@ void main() { expect(noteOid.sha, 'ffd6e2ceaf91c00ea6d29e2e897f906da720529f'); expect(noteBlob.content, 'New note for HEAD'); - - noteBlob.free(); - head.free(); - signature.free(); }); test('throws when trying to create note and error occurs', () { @@ -108,7 +98,6 @@ void main() { email: 'author@email.com', time: 1234, ); - final head = repo.head; Note.delete( repo: repo, @@ -118,12 +107,9 @@ void main() { ); expect( - () => Note.lookup(repo: repo, annotatedOid: head.target), + () => Note.lookup(repo: repo, annotatedOid: repo.head.target), throwsA(isA()), ); - - head.free(); - signature.free(); }); test('throws when trying to delete note and error occurs', () { @@ -138,10 +124,14 @@ void main() { ); }); + test('manually releases allocated memory', () { + final note = Note.lookup(repo: repo, annotatedOid: repo['821ed6e']); + expect(() => note.free(), returnsNormally); + }); + test('returns string representation of Note object', () { final note = Note.lookup(repo: repo, annotatedOid: repo['821ed6e']); expect(note.toString(), contains('Note{')); - note.free(); }); }); } diff --git a/test/odb_test.dart b/test/odb_test.dart index 51f989a..ac0065d 100644 --- a/test/odb_test.dart +++ b/test/odb_test.dart @@ -26,9 +26,7 @@ void main() { group('Odb', () { test('successfully initializes', () { - final odb = repo.odb; - expect(odb, isA()); - odb.free(); + expect(repo.odb, isA()); }); test('throws when trying to get odb and error occurs', () { @@ -36,9 +34,7 @@ void main() { }); test('creates new odb with no backends', () { - final odb = Odb.create(); - expect(odb, isA()); - odb.free(); + expect(Odb.create(), isA()); }); test('adds disk alternate', () { @@ -46,33 +42,23 @@ void main() { odb.addDiskAlternate(p.join(repo.path, 'objects')); expect(odb.contains(repo[blobSha]), true); - - odb.free(); }); test('reads object', () { final oid = repo[blobSha]; - final odb = repo.odb; - final object = odb.read(oid); + final object = repo.odb.read(oid); expect(object.oid, oid); expect(object.type, GitObject.blob); expect(object.data, blobContent); expect(object.size, 13); - - object.free(); - odb.free(); }); test('throws when trying to read object and error occurs', () { - final odb = repo.odb; - expect( - () => odb.read(repo['0' * 40]), + () => repo.odb.read(repo['0' * 40]), throwsA(isA()), ); - - odb.free(); }); test("returns list of all objects oid's in database", () { @@ -80,8 +66,6 @@ void main() { expect(odb.objects, isNot(isEmpty)); expect(odb.objects.contains(repo[commitSha]), true); - - odb.free(); }); test('throws when trying to get list of all objects and error occurs', () { @@ -92,8 +76,6 @@ void main() { () => odb.objects, throwsA(isA()), ); - - odb.free(); }); test('writes data', () { @@ -103,19 +85,13 @@ void main() { expect(odb.contains(oid), true); expect(object.data, 'testing'); - - object.free(); - odb.free(); }); test('throws when trying to write with invalid object type', () { - final odb = repo.odb; expect( - () => odb.write(type: GitObject.any, data: 'testing'), + () => repo.odb.write(type: GitObject.any, data: 'testing'), throwsA(isA()), ); - - odb.free(); }); test('throws when trying to write alternate odb to disk', () { @@ -126,15 +102,20 @@ void main() { () => odb.write(type: GitObject.blob, data: ''), throwsA(isA()), ); + }); - odb.free(); + test('manually releases allocated memory', () { + expect(() => repo.odb.free(), returnsNormally); + }); + + test('manually releases allocated memory for odbObject object', () { + final object = repo.odb.read(repo[blobSha]); + expect(() => object.free(), returnsNormally); }); test('returns string representation of OdbObject object', () { - final odb = repo.odb; - final object = odb.read(repo[blobSha]); + final object = repo.odb.read(repo[blobSha]); expect(object.toString(), contains('OdbObject{')); - odb.free(); }); }); } diff --git a/test/packbuilder_test.dart b/test/packbuilder_test.dart index 4a3d60d..172b173 100644 --- a/test/packbuilder_test.dart +++ b/test/packbuilder_test.dart @@ -29,8 +29,6 @@ void main() { expect(packbuilder, isA()); expect(packbuilder.length, 0); - - packbuilder.free(); }); test('throws when trying to initialize and error occurs', () { @@ -49,20 +47,13 @@ void main() { packbuilder.add(odb.objects[1]); expect(packbuilder.length, 2); - - odb.free(); - packbuilder.free(); }); test('throws when trying to add object and error occurs', () { - final packbuilder = PackBuilder(repo); - expect( - () => packbuilder.add(Oid(nullptr)), + () => PackBuilder(repo).add(Oid(nullptr)), throwsA(isA()), ); - - packbuilder.free(); }); test('adds object recursively', () { @@ -71,19 +62,13 @@ void main() { packbuilder.addRecursively(oid); expect(packbuilder.length, 3); - - packbuilder.free(); }); test('throws when trying to add object recursively and error occurs', () { - final packbuilder = PackBuilder(repo); - expect( - () => packbuilder.addRecursively(Oid(nullptr)), + () => PackBuilder(repo).addRecursively(Oid(nullptr)), throwsA(isA()), ); - - packbuilder.free(); }); test('adds commit', () { @@ -92,17 +77,15 @@ void main() { packbuilder.addCommit(oid); expect(packbuilder.length, 3); - - packbuilder.free(); }); test('throws when trying to add commit with invalid oid', () { - final packbuilder = PackBuilder(repo); final oid = Oid.fromSHA(repo: repo, sha: '0' * 40); - expect(() => packbuilder.addCommit(oid), throwsA(isA())); - - packbuilder.free(); + expect( + () => PackBuilder(repo).addCommit(oid), + throwsA(isA()), + ); }); test('adds tree', () { @@ -111,17 +94,15 @@ void main() { packbuilder.addTree(oid); expect(packbuilder.length, 2); - - packbuilder.free(); }); test('throws when trying to add tree with invalid oid', () { - final packbuilder = PackBuilder(repo); final oid = Oid.fromSHA(repo: repo, sha: '0' * 40); - expect(() => packbuilder.addTree(oid), throwsA(isA())); - - packbuilder.free(); + expect( + () => PackBuilder(repo).addTree(oid), + throwsA(isA()), + ); }); test('adds objects with walker', () { @@ -133,48 +114,34 @@ void main() { packbuilder.addWalk(walker); expect(packbuilder.length, 3); - - walker.free(); - packbuilder.free(); }); test('sets number of threads', () { - final packbuilder = PackBuilder(repo); - - expect(packbuilder.setThreads(1), 1); - - packbuilder.free(); + expect(PackBuilder(repo).setThreads(1), 1); }); test('returns name of packfile', () { final packbuilder = PackBuilder(repo); - final odb = repo.odb; - packbuilder.add(odb.objects[0]); + packbuilder.add(repo.odb.objects[0]); Directory(packDirPath).createSync(); expect(packbuilder.name, isEmpty); packbuilder.write(null); expect(packbuilder.name, isNotEmpty); - - packbuilder.free(); }); test('packs with default arguments', () { - final odb = repo.odb; - final objectsCount = odb.objects.length; + final objectsCount = repo.odb.objects.length; Directory(packDirPath).createSync(); final writtenCount = repo.pack(); expect(writtenCount, objectsCount); - - odb.free(); }); test('packs into provided path with threads set', () { - final odb = repo.odb; - final objectsCount = odb.objects.length; + final objectsCount = repo.odb.objects.length; final testPackPath = p.join(repo.workdir, 'test-pack'); Directory(testPackPath).createSync(); @@ -182,8 +149,6 @@ void main() { expect(writtenCount, objectsCount); expect(Directory(testPackPath).listSync().isNotEmpty, true); - - odb.free(); }); test('packs with provided packDelegate', () { @@ -198,10 +163,7 @@ void main() { ); for (final commit in repo.log(oid: ref.target)) { packBuilder.addRecursively(commit.oid); - commit.free(); } - ref.free(); - branch.free(); } } @@ -210,20 +172,18 @@ void main() { }); test('throws when trying to write pack into invalid path', () { - final packbuilder = PackBuilder(repo); - expect( - () => packbuilder.write('invalid/path'), + () => PackBuilder(repo).write('invalid/path'), throwsA(isA()), ); + }); - packbuilder.free(); + test('manually releases allocated memory', () { + expect(() => PackBuilder(repo).free(), returnsNormally); }); test('returns string representation of PackBuilder object', () { - final packbuilder = PackBuilder(repo); - expect(packbuilder.toString(), contains('PackBuilder{')); - packbuilder.free(); + expect(PackBuilder(repo).toString(), contains('PackBuilder{')); }); }); } diff --git a/test/patch_test.dart b/test/patch_test.dart index 81f8e38..0c0959d 100644 --- a/test/patch_test.dart +++ b/test/patch_test.dart @@ -65,8 +65,6 @@ index e69de29..0000000 expect(patch.size(), 14); expect(patch.text, blobPatch); - - patch.free(); }); test('creates from one buffer (add)', () { @@ -78,8 +76,6 @@ index e69de29..0000000 ); expect(patch.text, blobPatchAdd); - - patch.free(); }); test('creates from one buffer (delete)', () { @@ -91,8 +87,6 @@ index e69de29..0000000 ); expect(patch.text, blobPatchDelete); - - patch.free(); }); test('creates from blobs', () { @@ -106,8 +100,6 @@ index e69de29..0000000 ); expect(patch.text, blobPatch); - - patch.free(); }); test('creates from one blob (add)', () { @@ -120,8 +112,6 @@ index e69de29..0000000 ); expect(patch.text, blobPatchAdd); - - patch.free(); }); test('creates from one blob (delete)', () { @@ -134,8 +124,6 @@ index e69de29..0000000 ); expect(patch.text, blobPatchDelete); - - patch.free(); }); test('creates from blob and buffer', () { @@ -148,8 +136,6 @@ index e69de29..0000000 ); expect(patch.text, blobPatch); - - patch.free(); }); test('creates from empty blob and buffer', () { @@ -161,8 +147,6 @@ index e69de29..0000000 ); expect(patch.text, blobPatchAdd); - - patch.free(); }); test('throws when trying to create from diff and error occurs', () { @@ -189,8 +173,16 @@ index e69de29..0000000 expect(stats.insertions, equals(1)); expect(stats.deletions, equals(0)); expect(stats.toString(), contains('PatchStats{')); + }); - patch.free(); + test('manually releases allocated memory', () { + final patch = Patch.fromBuffers( + oldBuffer: oldBuffer, + newBuffer: newBuffer, + oldBufferPath: path, + newBufferPath: path, + ); + expect(() => patch.free(), returnsNormally); }); test('returns string representation of Patch object', () { @@ -202,8 +194,6 @@ index e69de29..0000000 ); expect(patch.toString(), contains('Patch{')); - - patch.free(); }); }); } diff --git a/test/rebase_test.dart b/test/rebase_test.dart index f63bb78..0348277 100644 --- a/test/rebase_test.dart +++ b/test/rebase_test.dart @@ -76,13 +76,6 @@ void main() { rebase.finish(); expect(repo.index['.gitignore'], isA()); - - rebase.free(); - ontoHead.free(); - branchHead.free(); - feature.free(); - master.free(); - signature.free(); }); test('performs rebase without branch provided', () { @@ -124,11 +117,6 @@ void main() { } rebase.finish(); - - rebase.free(); - ontoHead.free(); - feature.free(); - signature.free(); }); test('performs rebase with provided upstream', () { @@ -170,13 +158,6 @@ void main() { rebase.finish(); expect(repo.index['conflict_file'], isA()); - - rebase.free(); - upstream.free(); - branchHead.free(); - feature.free(); - master.free(); - signature.free(); }); test( @@ -216,22 +197,10 @@ void main() { () => rebase.commit(committer: signature), throwsA(isA()), ); - - rebase.free(); - ontoHead.free(); - branchHead.free(); - conflict.free(); - master.free(); - signature.free(); }); test('throws when trying to perfrom next rebase operation and error occurs', () { - final signature = Signature.create( - name: 'Author', - email: 'author@email.com', - time: 1234, - ); final master = Reference.lookup(repo: repo, name: 'refs/heads/master'); final branchHead = AnnotatedCommit.lookup(repo: repo, oid: master.target); final conflict = Reference.lookup( @@ -252,13 +221,6 @@ void main() { rebase.next(); // repo now have conflicts expect(() => rebase.next(), throwsA(isA())); - - rebase.free(); - ontoHead.free(); - branchHead.free(); - conflict.free(); - master.free(); - signature.free(); }); test('aborts rebase in progress', () { @@ -287,12 +249,6 @@ void main() { rebase.abort(); expect(repo.status, isEmpty); expect(repo.state, GitRepositoryState.none); - - rebase.free(); - ontoHead.free(); - branchHead.free(); - conflict.free(); - master.free(); }); test('opens an existing rebase', () { @@ -307,15 +263,21 @@ void main() { final openRebase = Rebase.open(repo); expect(openRebase.operations.length, 3); - - openRebase.free(); - rebase.free(); - ontoHead.free(); - feature.free(); }); test('throws when trying to open an existing rebase but there is none', () { expect(() => Rebase.open(repo), throwsA(isA())); }); + + test('manually releases allocated memory', () { + final feature = Reference.lookup(repo: repo, name: 'refs/heads/feature'); + final ontoHead = AnnotatedCommit.lookup(repo: repo, oid: feature.target); + final rebase = Rebase.init( + repo: repo, + onto: ontoHead, + ); + + expect(() => rebase.free(), returnsNormally); + }); }); } diff --git a/test/reference_test.dart b/test/reference_test.dart index 3c1f436..6f06585 100644 --- a/test/reference_test.dart +++ b/test/reference_test.dart @@ -46,25 +46,19 @@ void main() { }); test('returns correct type of reference', () { - final head = repo.head; - expect(head.type, ReferenceType.direct); - head.free(); + expect(repo.head.type, ReferenceType.direct); final ref = Reference.lookup(repo: repo, name: 'HEAD'); expect(ref.type, ReferenceType.symbolic); - ref.free(); }); test('returns SHA hex of direct reference', () { - final head = repo.head; - expect(head.target.sha, lastCommit); - head.free(); + expect(repo.head.target.sha, lastCommit); }); test('returns SHA hex of symbolic reference', () { final ref = Reference.lookup(repo: repo, name: 'HEAD'); expect(ref.target.sha, lastCommit); - ref.free(); }); test('throws when trying to resolve invalid reference', () { @@ -75,9 +69,7 @@ void main() { }); test('returns the full name', () { - final head = repo.head; - expect(head.name, 'refs/heads/master'); - head.free(); + expect(repo.head.name, 'refs/heads/master'); }); test('returns the short name', () { @@ -87,25 +79,18 @@ void main() { target: repo[lastCommit], ); - final head = repo.head; - - expect(head.shorthand, 'master'); + expect(repo.head.shorthand, 'master'); expect(ref.shorthand, 'origin/upstream'); - - head.free(); - ref.free(); }); test('checks if reference is a local branch', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/feature'); expect(ref.isBranch, true); - ref.free(); }); test('checks if reference is a note', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); expect(ref.isNote, false); - ref.free(); }); test('checks if reference is a remote branch', () { @@ -114,13 +99,11 @@ void main() { name: 'refs/remotes/origin/master', ); expect(ref.isRemote, true); - ref.free(); }); test('checks if reference is a tag', () { final ref = Reference.lookup(repo: repo, name: 'refs/tags/v0.1'); expect(ref.isTag, true); - ref.free(); }); test('checks if reflog exists for the reference', () { @@ -129,8 +112,6 @@ void main() { ref = Reference.lookup(repo: repo, name: 'refs/tags/v0.1'); expect(ref.hasLog, false); - - ref.free(); }); test('ensures updates to the reference will append to its log', () { @@ -141,12 +122,8 @@ void main() { name: 'refs/tags/tag', target: repo[lastCommit], ); - final reflog = ref.log; - expect(reflog.length, 1); - - reflog.free(); - ref.free(); + expect(ref.log.length, 1); }); test('throws when trying to ensure there is a reflog and error occurs', () { @@ -167,24 +144,19 @@ void main() { expect(repo.references.length, 6); expect(duplicate.equals(ref), true); - - duplicate.free(); - ref.free(); }); group('create direct', () { test('creates with oid as target', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final refFromOid = Reference.create( + + Reference.create( repo: repo, name: 'refs/tags/from.oid', target: ref.target, ); expect(repo.references, contains('refs/tags/from.oid')); - - refFromOid.free(); - ref.free(); }); test('creates with log message', () { @@ -196,15 +168,11 @@ void main() { logMessage: 'log message', ); - final reflog = ref.log; - final reflogEntry = reflog[0]; + final reflogEntry = ref.log[0]; expect(reflogEntry.message, 'log message'); expect(reflogEntry.committer.name, 'name'); expect(reflogEntry.committer.email, 'email'); - - reflog.free(); - ref.free(); }); test('throws if target is not valid', () { @@ -239,7 +207,7 @@ void main() { }); test('creates with force flag if name already exists', () { - final ref = Reference.create( + Reference.create( repo: repo, name: 'refs/tags/test', target: repo[lastCommit], @@ -253,13 +221,10 @@ void main() { ); expect(forceRef.target.sha, lastCommit); - - ref.free(); - forceRef.free(); }); test('throws if name already exists', () { - final ref = Reference.create( + Reference.create( repo: repo, name: 'refs/tags/test', target: repo[lastCommit], @@ -273,8 +238,6 @@ void main() { ), throwsA(isA()), ); - - ref.free(); }); }); @@ -288,12 +251,10 @@ void main() { expect(repo.references, contains('refs/tags/symbolic')); expect(ref.type, ReferenceType.symbolic); - - ref.free(); }); test('creates with force flag if name already exists', () { - final ref = Reference.create( + Reference.create( repo: repo, name: 'refs/tags/test', target: 'refs/heads/master', @@ -308,13 +269,10 @@ void main() { expect(forceRef.target.sha, lastCommit); expect(forceRef.type, ReferenceType.symbolic); - - ref.free(); - forceRef.free(); }); test('throws if name already exists', () { - final ref = Reference.create( + Reference.create( repo: repo, name: 'refs/tags/exists', target: 'refs/heads/master', @@ -328,8 +286,6 @@ void main() { ), throwsA(isA()), ); - - ref.free(); }); test('throws if name is not valid', () { @@ -353,15 +309,11 @@ void main() { logMessage: 'log message', ); - final reflog = ref.log; - final reflogEntry = reflog[0]; + final reflogEntry = ref.log[0]; expect(reflogEntry.message, 'log message'); expect(reflogEntry.committer.name, 'name'); expect(reflogEntry.committer.email, 'email'); - - reflog.free(); - ref.free(); }); }); @@ -376,7 +328,6 @@ void main() { test('with provided name', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); expect(ref.target.sha, lastCommit); - ref.free(); }); test('throws when error occured', () { @@ -389,11 +340,7 @@ void main() { test('returns log for reference', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final reflog = ref.log; - expect(reflog.last.message, 'commit (initial): init'); - - reflog.free(); - ref.free(); + expect(ref.log.last.message, 'commit (initial): init'); }); group('set target', () { @@ -401,8 +348,6 @@ void main() { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); ref.setTarget(target: repo[newCommit]); expect(ref.target.sha, newCommit); - - ref.free(); }); test('sets symbolic target with provided reference name', () { @@ -411,8 +356,6 @@ void main() { ref.setTarget(target: 'refs/heads/feature'); expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); - - ref.free(); }); test('sets target with log message', () { @@ -422,13 +365,10 @@ void main() { repo.setIdentity(name: 'name', email: 'email'); ref.setTarget(target: 'refs/heads/feature', logMessage: 'log message'); expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); - final reflog = ref.log; - expect(reflog.first.message, 'log message'); - expect(reflog.first.committer.name, 'name'); - expect(reflog.first.committer.email, 'email'); - - reflog.free(); - ref.free(); + final logEntry = ref.log.first; + expect(logEntry.message, 'log message'); + expect(logEntry.committer.name, 'name'); + expect(logEntry.committer.email, 'email'); }); test('throws on invalid target', () { @@ -444,8 +384,6 @@ void main() { ); expect(() => ref.setTarget(target: 0), throwsA(isA())); - - ref.free(); }); }); @@ -508,9 +446,6 @@ void main() { ); expect(repo.references, isNot(contains('refs/tags/v0.1'))); expect(repo.references.length, 5); - - ref1.free(); - ref2.free(); }); }); @@ -523,10 +458,6 @@ void main() { expect(ref1.notEquals(ref2), false); expect(ref1.equals(ref3), false); expect(ref1.notEquals(ref3), true); - - ref1.free(); - ref2.free(); - ref3.free(); }); test('peels to non-tag object when no type is provided', () { @@ -535,10 +466,6 @@ void main() { final peeled = ref.peel() as Commit; expect(peeled.oid, commit.oid); - - peeled.free(); - commit.free(); - ref.free(); }); test('peels to object of provided type', () { @@ -562,17 +489,6 @@ void main() { expect(peeledTree.oid, tree.oid); expect(peeledBlob.content, 'Feature edit\n'); expect(peeledTag.name, 'v0.2'); - - peeledTag.free(); - peeledBlob.free(); - peeledTree.free(); - peeledCommit.free(); - tagRef.free(); - blobRef.free(); - blob.free(); - commit.free(); - tree.free(); - ref.free(); }); test('throws when trying to peel and error occurs', () { @@ -598,10 +514,14 @@ void main() { ); }); + test('manually releases allocated memory', () { + final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); + expect(() => ref.free(), returnsNormally); + }); + test('returns string representation of Reference object', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); expect(ref.toString(), contains('Reference{')); - ref.free(); }); }); } diff --git a/test/reflog_test.dart b/test/reflog_test.dart index d7dad43..021d1f8 100644 --- a/test/reflog_test.dart +++ b/test/reflog_test.dart @@ -10,19 +10,15 @@ import 'helpers/util.dart'; void main() { late Repository repo; late RefLog reflog; - late Reference head; late Directory tmpDir; setUp(() { tmpDir = setupRepo(Directory(p.join('test', 'assets', 'test_repo'))); repo = Repository.open(tmpDir.path); - head = repo.head; - reflog = RefLog(head); + reflog = RefLog(repo.head); }); tearDown(() { - reflog.free(); - head.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -52,9 +48,9 @@ void main() { }); test('deletes the reflog of provided reference', () { - expect(head.hasLog, true); - RefLog.delete(head); - expect(head.hasLog, false); + expect(repo.head.hasLog, true); + RefLog.delete(repo.head); + expect(repo.head.hasLog, false); }); test('renames existing reflog', () { @@ -94,19 +90,21 @@ void main() { ); expect(reflog.length, 4); - reflog.add(oid: head.target, committer: committer); + reflog.add(oid: repo.head.target, committer: committer); expect(reflog.length, 5); - reflog.add(oid: head.target, committer: committer, message: 'new entry'); + reflog.add( + oid: repo.head.target, + committer: committer, + message: 'new entry', + ); expect(reflog.length, 6); expect(reflog[0].message, 'new entry'); - - committer.free(); }); test('throws when trying to add new entry', () { expect( - () => reflog.add(oid: head.target, committer: Signature(nullptr)), + () => reflog.add(oid: repo.head.target, committer: Signature(nullptr)), throwsA(isA()), ); }); @@ -132,12 +130,12 @@ void main() { reflog.remove(0); // making sure change is only in memory - final oldReflog = RefLog(head); + final oldReflog = RefLog(repo.head); expect(oldReflog.length, 4); reflog.write(); - final newReflog = RefLog(head); + final newReflog = RefLog(repo.head); expect(newReflog.length, 3); }); @@ -147,9 +145,10 @@ void main() { Reference.delete(repo: repo, name: ref.name); expect(() => reflog.write(), throwsA(isA())); + }); - reflog.free(); - ref.free(); + test('manually releases allocated memory', () { + expect(() => RefLog(repo.head).free(), returnsNormally); }); test('returns string representation of RefLogEntry object', () { diff --git a/test/remote_prune_test.dart b/test/remote_prune_test.dart index 7b435fa..d91da24 100644 --- a/test/remote_prune_test.dart +++ b/test/remote_prune_test.dart @@ -30,7 +30,6 @@ void main() { }); tearDown(() { - remote.free(); originRepo.free(); clonedRepo.free(); tmpDir.deleteSync(recursive: true); @@ -42,34 +41,28 @@ void main() { test('fetch() does not prune branch by default', () { remote.fetch(); - final branches = clonedRepo.branches; - expect(branches.any((branch) => branch.name == 'origin/feature'), true); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + true, + ); }); test('fetch() prunes branch with provided flag', () { remote.fetch(prune: GitFetchPrune.prune); - final branches = clonedRepo.branches; - expect(branches.any((branch) => branch.name == 'origin/feature'), false); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + false, + ); }); test('fetch() does not prune branch with provided flag', () { remote.fetch(prune: GitFetchPrune.noPrune); - final branches = clonedRepo.branches; - expect(branches.any((branch) => branch.name == 'origin/feature'), true); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + true, + ); }); test('prune() prunes branches', () { @@ -81,22 +74,18 @@ void main() { final callbacks = Callbacks(updateTips: updateTips); remote.fetch(prune: GitFetchPrune.noPrune); - var branches = clonedRepo.branches; - expect(branches.any((branch) => branch.name == 'origin/feature'), true); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + true, + ); remote.prune(callbacks); - branches = clonedRepo.branches; expect(pruned, contains('refs/remotes/origin/feature')); - expect(branches.any((branch) => branch.name == 'origin/feature'), false); - - for (final branch in branches) { - branch.free(); - } + expect( + clonedRepo.branches.any((branch) => branch.name == 'origin/feature'), + false, + ); }); test( diff --git a/test/remote_test.dart b/test/remote_test.dart index 0b865f6..d82e793 100644 --- a/test/remote_test.dart +++ b/test/remote_test.dart @@ -34,8 +34,6 @@ void main() { expect(remote.url, remoteUrl); expect(remote.pushUrl, ''); expect(remote.toString(), contains('Remote{')); - - remote.free(); }); test('throws when provided name for lookup is not found', () { @@ -56,8 +54,6 @@ void main() { expect(remote.name, 'upstream'); expect(remote.url, remoteUrl); expect(remote.pushUrl, ''); - - remote.free(); }); test('creates with provided fetchspec', () { @@ -74,8 +70,6 @@ void main() { expect(remote.url, remoteUrl); expect(remote.pushUrl, ''); expect(remote.fetchRefspecs, [spec]); - - remote.free(); }); test('throws when trying to create with fetchspec with invalid remote name', @@ -101,8 +95,6 @@ void main() { Remote.delete(repo: repo, name: remote.name); expect(repo.remotes.length, 1); - - remote.free(); }); test('throws when trying to delete non existing remote', () { @@ -125,9 +117,6 @@ void main() { final newRemote = Remote.lookup(repo: repo, name: 'new'); expect(newRemote.name, 'new'); - - newRemote.free(); - remote.free(); }); test('returns list of non-default refspecs that cannot be renamed', () { @@ -142,8 +131,6 @@ void main() { Remote.rename(repo: repo, oldName: remote.name, newName: 'renamed'), ['+refs/*:refs/*'], ); - - remote.free(); }); test('throws when renaming with invalid names', () { @@ -162,9 +149,6 @@ void main() { final newRemote = Remote.lookup(repo: repo, name: remoteName); expect(newRemote.url, newUrl); - - newRemote.free(); - remote.free(); }); test('throws when trying to set invalid url name', () { @@ -180,8 +164,6 @@ void main() { final remote = Remote.lookup(repo: repo, name: remoteName); expect(remote.pushUrl, newUrl); - - remote.free(); }); test('throws when trying to set invalid push url name', () { @@ -215,8 +197,6 @@ void main() { refspec.rTransform('refs/remotes/origin/master'), 'refs/heads/master', ); - - remote.free(); }); test('throws when trying to transform refspec with invalid reference name', @@ -233,8 +213,6 @@ void main() { () => refspec.rTransform('invalid/name'), throwsA(isA()), ); - - remote.free(); }); test('adds fetch refspec', () { @@ -252,8 +230,6 @@ void main() { '+refs/test/*:refs/test/remotes/*', ], ); - - remote.free(); }); test('throws when trying to add fetch refspec for invalid remote name', () { @@ -276,8 +252,6 @@ void main() { final remote = Remote.lookup(repo: repo, name: 'origin'); expect(remote.pushRefspecs.length, 1); expect(remote.pushRefspecs, ['+refs/test/*:refs/test/remotes/*']); - - remote.free(); }); test('throws when trying to add push refspec for invalid remote name', () { @@ -308,8 +282,6 @@ void main() { (refs.first['oid']! as Oid).sha, '49322bb17d3acc9146f98c97d078513228bbf3c0', ); - - remote.free(); }); test( @@ -319,8 +291,6 @@ void main() { final remote = Remote.lookup(repo: repo, name: 'libgit2'); expect(() => remote.ls(), throwsA(isA())); - - remote.free(); }); test( @@ -350,8 +320,6 @@ void main() { expect(stats.indexedDeltas, 3); expect(stats.receivedBytes, 0); expect(stats.toString(), contains('TransferProgress{')); - - remote.free(); }, tags: 'remote_fetch', ); @@ -384,8 +352,6 @@ void main() { expect(stats.indexedDeltas, 3); expect(stats.receivedBytes, 0); expect(stats.toString(), contains('TransferProgress{')); - - remote.free(); }, tags: 'remote_fetch', ); @@ -412,8 +378,6 @@ void main() { ), throwsA(isA()), ); - - remote.free(); }, tags: 'remote_fetch', ); @@ -426,8 +390,6 @@ void main() { () => remote.fetch(), throwsA(isA()), ); - - remote.free(); }); test( @@ -453,8 +415,6 @@ void main() { expect(stats.totalDeltas == callbackStats?.totalDeltas, true); expect(stats.indexedDeltas == callbackStats?.indexedDeltas, true); expect(stats.receivedBytes == callbackStats?.receivedBytes, true); - - remote.free(); }, tags: 'remote_fetch', ); @@ -483,8 +443,6 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 remote.fetch(callbacks: callbacks); expect(sidebandOutput.toString(), sidebandMessage); - - remote.free(); }, tags: 'remote_fetch', ); @@ -529,8 +487,6 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 remote.fetch(callbacks: callbacks); expect(updateTipsOutput, tipsExpected); - - remote.free(); }, tags: 'remote_fetch', ); @@ -566,7 +522,6 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 ); expect(updateRefOutput, {'refs/heads/master': ''}); - remote.free(); originRepo.free(); originDir.delete(recursive: true); }); @@ -579,8 +534,26 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 () => remote.push(refspecs: ['refs/heads/master']), throwsA(isA()), ); + }); - remote.free(); + test('manually releases allocated memory', () { + final remote = Remote.lookup(repo: repo, name: 'origin'); + expect(() => remote.free(), returnsNormally); + }); + }); + + group('RemoteCallback', () { + test('initializes and returns values', () { + const remoteCallback = RemoteCallback( + name: 'name', + url: 'url', + fetch: 'fetchRefspec', + ); + + expect(remoteCallback, isA()); + expect(remoteCallback.name, 'name'); + expect(remoteCallback.url, 'url'); + expect(remoteCallback.fetch, 'fetchRefspec'); }); }); } diff --git a/test/repository_clone_test.dart b/test/repository_clone_test.dart index db1cee4..0dbf1f4 100644 --- a/test/repository_clone_test.dart +++ b/test/repository_clone_test.dart @@ -9,7 +9,6 @@ import 'package:test/test.dart'; import 'helpers/util.dart'; void main() { - late Repository repo; late Directory tmpDir; final cloneDir = Directory( p.join(Directory.systemTemp.path, 'repository_cloned'), @@ -17,14 +16,12 @@ void main() { setUp(() { tmpDir = setupRepo(Directory(p.join('test', 'assets', 'test_repo'))); - repo = Repository.open(tmpDir.path); if (cloneDir.existsSync()) { cloneDir.delete(recursive: true); } }); tearDown(() { - repo.free(); tmpDir.deleteSync(recursive: true); if (cloneDir.existsSync()) { cloneDir.deleteSync(recursive: true); @@ -72,14 +69,13 @@ void main() { clonedRepo.free(); }); - test('clones repository with provided remote callback', () { - Remote remote(Repository repo, String name, String url) => - Remote.create(repo: repo, name: 'test', url: tmpDir.path); - + test( + 'clones repository with provided remote callback having default fetch ' + 'refspec value', () { final clonedRepo = Repository.clone( url: tmpDir.path, localPath: cloneDir.path, - remote: remote, + remoteCallback: RemoteCallback(name: 'test', url: tmpDir.path), ); expect(clonedRepo.isEmpty, false); @@ -90,15 +86,35 @@ void main() { clonedRepo.free(); }); - test('throws when cloning repository with invalid remote callback', () { - Remote remote(Repository repo, String name, String url) => - Remote.create(repo: repo, name: '', url: ''); + test('clones repository with provided remote callback ', () { + const fetchRefspec = '+refs/heads/*:refs/remotes/spec/*'; + final clonedRepo = Repository.clone( + url: tmpDir.path, + localPath: cloneDir.path, + remoteCallback: RemoteCallback( + name: 'test', + url: tmpDir.path, + fetch: fetchRefspec, + ), + ); + final remote = Remote.lookup(repo: clonedRepo, name: 'test'); + + expect(clonedRepo.isEmpty, false); + expect(clonedRepo.isBare, false); + expect(clonedRepo.remotes, ['test']); + expect(clonedRepo.references, contains('refs/remotes/spec/master')); + expect(remote.fetchRefspecs, [fetchRefspec]); + + clonedRepo.free(); + }); + + test('throws when cloning repository with invalid remote callback', () { expect( () => Repository.clone( url: tmpDir.path, localPath: cloneDir.path, - remote: remote, + remoteCallback: const RemoteCallback(name: '', url: ''), ), throwsA(isA()), ); @@ -113,13 +129,10 @@ void main() { } callbackPath.createSync(); - Repository repository(String path, bool bare) => - Repository.init(path: callbackPath.path); - final clonedRepo = Repository.clone( url: tmpDir.path, localPath: cloneDir.path, - repository: repository, + repositoryCallback: RepositoryCallback(path: callbackPath.path), ); expect(clonedRepo.isEmpty, false); @@ -131,17 +144,41 @@ void main() { }); test('throws when cloning repository with invalid repository callback', () { - Repository repository(String path, bool bare) => - Repository.init(path: ''); - expect( () => Repository.clone( url: tmpDir.path, localPath: cloneDir.path, - repository: repository, + repositoryCallback: const RepositoryCallback(path: ''), ), throwsA(isA()), ); }); }); + + group('RepositoryCallback', () { + test('initializes and returns values', () { + const repositoryCallback = RepositoryCallback( + path: 'some/path', + bare: true, + flags: {GitRepositoryInit.noReinit}, + mode: 1, + workdirPath: 'some/path', + description: 'description', + templatePath: 'some/path', + initialHead: 'feature', + originUrl: 'some.url', + ); + + expect(repositoryCallback, isA()); + expect(repositoryCallback.path, 'some/path'); + expect(repositoryCallback.bare, true); + expect(repositoryCallback.flags, {GitRepositoryInit.noReinit}); + expect(repositoryCallback.mode, 1); + expect(repositoryCallback.workdirPath, 'some/path'); + expect(repositoryCallback.description, 'description'); + expect(repositoryCallback.templatePath, 'some/path'); + expect(repositoryCallback.initialHead, 'feature'); + expect(repositoryCallback.originUrl, 'some.url'); + }); + }); } diff --git a/test/repository_init_test.dart b/test/repository_init_test.dart index 1eda752..2a64a92 100644 --- a/test/repository_init_test.dart +++ b/test/repository_init_test.dart @@ -5,7 +5,6 @@ import 'package:path/path.dart' as p; import 'package:test/test.dart'; void main() { - late Repository repo; final initDir = Directory(p.join(Directory.systemTemp.path, 'init_repo')); setUp(() { @@ -17,27 +16,30 @@ void main() { }); tearDown(() { - repo.free(); initDir.deleteSync(recursive: true); }); group('Repository.init', () { test('creates new bare repo at provided path', () { - repo = Repository.init(path: initDir.path, bare: true); + final repo = Repository.init(path: initDir.path, bare: true); expect(repo.path, contains('init_repo')); expect(repo.isBare, true); + + repo.free(); }); test('creates new standard repo at provided path', () { - repo = Repository.init(path: initDir.path); + final repo = Repository.init(path: initDir.path); expect(repo.path, contains('init_repo/.git/')); expect(repo.isBare, false); expect(repo.isEmpty, true); + + repo.free(); }); test('creates new standard repo with provided options', () { - repo = Repository.init( + final repo = Repository.init( path: initDir.path, description: 'test repo', originUrl: 'test.url', @@ -52,6 +54,8 @@ void main() { 'test repo', ); expect(Remote.lookup(repo: repo, name: 'origin').url, 'test.url'); + + repo.free(); }); }); } diff --git a/test/repository_test.dart b/test/repository_test.dart index 49f6e66..8a79323 100644 --- a/test/repository_test.dart +++ b/test/repository_test.dart @@ -25,22 +25,17 @@ void main() { group('Repository', () { test('returns config for repository', () { - final config = repo.config; expect( - config['remote.origin.url'].value, + repo.config['remote.origin.url'].value, 'git://github.com/SkinnyMind/libgit2dart.git', ); - - config.free(); }); test('returns snapshot of repository config', () { - final snapshot = repo.configSnapshot; expect( - snapshot['remote.origin.url'].value, + repo.configSnapshot['remote.origin.url'].value, 'git://github.com/SkinnyMind/libgit2dart.git', ); - snapshot.free(); }); test('returns list of commits by walking from provided starting oid', () { @@ -57,10 +52,6 @@ void main() { for (var i = 0; i < commits.length; i++) { expect(commits[i].oid.sha, log[i]); } - - for (final c in commits) { - c.free(); - } }); group('.discover()', () { @@ -119,11 +110,6 @@ void main() { }); group('setHead', () { - late Reference head; - - setUp(() => head = repo.head); - tearDown(() => head.free()); - test('sets head when target is reference', () { expect(repo.head.name, 'refs/heads/master'); expect(repo.head.target.sha, lastCommit); @@ -164,9 +150,8 @@ void main() { test('returns status of a repository', () { File(p.join(tmpDir.path, 'new_file.txt')).createSync(); - final index = repo.index; - index.remove('file'); - index.add('new_file.txt'); + repo.index.remove('file'); + repo.index.add('new_file.txt'); expect( repo.status, { @@ -174,8 +159,6 @@ void main() { 'new_file.txt': {GitStatus.indexNew} }, ); - - index.free(); }); test('throws when trying to get status of bare repository', () { @@ -194,8 +177,6 @@ void main() { expect(repo.state, GitRepositoryState.cherrypick); repo.stateCleanup(); expect(repo.state, GitRepositoryState.none); - - commit.free(); }); test('throws when trying to clean up state and error occurs', () { @@ -206,15 +187,12 @@ void main() { }); test('returns status of a single file for provided path', () { - final index = repo.index; - index.remove('file'); + repo.index.remove('file'); expect( repo.statusFile('file'), {GitStatus.indexDeleted, GitStatus.wtNew}, ); expect(repo.statusFile('.gitignore'), {GitStatus.current}); - - index.free(); }); test('throws when checking status of a single file for invalid path', () { @@ -229,9 +207,6 @@ void main() { final signature = repo.defaultSignature; expect(signature.name, 'Some Name'); expect(signature.email, 'some@email.com'); - - signature.free(); - config.free(); }); test('returns attribute value', () { @@ -265,9 +240,11 @@ void main() { repo.aheadBehind(local: commit1.oid, upstream: commit1.oid), [0, 0], ); + }); - commit1.free(); - commit2.free(); + test('manually releases allocated memory', () { + final repo = Repository.open(tmpDir.path); + expect(() => repo.free(), returnsNormally); }); test('returns string representation of Repository object', () { diff --git a/test/reset_test.dart b/test/reset_test.dart index 06e5745..2bd907b 100644 --- a/test/reset_test.dart +++ b/test/reset_test.dart @@ -41,11 +41,8 @@ void main() { contents = file.readAsStringSync(); expect(contents, 'Feature edit\n'); - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(diff.deltas, isEmpty); - - index.free(); }); test('resets with mixed', () { @@ -56,37 +53,26 @@ void main() { contents = file.readAsStringSync(); expect(contents, 'Feature edit\n'); - final index = repo.index; - final diff = Diff.indexToWorkdir(repo: repo, index: index); + final diff = Diff.indexToWorkdir(repo: repo, index: repo.index); expect(diff.deltas.length, 1); - - index.free(); }); group('resetDefault', () { test('updates entry in the index', () { file.writeAsStringSync('new edit'); - final index = repo.index; - index.add('feature_file'); + repo.index.add('feature_file'); expect(repo.status['feature_file'], {GitStatus.indexModified}); - final head = repo.head; - repo.resetDefault(oid: head.target, pathspec: ['feature_file']); + repo.resetDefault(oid: repo.head.target, pathspec: ['feature_file']); expect(repo.status['feature_file'], {GitStatus.wtModified}); - - head.free(); - index.free(); }); test('throws when pathspec list is empty', () { - final head = repo.head; expect( - () => repo.resetDefault(oid: head.target, pathspec: []), + () => repo.resetDefault(oid: repo.head.target, pathspec: []), throwsA(isA()), ); - - head.free(); }); }); }); diff --git a/test/revparse_test.dart b/test/revparse_test.dart index 8892d0d..e4bf64c 100644 --- a/test/revparse_test.dart +++ b/test/revparse_test.dart @@ -42,11 +42,6 @@ void main() { final tag = RevParse.single(repo: repo, spec: 'v0.2') as Tag; expect(tag, isA()); expect(tag.message, 'annotated tag\n'); - - commit.free(); - tree.free(); - blob.free(); - tag.free(); }); test('.single() throws when spec string not found or invalid', () { @@ -68,10 +63,6 @@ void main() { expect(headParse.reference?.equals(masterRef), true); expect(headParse.toString(), contains('RevParse{')); - masterRef.free(); - headParse.object.free(); - headParse.reference?.free(); - final featureRef = Reference.lookup( repo: repo, name: 'refs/heads/feature', @@ -83,10 +74,6 @@ void main() { '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4', ); expect(headParse.reference?.equals(featureRef), true); - - featureRef.free(); - headParse.object.free(); - headParse.reference?.free(); }); test('.ext() returns only commit when no intermidiate reference found', () { @@ -94,8 +81,6 @@ void main() { expect(headParse.object.oid.sha, parentSHA); expect(headParse.reference, isNull); - - headParse.object.free(); }); test('.ext() throws when spec string not found or invalid', () { @@ -119,17 +104,12 @@ void main() { expect(revspec.flags, {GitRevSpec.single}); expect(revspec.toString(), contains('RevSpec{')); - revspec.from.free(); - revspec = RevParse.range(repo: repo, spec: 'HEAD^1..5aecfa'); expect(revspec.from.oid.sha, parentSHA); expect(revspec.to?.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(revspec.flags, {GitRevSpec.range}); - revspec.from.free(); - revspec.to?.free(); - revspec = RevParse.range(repo: repo, spec: 'HEAD...feature'); expect(revspec.from.oid.sha, headSHA); @@ -139,9 +119,6 @@ void main() { Merge.base(repo: repo, commits: [revspec.from.oid, revspec.to!.oid]), isA(), ); - - revspec.from.free(); - revspec.to?.free(); }); test('throws on invalid range spec', () { diff --git a/test/revwalk_test.dart b/test/revwalk_test.dart index cfe4279..53f5a1a 100644 --- a/test/revwalk_test.dart +++ b/test/revwalk_test.dart @@ -30,9 +30,7 @@ void main() { group('RevWalk', () { test('initializes walker', () { - final walker = RevWalk(repo); - expect(walker, isA()); - walker.free(); + expect(RevWalk(repo), isA()); }); test('throws when trying to initialize and error occurs', () { @@ -51,10 +49,7 @@ void main() { for (var i = 0; i < commits.length; i++) { expect(commits[i].oid.sha, log[i]); - commits[i].free(); } - - walker.free(); }); test('returns list of commits with reverse sorting', () { @@ -67,10 +62,7 @@ void main() { for (var i = 0; i < commits.length; i++) { expect(commits[i].oid.sha, log.reversed.toList()[i]); - commits[i].free(); } - - walker.free(); }); test('changes sorting', () { @@ -82,17 +74,13 @@ void main() { for (var i = 0; i < timeSortedCommits.length; i++) { expect(timeSortedCommits[i].oid.sha, log[i]); - timeSortedCommits[i].free(); } walker.sorting({GitSort.time, GitSort.reverse}); final reverseSortedCommits = walker.walk(); for (var i = 0; i < reverseSortedCommits.length; i++) { expect(reverseSortedCommits[i].oid.sha, log.reversed.toList()[i]); - reverseSortedCommits[i].free(); } - - walker.free(); }); test('adds matching references for traversal with provided glob', () { @@ -101,11 +89,6 @@ void main() { walker.pushGlob('heads'); final commits = walker.walk(); expect(commits.length, 7); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test("adds repository's head for traversal", () { @@ -114,11 +97,6 @@ void main() { walker.pushHead(); final commits = walker.walk(); expect(commits.length, 6); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test('adds reference for traversal with provided name', () { @@ -127,23 +105,14 @@ void main() { walker.pushReference('refs/heads/master'); final commits = walker.walk(); expect(commits.length, 6); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test('throws when trying to add reference for traversal with invalid name', () { - final walker = RevWalk(repo); - expect( - () => walker.pushReference('invalid'), + () => RevWalk(repo).pushReference('invalid'), throwsA(isA()), ); - - walker.free(); }); test('adds range for traversal', () { @@ -152,22 +121,13 @@ void main() { walker.pushRange('HEAD..@{-1}'); final commits = walker.walk(); expect(commits.length, 1); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test('throws when trying to add invalid range for traversal', () { - final walker = RevWalk(repo); - expect( - () => walker.pushRange('HEAD..invalid'), + () => RevWalk(repo).pushRange('HEAD..invalid'), throwsA(isA()), ); - - walker.free(); }); test('hides commit and its ancestors', () { @@ -178,22 +138,13 @@ void main() { final commits = walker.walk(); expect(commits.length, 2); - - for (final c in commits) { - c.free(); - } - walker.free(); }); test('throws when trying to hide commit oid and error occurs', () { - final walker = RevWalk(repo); - expect( - () => walker.hide(repo['0' * 40]), + () => RevWalk(repo).hide(repo['0' * 40]), throwsA(isA()), ); - - walker.free(); }); test('hides oids of references for provided glob pattern', () { @@ -207,12 +158,6 @@ void main() { walker.hideGlob('*master'); final hiddenCommits = walker.walk(); expect(hiddenCommits.length, 1); - - hiddenCommits[0].free(); - for (final c in commits) { - c.free(); - } - walker.free(); }); test('hides head', () { @@ -227,12 +172,6 @@ void main() { walker.hideHead(); final hiddenCommits = walker.walk(); expect(hiddenCommits.length, 0); - - for (final c in commits) { - c.free(); - } - head.free(); - walker.free(); }); test('hides oids of reference with provided name', () { @@ -247,23 +186,13 @@ void main() { walker.hideReference('refs/heads/master'); final hiddenCommits = walker.walk(); expect(hiddenCommits.length, 0); - - for (final c in commits) { - c.free(); - } - head.free(); - walker.free(); }); test('throws when trying to hide oids of reference with invalid name', () { - final walker = RevWalk(repo); - expect( - () => walker.hideReference('invalid'), + () => RevWalk(repo).hideReference('invalid'), throwsA(isA()), ); - - walker.free(); }); test('resets walker', () { @@ -275,8 +204,6 @@ void main() { final commits = walker.walk(); expect(commits, []); - - walker.free(); }); test('simplifies walker by enqueuing only first parent for each commit', @@ -290,22 +217,19 @@ void main() { for (var i = 0; i < commits.length; i++) { expect(commits.length, 3); - commits[i].free(); } - - walker.free(); }); test('throws when trying to add new root for traversal and error occurs', () { - final walker = RevWalk(repo); - expect( - () => walker.push(repo['0' * 40]), + () => RevWalk(repo).push(repo['0' * 40]), throwsA(isA()), ); + }); - walker.free(); + test('manually releases allocated memory', () { + expect(() => RevWalk(repo).free(), returnsNormally); }); }); } diff --git a/test/signature_test.dart b/test/signature_test.dart index ff147ee..303a607 100644 --- a/test/signature_test.dart +++ b/test/signature_test.dart @@ -16,9 +16,6 @@ void main() { ); }); - tearDown(() { - signature.free(); - }); group('Signature', () { test('creates with provided time and offset', () { expect(signature, isA()); @@ -50,7 +47,6 @@ void main() { lessThan(5), ); expect(sig.offset, isA()); - sig.free(); }); test('returns correct values', () { @@ -66,9 +62,16 @@ void main() { email: email, time: time, ); - expect(signature == otherSignature, true); + expect(signature, equals(otherSignature)); + }); - otherSignature.free(); + test('manually releases allocated memory', () { + final signature = Signature.create( + name: name, + email: email, + time: time, + ); + expect(() => signature.free(), returnsNormally); }); test('returns string representation of Signature object', () { diff --git a/test/stash_test.dart b/test/stash_test.dart index adf09bf..ca064fa 100644 --- a/test/stash_test.dart +++ b/test/stash_test.dart @@ -24,7 +24,6 @@ void main() { }); tearDown(() { - stasher.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -62,14 +61,11 @@ void main() { test('leaves changes added to index intact', () { File(filePath).writeAsStringSync('edit', mode: FileMode.append); - final index = repo.index; - index.add('file'); + repo.index.add('file'); Stash.create(repo: repo, stasher: stasher, flags: {GitStash.keepIndex}); expect(repo.status.isEmpty, false); expect(repo.stashes.length, 1); - - index.free(); }); test('applies changes from stash', () { @@ -109,8 +105,6 @@ void main() { Stash.apply(repo: repo, reinstateIndex: true); expect(repo.status, contains('stash.this')); expect(index.find('stash.this'), true); - - index.free(); }); test('throws when trying to apply with wrong index', () { @@ -182,8 +176,6 @@ void main() { Stash.pop(repo: repo, reinstateIndex: true); expect(repo.status, contains('stash.this')); expect(index.find('stash.this'), true); - - index.free(); }); test('throws when trying to pop with wrong index', () { diff --git a/test/submodule_test.dart b/test/submodule_test.dart index 77566e8..20e03b4 100644 --- a/test/submodule_test.dart +++ b/test/submodule_test.dart @@ -47,8 +47,6 @@ void main() { expect(submodule.ignore, GitSubmoduleIgnore.none); expect(submodule.updateRule, GitSubmoduleUpdate.checkout); expect(submodule.toString(), contains('Submodule{')); - - submodule.free(); }); test('throws when trying to lookup and submodule not found', () { @@ -92,24 +90,20 @@ void main() { Submodule.update(repo: repo, name: testSubmodule); final submoduleRepo = submodule.open(); - final subHead = submoduleRepo.head; expect(submoduleRepo, isA()); - expect(subHead.target.sha, submoduleHeadSha); + expect(submoduleRepo.head.target.sha, submoduleHeadSha); expect( submodule.workdirOid?.sha, '49322bb17d3acc9146f98c97d078513228bbf3c0', ); - subHead.free(); submoduleRepo.free(); - submodule.free(); }); test('throws when trying to open repository for not initialized submodule', () { final submodule = Submodule.lookup(repo: repo, name: testSubmodule); expect(() => submodule.open(), throwsA(isA())); - submodule.free(); }); test('adds submodule', () { @@ -125,7 +119,6 @@ void main() { expect(submoduleRepo.isEmpty, false); submoduleRepo.free(); - submodule.free(); }); test('throws when trying to add submodule with wrong url', () { @@ -170,9 +163,6 @@ void main() { expect(updatedSubmodule.branch, 'updated'); expect(updatedSubmodule.ignore, GitSubmoduleIgnore.all); expect(updatedSubmodule.updateRule, GitSubmoduleUpdate.rebase); - - updatedSubmodule.free(); - submodule.free(); }); test('syncs', () { @@ -205,13 +195,8 @@ void main() { 'https://updated.com/', ); - updatedSubmRepoConfig.free(); submRepo.free(); updatedSubmRepo.free(); - updatedSubmodule.free(); - submRepoConfig.free(); - repoConfig.free(); - submodule.free(); }); test('reloads info', () { @@ -222,8 +207,6 @@ void main() { submodule.reload(); expect(submodule.url, 'updated'); - - submodule.free(); }); test('returns status for a submodule', () { @@ -259,8 +242,11 @@ void main() { GitSubmoduleStatus.inWorkdir, }, ); + }); - submodule.free(); + test('manually releases allocated memory', () { + final submodule = Submodule.lookup(repo: repo, name: testSubmodule); + expect(() => submodule.free(), returnsNormally); }); }); } diff --git a/test/tag_test.dart b/test/tag_test.dart index 48e6760..6234b4a 100644 --- a/test/tag_test.dart +++ b/test/tag_test.dart @@ -21,7 +21,6 @@ void main() { }); tearDown(() { - tag.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -59,9 +58,6 @@ void main() { expect(target.message, 'add subdirectory file\n'); expect(tagger, signature); expect(tag.toString(), contains('Tag{')); - - signature.free(); - target.free(); }); test('creates new annotated tag with commit as target', () { @@ -94,10 +90,6 @@ void main() { expect(newTag.targetOid.sha, targetSHA); expect(tagger, signature); expect(newTagTarget.oid, target); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test('creates new lightweight tag with commit as target', () { @@ -115,8 +107,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, target); - - newTag.free(); }); test('creates new annotated tag with tree as target', () { @@ -147,10 +137,6 @@ void main() { expect(newTag.message, message); expect(tagger, signature); expect(newTagTarget.oid, target); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test('creates new lightweight tag with tree as target', () { @@ -168,8 +154,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, target); - - newTag.free(); }); test('creates new annotated tag with blob as target', () { @@ -200,10 +184,6 @@ void main() { expect(newTag.message, message); expect(tagger, signature); expect(newTagTarget.oid, target); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test('creates new lightweight tag with blob as target', () { @@ -221,8 +201,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, target); - - newTag.free(); }); test('creates new annotated tag with tag as target', () { @@ -252,10 +230,6 @@ void main() { expect(newTag.message, message); expect(tagger, signature); expect(newTagTarget.oid, tag.oid); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test('creates new lightweight tag with tag as target', () { @@ -272,8 +246,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, tag.oid); - - newTag.free(); }); test( @@ -313,10 +285,6 @@ void main() { expect(tagger, signature); expect(newTagTarget.oid, target); expect(repo.tags.length, equals(2)); - - newTag.free(); - newTagTarget.free(); - signature.free(); }); test( @@ -342,8 +310,6 @@ void main() { expect(newTag.shorthand, tagName); expect(newTag.target, target); expect(repo.tags.length, equals(2)); - - newTag.free(); }); test('throws when trying to create annotated tag with invalid name', () { @@ -420,5 +386,10 @@ void main() { throwsA(isA()), ); }); + + test('manually releases allocated memory', () { + tag = Tag.lookup(repo: repo, oid: tagOid); + expect(() => tag.free(), returnsNormally); + }); }); } diff --git a/test/tree_test.dart b/test/tree_test.dart index 05cbdbb..e35b6f1 100644 --- a/test/tree_test.dart +++ b/test/tree_test.dart @@ -19,7 +19,6 @@ void main() { }); tearDown(() { - tree.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -65,7 +64,6 @@ void main() { final entry = tree['dir/dir_file.txt']; expect(entry.oid.sha, 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'); expect(entry.toString(), contains('TreeEntry{')); - entry.free(); }); test('throws when nothing found for provided path', () { @@ -92,9 +90,17 @@ void main() { expect(entry.name, 'filename'); expect(entry.filemode, GitFilemode.blob); expect(entry.oid, fileOid); + }); - builder.free(); - newTree.free(); + test('manually releases allocated memory', () { + final tree = Tree.lookup(repo: repo, oid: repo['a8ae3dd']); + expect(() => tree.free(), returnsNormally); + }); + + test( + 'manually releases allocated memory for tree entry ' + 'looked up by path', () { + expect(() => tree['dir/dir_file.txt'].free(), returnsNormally); }); }); } diff --git a/test/treebuilder_test.dart b/test/treebuilder_test.dart index 9b8ad07..9cd2c46 100644 --- a/test/treebuilder_test.dart +++ b/test/treebuilder_test.dart @@ -19,7 +19,6 @@ void main() { }); tearDown(() { - tree.free(); repo.free(); tmpDir.deleteSync(recursive: true); }); @@ -29,7 +28,6 @@ void main() { final builder = TreeBuilder(repo: repo); expect(builder, isA()); expect(builder.toString(), contains('TreeBuilder{')); - builder.free(); }); test('initializes tree builder with provided tree', () { @@ -39,8 +37,6 @@ void main() { expect(builder, isA()); expect(builder.length, tree.length); expect(oid, tree.oid); - - builder.free(); }); test('throws when trying to initialize and error occurs', () { @@ -56,8 +52,6 @@ void main() { expect(builder.length, 4); builder.clear(); expect(builder.length, 0); - - builder.free(); }); test('builds the tree builder from entry of tree', () { @@ -72,8 +66,6 @@ void main() { filemode: entry.filemode, ); expect(builder[entry.name].name, entry.name); - - builder.free(); }); test('throws when trying to add entry with invalid name or invalid oid', @@ -96,8 +88,6 @@ void main() { ), throwsA(isA()), ); - - builder.free(); }); test('removes an entry', () { @@ -108,14 +98,17 @@ void main() { builder.remove('.gitignore'); expect(() => builder['.gitignore'], throwsA(isA())); expect(builder.length, tree.length - 1); - - builder.free(); }); test('throws when trying to remove entry that is not in the tree', () { - final builder = TreeBuilder(repo: repo); - expect(() => builder.remove('not.there'), throwsA(isA())); - builder.free(); + expect( + () => TreeBuilder(repo: repo).remove('not.there'), + throwsA(isA()), + ); + }); + + test('manually releases allocated memory', () { + expect(() => TreeBuilder(repo: repo).free(), returnsNormally); }); }); } diff --git a/test/worktree_test.dart b/test/worktree_test.dart index d8b5c24..4da3b99 100644 --- a/test/worktree_test.dart +++ b/test/worktree_test.dart @@ -38,20 +38,14 @@ void main() { name: worktreeName, path: worktreeDir.path, ); - final branches = repo.branches; expect(repo.worktrees, [worktreeName]); - expect(branches.any((branch) => branch.name == worktreeName), true); + expect(repo.branches.any((branch) => branch.name == worktreeName), true); expect(worktree.name, worktreeName); expect(worktree.path, contains('worktree')); expect(worktree.isLocked, false); expect(worktree.toString(), contains('Worktree{')); expect(File(p.join(worktreeDir.path, '.git')).existsSync(), true); - - for (final branch in branches) { - branch.free(); - } - worktree.free(); }); test('creates worktree at provided path from provided reference', () { @@ -83,14 +77,6 @@ void main() { expect(repo.worktrees, []); expect(worktreeBranch.isCheckedOut, false); expect(branches.any((branch) => branch.name == 'v1'), true); - - for (final branch in branches) { - branch.free(); - } - worktreeBranch.free(); - ref.free(); - head.free(); - worktree.free(); }); test('throws when trying to create worktree with invalid name or path', () { @@ -113,19 +99,16 @@ void main() { }); test('lookups worktree', () { - final worktree = Worktree.create( + Worktree.create( repo: repo, name: worktreeName, path: worktreeDir.path, ); - final lookedupWorktree = Worktree.lookup(repo: repo, name: worktreeName); + final worktree = Worktree.lookup(repo: repo, name: worktreeName); - expect(lookedupWorktree.name, worktreeName); - expect(lookedupWorktree.path, contains('worktree')); - expect(lookedupWorktree.isLocked, false); - - lookedupWorktree.free(); - worktree.free(); + expect(worktree.name, worktreeName); + expect(worktree.path, contains('worktree')); + expect(worktree.isLocked, false); }); test('throws when trying to lookup and error occurs', () { @@ -148,8 +131,6 @@ void main() { worktree.unlock(); expect(worktree.isLocked, false); - - worktree.free(); }); test('prunes worktree', () { @@ -170,8 +151,6 @@ void main() { worktree.prune(); expect(repo.worktrees, []); - - worktree.free(); }); test('throws when trying get list of worktrees and error occurs', () { @@ -180,5 +159,14 @@ void main() { throwsA(isA()), ); }); + + test('manually releases allocated memory', () { + final worktree = Worktree.create( + repo: repo, + name: worktreeName, + path: worktreeDir.path, + ); + expect(() => worktree.free(), returnsNormally); + }); }); }