Skip to content

Commit deeabd6

Browse files
authored
Check memory leaks in all tests, and fix the 3 detected (#26)
In PR #24 I started removing memory leaks by checking them when the utility function `with_db` was used in unit tests. This PR finalizes that process. It contains a new utility class `LeakChecker` that checks the leaks for a unit test. `LeakChecker` is added to the utility classes `ReplicationTwoDbsTester` and `ReplicationThreeDbsTester`, used to test replication use cases. `LeakChecker` is also added to all tests that do not use one of the three utility function & classes. Three new sources of memory leaks were discovered this time: - in `src/encryptable.rs`, `reference` was used instead of `take_ownership` when creating a new `Encryptable` - in `src/replicator.rs`, the struct `Endpoint` was not implementing `Drop` - in `src/replicator.rs`, when a `Replicator` was created we were not giving ownership of the `CBLDatabase` object to the `CBLReplicatorConfiguration` object
1 parent 97d8d9a commit deeabd6

File tree

6 files changed

+130
-25
lines changed

6 files changed

+130
-25
lines changed

src/encryptable.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl CblRef for Encryptable {
6666

6767
impl From<*mut CBLEncryptable> for Encryptable {
6868
fn from(cbl_ref: *mut CBLEncryptable) -> Self {
69-
Self::reference(cbl_ref)
69+
Self::take_ownership(cbl_ref)
7070
}
7171
}
7272

@@ -80,6 +80,11 @@ impl Encryptable {
8080
}
8181
}
8282

83+
/// Takes ownership of the CBL ref, the reference counter is not increased so dropping the instance will free the ref.
84+
pub(crate) const fn take_ownership(cbl_ref: *mut CBLEncryptable) -> Self {
85+
Self { cbl_ref }
86+
}
87+
8388
////////
8489

8590
/// Creates Encryptable object with null value.

src/replicator.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::{
2525
};
2626
use crate::{
2727
CblRef, Database, Dict, Document, Error, ListenerToken, MutableDict, Result, check_error,
28-
release, retain,
28+
release,
2929
slice::{from_str, self},
3030
c_api::{
3131
CBLListener_Remove, CBLAuth_CreatePassword, CBLAuth_CreateSession, CBLAuthenticator,
@@ -107,6 +107,14 @@ impl Clone for Endpoint {
107107
}
108108
}
109109

110+
impl Drop for Endpoint {
111+
fn drop(&mut self) {
112+
unsafe {
113+
release(self.get_ref());
114+
}
115+
}
116+
}
117+
110118
/** An opaque object representing authentication credentials for a remote server. */
111119
#[derive(Debug, PartialEq, Eq)]
112120
pub struct Authenticator {
@@ -786,7 +794,7 @@ impl Replicator {
786794
database: config
787795
.database
788796
.as_ref()
789-
.map(|d| retain(d.get_ref()))
797+
.map(|d| d.get_ref())
790798
.unwrap_or(ptr::null_mut()),
791799
endpoint: config.endpoint.get_ref(),
792800
replicatorType: config.replicator_type.clone().into(),

tests/database_tests.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extern crate lazy_static;
2222
use self::couchbase_lite::*;
2323
use self::tempdir::TempDir;
2424
use lazy_static::lazy_static;
25-
use utils::init_logging;
25+
use utils::{init_logging, LeakChecker};
2626

2727
pub mod utils;
2828

@@ -44,6 +44,7 @@ fn delete_file() {
4444
pub const DB_NAME: &str = "test_db";
4545

4646
init_logging();
47+
let _leak_checker = LeakChecker::new();
4748

4849
let tmp_dir = TempDir::new("cbl_rust").expect("create temp dir");
4950
let cfg = DatabaseConfiguration {
@@ -66,6 +67,7 @@ fn copy_file() {
6667
pub const DB_NAME_BACKUP: &str = "test_db_backup";
6768

6869
init_logging();
70+
let _leak_checker = LeakChecker::new();
6971

7072
// Initial DB
7173
let tmp_dir = TempDir::new("cbl_rust").expect("create temp dir");
@@ -147,6 +149,9 @@ fn db_properties() {
147149
#[test]
148150
#[cfg(feature = "enterprise")]
149151
fn db_encryption_key() {
152+
init_logging();
153+
let _leak_checker = LeakChecker::new();
154+
150155
let tmp_dir = TempDir::new("cbl_rust").expect("create temp dir");
151156
let cfg_no_encryption = DatabaseConfiguration {
152157
directory: tmp_dir.path(),

tests/document_tests.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ extern crate couchbase_lite;
33

44
use self::couchbase_lite::*;
55
use std::time::Duration;
6+
use utils::{init_logging, LeakChecker};
67

78
pub mod utils;
89

910
#[test]
1011
fn document_new() {
12+
init_logging();
13+
let _leak_checker = LeakChecker::new();
14+
1115
let document = Document::new();
1216
assert_ne!(document.id(), "");
1317
assert_eq!(document.revision_id(), None);
@@ -18,6 +22,9 @@ fn document_new() {
1822

1923
#[test]
2024
fn document_new_with_id() {
25+
init_logging();
26+
let _leak_checker = LeakChecker::new();
27+
2128
let document = Document::new_with_id("foo");
2229
assert_eq!(document.id(), "foo");
2330
assert_eq!(document.revision_id(), None);
@@ -70,6 +77,9 @@ fn document_sequence() {
7077

7178
#[test]
7279
fn document_properties() {
80+
init_logging();
81+
let _leak_checker = LeakChecker::new();
82+
7383
let mut document = Document::new();
7484
let mut properties = MutableDict::new();
7585
properties.at("foo").put_bool(false);
@@ -87,6 +97,9 @@ fn document_properties() {
8797

8898
#[test]
8999
fn document_properties_as_json() {
100+
init_logging();
101+
let _leak_checker = LeakChecker::new();
102+
90103
let mut document = Document::new();
91104
document
92105
.set_properties_as_json(r#"{"foo":true,"bar":true}"#)

tests/fleece_tests.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,15 @@
2020
extern crate couchbase_lite;
2121

2222
use couchbase_lite::*;
23+
use utils::{init_logging, LeakChecker};
24+
25+
pub mod utils;
2326

2427
#[test]
2528
fn empty_values() {
29+
init_logging();
30+
let _leak_checker = LeakChecker::new();
31+
2632
let v = Value::default();
2733
assert_eq!(v.get_type(), ValueType::Undefined);
2834
assert!(!v.is_type(ValueType::Bool));
@@ -38,6 +44,9 @@ fn empty_values() {
3844

3945
#[test]
4046
fn basic_values() {
47+
init_logging();
48+
let _leak_checker = LeakChecker::new();
49+
4150
let doc = Fleece::parse_json(r#"{"i":1234,"f":12.34,"a":[1, 2],"s":"Foo"}"#).unwrap();
4251
let dict = doc.as_dict();
4352
assert_eq!(dict.count(), 4);
@@ -82,6 +91,9 @@ fn basic_values() {
8291

8392
#[test]
8493
fn nested_borrow_check() {
94+
init_logging();
95+
let _leak_checker = LeakChecker::new();
96+
8597
let v: Value;
8698
let mut str = String::new();
8799

@@ -116,6 +128,9 @@ fn borrow_check() {
116128

117129
#[test]
118130
fn dict_to_hash_set() {
131+
init_logging();
132+
let _leak_checker = LeakChecker::new();
133+
119134
let mut mut_dict = MutableDict::new();
120135

121136
mut_dict.at("id1").put_bool(true);
@@ -132,6 +147,9 @@ fn dict_to_hash_set() {
132147

133148
#[test]
134149
fn mutable_dict() {
150+
init_logging();
151+
let _leak_checker = LeakChecker::new();
152+
135153
let mut dict = MutableDict::new();
136154
assert_eq!(dict.count(), 0);
137155
assert_eq!(dict.get("a"), Value::UNDEFINED);
@@ -152,6 +170,9 @@ fn mutable_dict() {
152170

153171
#[test]
154172
fn mutable_dict_to_from_hash_map() {
173+
init_logging();
174+
let _leak_checker = LeakChecker::new();
175+
155176
let mut dict = MutableDict::new();
156177

157178
dict.at("id1").put_string("value1");
@@ -170,6 +191,9 @@ fn mutable_dict_to_from_hash_map() {
170191

171192
#[test]
172193
fn dict_exact_size_iterator() {
194+
init_logging();
195+
let _leak_checker = LeakChecker::new();
196+
173197
let mut mut_dict = MutableDict::new();
174198
mut_dict.at("1").put_string("value1");
175199
mut_dict.at("2").put_string("value2");
@@ -182,6 +206,9 @@ fn dict_exact_size_iterator() {
182206

183207
#[test]
184208
fn dict_from_iterator() {
209+
init_logging();
210+
let _leak_checker = LeakChecker::new();
211+
185212
let dict: MutableDict = Fleece::parse_json(r#"{"1": "value1","f":12.34}"#)
186213
.unwrap()
187214
.as_dict()
@@ -202,6 +229,9 @@ fn dict_from_iterator() {
202229

203230
#[test]
204231
fn array_at() {
232+
init_logging();
233+
let _leak_checker = LeakChecker::new();
234+
205235
let mut mut_arr = MutableArray::new();
206236
assert!(mut_arr.at(0).is_none());
207237
mut_arr.append().put_string("value1");
@@ -210,6 +240,9 @@ fn array_at() {
210240

211241
#[test]
212242
fn array_exact_size_iterator() {
243+
init_logging();
244+
let _leak_checker = LeakChecker::new();
245+
213246
let mut mut_arr = MutableArray::new();
214247
mut_arr.append().put_string("value1");
215248
mut_arr.append().put_string("value2");
@@ -222,6 +255,9 @@ fn array_exact_size_iterator() {
222255

223256
#[test]
224257
fn array_from_iterator() {
258+
init_logging();
259+
let _leak_checker = LeakChecker::new();
260+
225261
let arr: MutableArray = Fleece::parse_json(r#"["value1","value2"]"#)
226262
.unwrap()
227263
.as_array()

0 commit comments

Comments
 (0)