mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
mononoke: remove most of bookmarks_old
Summary: We only need stockbookmarks now Reviewed By: farnz Differential Revision: D7382258 fbshipit-source-id: d8f5f5fa1424d509c9e0364529f8aeb9b4627585
This commit is contained in:
parent
e108e9b258
commit
2c8cd78c6e
@ -1,241 +0,0 @@
|
||||
// Copyright (c) 2004-present, Facebook, Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// This software may be used and distributed according to the terms of the
|
||||
// GNU General Public License version 2 or any later version.
|
||||
|
||||
#![deny(warnings)]
|
||||
#![feature(try_from)]
|
||||
|
||||
extern crate bookmarks;
|
||||
|
||||
extern crate ascii;
|
||||
extern crate failure_ext as failure;
|
||||
extern crate futures;
|
||||
extern crate mysql;
|
||||
#[macro_use]
|
||||
extern crate mysql_async;
|
||||
extern crate tokio_core;
|
||||
|
||||
extern crate db;
|
||||
extern crate futures_ext;
|
||||
extern crate mercurial_types;
|
||||
extern crate sendwrapper;
|
||||
extern crate storage_types;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ascii::AsciiStr;
|
||||
use failure::{Error, SyncFailure};
|
||||
use futures::{future, stream, Future, Stream};
|
||||
use mysql_async::{Opts, Pool, Row, TransactionOptions};
|
||||
use mysql_async::prelude::*;
|
||||
use tokio_core::reactor::Remote;
|
||||
|
||||
use bookmarks::{Bookmarks, BookmarksMut};
|
||||
use db::ConnectionParams;
|
||||
use futures_ext::{BoxFuture, BoxFutureNonSend, BoxStream, FutureExt, StreamExt};
|
||||
use mercurial_types::nodehash::HgChangesetId;
|
||||
use sendwrapper::SendWrapper;
|
||||
use storage_types::Version;
|
||||
|
||||
pub struct DbBookmarks {
|
||||
wrapper: SendWrapper<Pool>,
|
||||
}
|
||||
|
||||
impl DbBookmarks {
|
||||
pub fn new_async(params: ConnectionParams, remote: &Remote) -> BoxFuture<Self, Error> {
|
||||
SendWrapper::new(remote, |handle| {
|
||||
Opts::try_from(params)
|
||||
.and_then(|opts| Ok(Pool::new(opts, handle)))
|
||||
.map_err(Into::into)
|
||||
}).and_then(|wrapper| Ok(DbBookmarks { wrapper }))
|
||||
.boxify()
|
||||
}
|
||||
}
|
||||
|
||||
impl Bookmarks for DbBookmarks {
|
||||
fn get(&self, key: &AsRef<[u8]>) -> BoxFuture<Option<(HgChangesetId, Version)>, Error> {
|
||||
let key = key.as_ref().to_vec();
|
||||
self.wrapper
|
||||
.with_inner(move |pool| get_bookmark(pool, key))
|
||||
.map_err(|e| e.context("DbBookmarks get failed").into())
|
||||
.boxify()
|
||||
}
|
||||
|
||||
fn keys(&self) -> BoxStream<Vec<u8>, Error> {
|
||||
self.wrapper
|
||||
.with_inner(move |pool| list_keys(pool))
|
||||
.flatten_stream()
|
||||
.map_err(|e| e.context("DbBookmarks keys failed").into())
|
||||
.boxify()
|
||||
}
|
||||
}
|
||||
|
||||
impl BookmarksMut for DbBookmarks {
|
||||
fn set(
|
||||
&self,
|
||||
key: &AsRef<[u8]>,
|
||||
value: &HgChangesetId,
|
||||
version: &Version,
|
||||
) -> BoxFuture<Option<Version>, Error> {
|
||||
let key = key.as_ref().to_vec();
|
||||
let value = value.clone();
|
||||
let version = version.clone();
|
||||
self.wrapper
|
||||
.with_inner(move |pool| set_bookmark(pool, key, value, version))
|
||||
.map_err(|e| e.context("DbBookmarks set failed").into())
|
||||
.boxify()
|
||||
}
|
||||
|
||||
fn delete(&self, key: &AsRef<[u8]>, version: &Version) -> BoxFuture<Option<Version>, Error> {
|
||||
let key = key.as_ref().to_vec();
|
||||
let version = version.clone();
|
||||
self.wrapper
|
||||
.with_inner(move |pool| delete_bookmark(pool, key, version))
|
||||
.map_err(|e| e.context("DbBookmarks delete failed").into())
|
||||
.boxify()
|
||||
}
|
||||
}
|
||||
|
||||
fn list_keys(pool: Rc<Pool>) -> BoxFutureNonSend<BoxStream<Vec<u8>, Error>, Error> {
|
||||
pool.get_conn()
|
||||
.and_then(|conn| conn.query("SELECT name FROM bookmarks"))
|
||||
.and_then(|res| res.collect::<(Vec<u8>,)>())
|
||||
.map(|(_, rows)| stream::iter_ok(rows.into_iter().map(|row| row.0)).boxify())
|
||||
.map_err(|e| SyncFailure::new(e).into())
|
||||
.boxify_nonsend()
|
||||
}
|
||||
|
||||
fn get_bookmark(
|
||||
pool: Rc<Pool>,
|
||||
key: Vec<u8>,
|
||||
) -> BoxFutureNonSend<Option<(HgChangesetId, Version)>, Error> {
|
||||
pool.get_conn()
|
||||
.and_then(|conn| {
|
||||
conn.prep_exec(
|
||||
"SELECT value, version FROM bookmarks WHERE name = ?",
|
||||
(key,),
|
||||
)
|
||||
})
|
||||
.and_then(|res| res.collect::<(String, u64)>())
|
||||
.map_err(|e| SyncFailure::new(e).into())
|
||||
.and_then(|(_, mut rows)| {
|
||||
if let Some((value, version)) = rows.pop() {
|
||||
let value = AsciiStr::from_ascii(&value)?;
|
||||
let value = HgChangesetId::from_ascii_str(&value)?;
|
||||
Ok(Some((value, Version::from(version))))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
.boxify_nonsend()
|
||||
}
|
||||
|
||||
fn set_bookmark(
|
||||
pool: Rc<Pool>,
|
||||
key: Vec<u8>,
|
||||
value: HgChangesetId,
|
||||
version: Version,
|
||||
) -> BoxFutureNonSend<Option<Version>, Error> {
|
||||
pool.get_conn()
|
||||
// Need to use a transaction since we need to perform both a read (to get the
|
||||
// current version, if any) and a write (to set the bookmark).
|
||||
.and_then(|conn| conn.start_transaction(TransactionOptions::new()))
|
||||
.and_then({
|
||||
let key = key.clone();
|
||||
// Get current version for this bookmark (if the key is present).
|
||||
move |txn| txn.prep_exec("SELECT version FROM bookmarks WHERE name = ?", (key,))
|
||||
})
|
||||
.and_then(|res| res.collect_and_drop::<(u64,)>())
|
||||
// At this point, change the `Error` type of this combinator chain to this crate's
|
||||
// `Error` type so we can return custom errors. This means all subsequent `Future`s
|
||||
// from `mysql_async` will need `.from_err()` to convert to our `Error` type.
|
||||
.map_err(|e| SyncFailure::new(e).into())
|
||||
.and_then(move |(txn, mut rows)| {
|
||||
// Get the current and new versions for this bookmark. If the bookmark is not present,
|
||||
// default to a current version of Version::absent() and a new version of 0.
|
||||
let raw_version = rows.pop().map(|row| row.0);
|
||||
let old_version = raw_version.map(|v| Version::from(v)).unwrap_or_default();
|
||||
let new_version = raw_version.map(|v| Version::from(v+1)).unwrap_or(Version::from(0));
|
||||
|
||||
// If version matches the one passed by the caller, write the new value and version.
|
||||
if version == old_version {
|
||||
let value: String = value.to_hex().into();
|
||||
txn.prep_exec(
|
||||
"INSERT INTO bookmarks (name, value, version) \
|
||||
VALUES (:key, :value, 0) \
|
||||
ON DUPLICATE KEY UPDATE \
|
||||
value = :value, version = version + 1",
|
||||
params!(key, value),
|
||||
).and_then(|res| res.drop_result())
|
||||
// Commit the transaction, and return the new version.
|
||||
.and_then(|txn| txn.commit())
|
||||
.map_err(|e| SyncFailure::new(e).into())
|
||||
.map(move |_| Some(new_version))
|
||||
.boxify_nonsend()
|
||||
} else {
|
||||
future::ok(None).boxify_nonsend()
|
||||
}
|
||||
})
|
||||
.boxify_nonsend()
|
||||
}
|
||||
|
||||
fn delete_bookmark(
|
||||
pool: Rc<Pool>,
|
||||
key: Vec<u8>,
|
||||
version: Version,
|
||||
) -> BoxFutureNonSend<Option<Version>, Error> {
|
||||
pool.get_conn()
|
||||
.and_then(move |conn| {
|
||||
// Do we expect this bookmark to exist at all? (i.e., did the caller pass the
|
||||
// the absent version?) If so, then attempt the deletion and see if it succeeds.
|
||||
// Otherwise, just check to make sure the bookmark is actually absent.
|
||||
if let Version(Some(v)) = version {
|
||||
conn.prep_exec(
|
||||
"DELETE FROM bookmarks WHERE name = :key AND version = :v",
|
||||
params!(key, v),
|
||||
).map(|res| if res.affected_rows() > 0 {
|
||||
Some(Version::absent())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.boxify_nonsend()
|
||||
} else {
|
||||
// Caller passed the absent version, so this is a no-op. That said, we
|
||||
// still need to verify that the bookmark is actually not present, and if
|
||||
// it is, signal an error by returning None.
|
||||
conn.prep_exec("SELECT 1 FROM bookmarks WHERE name = ?", (key,))
|
||||
.and_then(|res| res.collect::<Row>())
|
||||
.map(|(_, rows)| if rows.is_empty() {
|
||||
Some(Version::absent())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.boxify_nonsend()
|
||||
}
|
||||
})
|
||||
.map_err(|e| SyncFailure::new(e).into())
|
||||
.boxify_nonsend()
|
||||
}
|
||||
|
||||
pub fn init_test_db() -> ConnectionParams {
|
||||
let params = db::create_test_db("mononoke_dbbookmarks").unwrap();
|
||||
let pool = mysql::Pool::new(params.clone()).unwrap();
|
||||
|
||||
let _ = pool.prep_exec(
|
||||
"CREATE TABLE bookmarks (
|
||||
id INTEGER PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARBINARY(256) NOT NULL,
|
||||
value VARCHAR(40) NOT NULL,
|
||||
version INTEGER UNSIGNED NOT NULL,
|
||||
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
modified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY (name)
|
||||
);",
|
||||
(),
|
||||
).unwrap();
|
||||
|
||||
params
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
// Copyright (c) 2004-present, Facebook, Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// This software may be used and distributed according to the terms of the
|
||||
// GNU General Public License version 2 or any later version.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate bookmarks;
|
||||
|
||||
extern crate failure_ext as failure;
|
||||
extern crate futures;
|
||||
extern crate futures_cpupool;
|
||||
extern crate percent_encoding;
|
||||
|
||||
extern crate filekv;
|
||||
extern crate futures_ext;
|
||||
extern crate mercurial_types;
|
||||
extern crate storage_types;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
use failure::{Error, Result};
|
||||
use futures::{Future, Stream};
|
||||
use futures_cpupool::CpuPool;
|
||||
use percent_encoding::{percent_decode, percent_encode, DEFAULT_ENCODE_SET};
|
||||
|
||||
use bookmarks::{Bookmarks, BookmarksMut};
|
||||
use filekv::FileKV;
|
||||
use futures_ext::{BoxFuture, BoxStream, FutureExt, StreamExt};
|
||||
use mercurial_types::nodehash::HgChangesetId;
|
||||
use storage_types::Version;
|
||||
|
||||
static PREFIX: &'static str = "bookmark:";
|
||||
|
||||
/// A basic file-based persistent bookmark store.
|
||||
///
|
||||
/// Bookmarks are stored as files in the specified base directory. File operations are dispatched
|
||||
/// to a thread pool to avoid blocking the main thread. File accesses between these threads
|
||||
/// are synchronized by a global map of per-path locks.
|
||||
pub struct FileBookmarks {
|
||||
kv: FileKV<HgChangesetId>,
|
||||
}
|
||||
|
||||
impl FileBookmarks {
|
||||
#[inline]
|
||||
pub fn open<P: Into<PathBuf>>(path: P) -> Result<Self> {
|
||||
Ok(FileBookmarks {
|
||||
kv: FileKV::open(path, PREFIX)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn open_with_pool<P: Into<PathBuf>>(path: P, pool: Arc<CpuPool>) -> Result<Self> {
|
||||
Ok(FileBookmarks {
|
||||
kv: FileKV::open_with_pool(path, PREFIX, pool)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create<P: Into<PathBuf>>(path: P) -> Result<Self> {
|
||||
Ok(FileBookmarks {
|
||||
kv: FileKV::create(path, PREFIX)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_with_pool<P: Into<PathBuf>>(path: P, pool: Arc<CpuPool>) -> Result<Self> {
|
||||
Ok(FileBookmarks {
|
||||
kv: FileKV::create_with_pool(path, PREFIX, pool)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn encode_key(key: &AsRef<[u8]>) -> String {
|
||||
percent_encode(key.as_ref(), DEFAULT_ENCODE_SET).to_string()
|
||||
}
|
||||
|
||||
impl Bookmarks for FileBookmarks {
|
||||
#[inline]
|
||||
fn get(&self, name: &AsRef<[u8]>) -> BoxFuture<Option<(HgChangesetId, Version)>, Error> {
|
||||
self.kv
|
||||
.get(encode_key(name))
|
||||
.map_err(|e| e.context("FileBookmarks get failed").into())
|
||||
.boxify()
|
||||
}
|
||||
|
||||
fn keys(&self) -> BoxStream<Vec<u8>, Error> {
|
||||
self.kv
|
||||
.keys()
|
||||
.and_then(|name| Ok(percent_decode(&name[..].as_bytes()).collect()))
|
||||
.map_err(|e| e.context("FileBookmarks keys failed").into())
|
||||
.boxify()
|
||||
}
|
||||
}
|
||||
|
||||
impl BookmarksMut for FileBookmarks {
|
||||
#[inline]
|
||||
fn set(
|
||||
&self,
|
||||
key: &AsRef<[u8]>,
|
||||
value: &HgChangesetId,
|
||||
version: &Version,
|
||||
) -> BoxFuture<Option<Version>, Error> {
|
||||
self.kv
|
||||
.set(encode_key(key), value, version, None)
|
||||
.map_err(|e| e.context("FileBookmarks set failed").into())
|
||||
.boxify()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn delete(&self, key: &AsRef<[u8]>, version: &Version) -> BoxFuture<Option<Version>, Error> {
|
||||
self.kv
|
||||
.delete(encode_key(key), version)
|
||||
.map_err(|e| e.context("FileBookmarks delete failed").into())
|
||||
.boxify()
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
// Copyright (c) 2004-present, Facebook, Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// This software may be used and distributed according to the terms of the
|
||||
// GNU General Public License version 2 or any later version.
|
||||
|
||||
#![feature(never_type)]
|
||||
|
||||
extern crate bookmarks;
|
||||
extern crate failure;
|
||||
extern crate futures;
|
||||
extern crate futures_ext;
|
||||
extern crate mercurial_types;
|
||||
extern crate storage_types;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
|
||||
use failure::Error;
|
||||
use futures::future::ok;
|
||||
use futures::stream::iter_ok;
|
||||
|
||||
use bookmarks::{Bookmarks, BookmarksMut};
|
||||
use futures_ext::{BoxFuture, BoxStream, FutureExt, StreamExt};
|
||||
use mercurial_types::nodehash::HgChangesetId;
|
||||
use storage_types::Version;
|
||||
|
||||
static VERSION_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
fn version_next() -> Version {
|
||||
Version::from(VERSION_COUNTER.fetch_add(1, Ordering::Relaxed) as u64)
|
||||
}
|
||||
|
||||
/// In-memory bookmark store backed by a HashMap, intended to be used in tests.
|
||||
pub struct MemBookmarks {
|
||||
bookmarks: Mutex<HashMap<Vec<u8>, (HgChangesetId, Version)>>,
|
||||
}
|
||||
|
||||
impl MemBookmarks {
|
||||
pub fn new() -> Self {
|
||||
MemBookmarks {
|
||||
bookmarks: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bookmarks for MemBookmarks {
|
||||
fn get(&self, key: &AsRef<[u8]>) -> BoxFuture<Option<(HgChangesetId, Version)>, Error> {
|
||||
ok(self.bookmarks
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(key.as_ref())
|
||||
.map(Clone::clone))
|
||||
.boxify()
|
||||
}
|
||||
|
||||
fn keys(&self) -> BoxStream<Vec<u8>, Error> {
|
||||
let guard = self.bookmarks.lock().unwrap();
|
||||
let keys = guard.keys().map(|k| k.clone()).collect::<Vec<_>>();
|
||||
iter_ok(keys.into_iter()).boxify()
|
||||
}
|
||||
}
|
||||
|
||||
impl BookmarksMut for MemBookmarks {
|
||||
fn set(
|
||||
&self,
|
||||
key: &AsRef<[u8]>,
|
||||
value: &HgChangesetId,
|
||||
version: &Version,
|
||||
) -> BoxFuture<Option<Version>, Error> {
|
||||
let mut bookmarks = self.bookmarks.lock().unwrap();
|
||||
|
||||
match bookmarks.entry(key.as_ref().to_vec()) {
|
||||
Entry::Occupied(mut entry) => if *version == entry.get().1 {
|
||||
let new = version_next();
|
||||
entry.insert((value.clone(), new));
|
||||
ok(Some(new))
|
||||
} else {
|
||||
ok(None)
|
||||
},
|
||||
Entry::Vacant(entry) => if *version == Version::absent() {
|
||||
let new = version_next();
|
||||
entry.insert((value.clone(), new));
|
||||
ok(Some(new))
|
||||
} else {
|
||||
ok(None)
|
||||
},
|
||||
}.boxify()
|
||||
}
|
||||
|
||||
fn delete(&self, key: &AsRef<[u8]>, version: &Version) -> BoxFuture<Option<Version>, Error> {
|
||||
let mut bookmarks = self.bookmarks.lock().unwrap();
|
||||
|
||||
match bookmarks.entry(key.as_ref().to_vec()) {
|
||||
Entry::Occupied(entry) => if *version == entry.get().1 {
|
||||
entry.remove();
|
||||
ok(Some(Version::absent()))
|
||||
} else {
|
||||
ok(None)
|
||||
},
|
||||
Entry::Vacant(_) => if *version == Version::absent() {
|
||||
ok(Some(Version::absent()))
|
||||
} else {
|
||||
ok(None)
|
||||
},
|
||||
}.boxify()
|
||||
}
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
// Copyright (c) 2017-present, Facebook, Inc.
|
||||
// All Rights Reserved.
|
||||
//
|
||||
// This software may be used and distributed according to the terms of the
|
||||
// GNU General Public License version 2 or any later version.
|
||||
|
||||
//! Tests run against all bookmarks implementations.
|
||||
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate tempdir;
|
||||
extern crate tokio_core;
|
||||
|
||||
extern crate bookmarks;
|
||||
extern crate db;
|
||||
extern crate dbbookmarks;
|
||||
extern crate filebookmarks;
|
||||
extern crate membookmarks;
|
||||
extern crate mercurial_types;
|
||||
extern crate mercurial_types_mocks;
|
||||
extern crate storage_types;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::Stream;
|
||||
use tempdir::TempDir;
|
||||
use tokio_core::reactor::Core;
|
||||
|
||||
use bookmarks::BookmarksMut;
|
||||
use dbbookmarks::DbBookmarks;
|
||||
use filebookmarks::FileBookmarks;
|
||||
use membookmarks::MemBookmarks;
|
||||
use mercurial_types::nodehash::HgChangesetId;
|
||||
use mercurial_types_mocks::nodehash;
|
||||
use storage_types::Version;
|
||||
|
||||
fn basic<B>(bookmarks: B, core: &mut Core)
|
||||
where
|
||||
B: BookmarksMut,
|
||||
{
|
||||
let foo = b"foo";
|
||||
let one = HgChangesetId::new(nodehash::ONES_HASH);
|
||||
let two = HgChangesetId::new(nodehash::TWOS_HASH);
|
||||
let three = HgChangesetId::new(nodehash::THREES_HASH);
|
||||
|
||||
assert_eq!(core.run(bookmarks.get(&foo)).unwrap(), None);
|
||||
|
||||
let absent = Version::absent();
|
||||
let foo_v1 = core.run(bookmarks.set(&foo, &one, &absent))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
core.run(bookmarks.get(&foo)).unwrap(),
|
||||
Some((one.clone(), foo_v1))
|
||||
);
|
||||
|
||||
let foo_v2 = core.run(bookmarks.set(&foo, &two, &foo_v1))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
// Should fail due to version mismatch.
|
||||
assert_eq!(
|
||||
core.run(bookmarks.set(&foo, &three, &foo_v1)).unwrap(),
|
||||
None
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
core.run(bookmarks.delete(&foo, &foo_v2)).unwrap().unwrap(),
|
||||
absent
|
||||
);
|
||||
assert_eq!(core.run(bookmarks.get(&foo)).unwrap(), None);
|
||||
|
||||
// Even though bookmark doesn't exist, this should fail with a version mismatch.
|
||||
assert_eq!(core.run(bookmarks.delete(&foo, &foo_v2)).unwrap(), None);
|
||||
|
||||
// Deleting it with the absent version should work.
|
||||
assert_eq!(
|
||||
core.run(bookmarks.delete(&foo, &absent)).unwrap().unwrap(),
|
||||
absent
|
||||
);
|
||||
}
|
||||
|
||||
fn list<B>(bookmarks: B, core: &mut Core)
|
||||
where
|
||||
B: BookmarksMut,
|
||||
{
|
||||
let one = b"1";
|
||||
let two = b"2";
|
||||
let three = b"3";
|
||||
let hash = HgChangesetId::new(nodehash::ONES_HASH);
|
||||
|
||||
let _ = core.run(bookmarks.create(&one, &hash)).unwrap().unwrap();
|
||||
let _ = core.run(bookmarks.create(&two, &hash)).unwrap().unwrap();
|
||||
let _ = core.run(bookmarks.create(&three, &hash)).unwrap().unwrap();
|
||||
|
||||
let mut result = core.run(bookmarks.keys().collect()).unwrap();
|
||||
result.sort();
|
||||
|
||||
let expected = vec![one, two, three];
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
fn persistence<F, B>(mut new_bookmarks: F, core: Rc<RefCell<Core>>)
|
||||
where
|
||||
F: FnMut() -> B,
|
||||
B: BookmarksMut,
|
||||
{
|
||||
let foo = b"foo";
|
||||
let bar = HgChangesetId::new(nodehash::ONES_HASH);
|
||||
|
||||
let version = {
|
||||
let bookmarks = new_bookmarks();
|
||||
core.borrow_mut()
|
||||
.run(bookmarks.create(&foo, &bar))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let bookmarks = new_bookmarks();
|
||||
assert_eq!(
|
||||
core.borrow_mut().run(bookmarks.get(&foo)).unwrap(),
|
||||
Some((bar, version))
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! bookmarks_test_impl {
|
||||
($mod_name: ident => {
|
||||
state: $state: expr,
|
||||
new: $new_cb: expr,
|
||||
persistent: $persistent: expr,
|
||||
}) => {
|
||||
mod $mod_name {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
let mut core = Core::new().unwrap();
|
||||
let state = $state;
|
||||
let bookmarks = $new_cb(&state, &mut core);
|
||||
basic(bookmarks, &mut core);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list() {
|
||||
let mut core = Core::new().unwrap();
|
||||
let state = $state;
|
||||
let bookmarks = $new_cb(&state, &mut core);
|
||||
list(bookmarks, &mut core);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_persistence() {
|
||||
// Not all bookmark implementations support persistence.
|
||||
if $persistent {
|
||||
let core = Rc::new(RefCell::new(Core::new().unwrap()));
|
||||
let state = $state;
|
||||
let new_bookmarks = {
|
||||
let core = Rc::clone(&core);
|
||||
move || {
|
||||
$new_cb(&state, &mut *core.borrow_mut())
|
||||
}
|
||||
};
|
||||
persistence(new_bookmarks, core);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bookmarks_test_impl! {
|
||||
membookmarks_test => {
|
||||
state: (),
|
||||
new: |_, _| MemBookmarks::new(),
|
||||
persistent: false,
|
||||
}
|
||||
}
|
||||
|
||||
bookmarks_test_impl! {
|
||||
filebookmarks_test => {
|
||||
state: TempDir::new("filebookmarks_test").unwrap(),
|
||||
new: |dir: &TempDir, _| FileBookmarks::open(dir.as_ref()).unwrap(),
|
||||
persistent: true,
|
||||
}
|
||||
}
|
||||
|
||||
bookmarks_test_impl! {
|
||||
dbbookmarks_test => {
|
||||
state: dbbookmarks::init_test_db(),
|
||||
new: |params: &db::ConnectionParams, core: &mut Core| {
|
||||
let params = params.clone();
|
||||
let remote = core.remote();
|
||||
core.run(DbBookmarks::new_async(params, &remote)).unwrap()
|
||||
},
|
||||
persistent: true,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user