##// END OF EJS Templates
copies-rust: add a macro-based unit-testing framework...
copies-rust: add a macro-based unit-testing framework `compare_values`, `merge_copies_dict`, and `CombineChangesetCopies` are APIs whose signatures involve non-trivial types. Calling them directly in unit tests would involve a lot of verbose setup code that obscures the meaningful parts of a given test case. This adds a macro-based test-harness with pseudo-syntax to tersely create arguments and expected return values in the correct types. For now there is only one (not particularly meaningful) test case per tested function, just to exercize the macros. Differential Revision: https://phab.mercurial-scm.org/D10071

File last commit:

r47169:776b9717 default
r47405:b92083ad default
Show More
dirs_multiset.rs
140 lines | 4.2 KiB | application/rls-services+xml | RustLexer
// dirs_multiset.rs
//
// Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
//! Bindings for the `hg::dirstate::dirs_multiset` file provided by the
//! `hg-core` package.
use std::cell::RefCell;
use std::convert::TryInto;
use cpython::{
exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult,
Python, UnsafePyLeaked,
};
use crate::dirstate::extract_dirstate;
use hg::{
errors::HgError,
utils::hg_path::{HgPath, HgPathBuf},
DirsMultiset, DirsMultisetIter, DirstateMapError, EntryState,
};
py_class!(pub class Dirs |py| {
@shared data inner: DirsMultiset;
// `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
// a `list`)
def __new__(
_cls,
map: PyObject,
skip: Option<PyObject> = None
) -> PyResult<Self> {
let mut skip_state: Option<EntryState> = None;
if let Some(skip) = skip {
skip_state = Some(
skip.extract::<PyBytes>(py)?.data(py)[0]
.try_into()
.map_err(|e: HgError| {
PyErr::new::<exc::ValueError, _>(py, e.to_string())
})?,
);
}
let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
let dirstate = extract_dirstate(py, &map)?;
DirsMultiset::from_dirstate(&dirstate, skip_state)
.map_err(|e: DirstateMapError| {
PyErr::new::<exc::ValueError, _>(py, e.to_string())
})?
} else {
let map: Result<Vec<HgPathBuf>, PyErr> = map
.iter(py)?
.map(|o| {
Ok(HgPathBuf::from_bytes(
o?.extract::<PyBytes>(py)?.data(py),
))
})
.collect();
DirsMultiset::from_manifest(&map?)
.map_err(|e| {
PyErr::new::<exc::ValueError, _>(py, e.to_string())
})?
};
Self::create_instance(py, inner)
}
def addpath(&self, path: PyObject) -> PyResult<PyObject> {
self.inner(py).borrow_mut().add_path(
HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
).and(Ok(py.None())).or_else(|e| {
match e {
DirstateMapError::EmptyPath => {
Ok(py.None())
},
e => {
Err(PyErr::new::<exc::ValueError, _>(
py,
e.to_string(),
))
}
}
})
}
def delpath(&self, path: PyObject) -> PyResult<PyObject> {
self.inner(py).borrow_mut().delete_path(
HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
)
.and(Ok(py.None()))
.or_else(|e| {
match e {
DirstateMapError::EmptyPath => {
Ok(py.None())
},
e => {
Err(PyErr::new::<exc::ValueError, _>(
py,
e.to_string(),
))
}
}
})
}
def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
let leaked_ref = self.inner(py).leak_immutable();
DirsMultisetKeysIterator::from_inner(
py,
unsafe { leaked_ref.map(py, |o| o.iter()) },
)
}
def __contains__(&self, item: PyObject) -> PyResult<bool> {
Ok(self.inner(py).borrow().contains(HgPath::new(
item.extract::<PyBytes>(py)?.data(py).as_ref(),
)))
}
});
impl Dirs {
pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
Self::create_instance(py, d)
}
fn translate_key(
py: Python,
res: &HgPathBuf,
) -> PyResult<Option<PyBytes>> {
Ok(Some(PyBytes::new(py, res.as_bytes())))
}
}
py_shared_iterator!(
DirsMultisetKeysIterator,
UnsafePyLeaked<DirsMultisetIter<'static>>,
Dirs::translate_key,
Option<PyBytes>
);