use alloc::string::String;
use core::{
cell::{Ref, RefMut},
default::Default,
option::Option,
time::Duration,
};
#[cfg(feature = "std")]
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use super::Corpus;
use crate::{
bolts::{serdeany::SerdeAnyMap, HasLen},
corpus::CorpusId,
inputs::{Input, UsesInput},
state::HasMetadata,
Error,
};
pub trait HasTestcase: UsesInput {
fn testcase(&self, id: CorpusId) -> Result<Ref<Testcase<<Self as UsesInput>::Input>>, Error>;
fn testcase_mut(
&self,
id: CorpusId,
) -> Result<RefMut<Testcase<<Self as UsesInput>::Input>>, Error>;
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct Testcase<I>
where
I: Input,
{
input: Option<I>,
filename: Option<String>,
#[cfg(feature = "std")]
file_path: Option<PathBuf>,
metadata: SerdeAnyMap,
#[cfg(feature = "std")]
metadata_path: Option<PathBuf>,
exec_time: Option<Duration>,
cached_len: Option<usize>,
executions: usize,
scheduled_count: usize,
parent_id: Option<CorpusId>,
}
impl<I> HasMetadata for Testcase<I>
where
I: Input,
{
#[inline]
fn metadata_map(&self) -> &SerdeAnyMap {
&self.metadata
}
#[inline]
fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap {
&mut self.metadata
}
}
impl<I> Testcase<I>
where
I: Input,
{
pub fn load_input<C: Corpus<Input = I>>(&mut self, corpus: &C) -> Result<&I, Error> {
corpus.load_input_into(self)?;
Ok(self.input.as_ref().unwrap())
}
#[inline]
pub fn input(&self) -> &Option<I> {
&self.input
}
#[inline]
pub fn input_mut(&mut self) -> &mut Option<I> {
&mut self.input
}
#[inline]
pub fn set_input(&mut self, mut input: I) {
input.wrapped_as_testcase();
self.input = Some(input);
}
#[inline]
pub fn filename(&self) -> &Option<String> {
&self.filename
}
#[inline]
pub fn filename_mut(&mut self) -> &mut Option<String> {
&mut self.filename
}
#[inline]
#[cfg(feature = "std")]
pub fn file_path(&self) -> &Option<PathBuf> {
&self.file_path
}
#[inline]
#[cfg(feature = "std")]
pub fn file_path_mut(&mut self) -> &mut Option<PathBuf> {
&mut self.file_path
}
#[inline]
#[cfg(feature = "std")]
pub fn metadata_path(&self) -> &Option<PathBuf> {
&self.metadata_path
}
#[inline]
#[cfg(feature = "std")]
pub fn metadata_path_mut(&mut self) -> &mut Option<PathBuf> {
&mut self.metadata_path
}
#[inline]
pub fn exec_time(&self) -> &Option<Duration> {
&self.exec_time
}
#[inline]
pub fn exec_time_mut(&mut self) -> &mut Option<Duration> {
&mut self.exec_time
}
#[inline]
pub fn set_exec_time(&mut self, time: Duration) {
self.exec_time = Some(time);
}
#[inline]
pub fn executions(&self) -> &usize {
&self.executions
}
#[inline]
pub fn executions_mut(&mut self) -> &mut usize {
&mut self.executions
}
#[inline]
pub fn scheduled_count(&self) -> usize {
self.scheduled_count
}
#[inline]
pub fn set_scheduled_count(&mut self, scheduled_count: usize) {
self.scheduled_count = scheduled_count;
}
#[inline]
pub fn new(mut input: I) -> Self {
input.wrapped_as_testcase();
Self {
input: Some(input),
..Testcase::default()
}
}
pub fn with_parent_id(mut input: I, parent_id: CorpusId) -> Self {
input.wrapped_as_testcase();
Self {
input: Some(input),
parent_id: Some(parent_id),
..Testcase::default()
}
}
#[inline]
pub fn with_filename(mut input: I, filename: String) -> Self {
input.wrapped_as_testcase();
Self {
input: Some(input),
filename: Some(filename),
..Testcase::default()
}
}
#[inline]
pub fn with_executions(mut input: I, executions: usize) -> Self {
input.wrapped_as_testcase();
Self {
input: Some(input),
executions,
..Testcase::default()
}
}
#[must_use]
pub fn parent_id(&self) -> Option<CorpusId> {
self.parent_id
}
pub fn set_parent_id(&mut self, parent_id: CorpusId) {
self.parent_id = Some(parent_id);
}
pub fn set_parent_id_optional(&mut self, parent_id: Option<CorpusId>) {
self.parent_id = parent_id;
}
}
impl<I> Default for Testcase<I>
where
I: Input,
{
#[inline]
fn default() -> Self {
Testcase {
input: None,
filename: None,
metadata: SerdeAnyMap::new(),
exec_time: None,
cached_len: None,
scheduled_count: 0,
executions: 0,
parent_id: None,
#[cfg(feature = "std")]
file_path: None,
#[cfg(feature = "std")]
metadata_path: None,
}
}
}
impl<I> Testcase<I>
where
I: Input + HasLen,
{
#[inline]
pub fn cached_len(&mut self) -> Option<usize> {
self.cached_len
}
#[allow(clippy::len_without_is_empty)]
pub fn load_len<C: Corpus<Input = I>>(&mut self, corpus: &C) -> Result<usize, Error> {
match &self.input {
Some(i) => {
let l = i.len();
self.cached_len = Some(l);
Ok(l)
}
None => {
if let Some(l) = self.cached_len {
Ok(l)
} else {
corpus.load_input_into(self)?;
self.load_len(corpus)
}
}
}
}
}
impl<I> From<I> for Testcase<I>
where
I: Input,
{
fn from(input: I) -> Self {
Testcase::new(input)
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SchedulerTestcaseMetadata {
bitmap_size: u64,
handicap: u64,
depth: u64,
n_fuzz_entry: usize,
cycle_and_time: (Duration, usize),
}
impl SchedulerTestcaseMetadata {
#[must_use]
pub fn new(depth: u64) -> Self {
Self {
bitmap_size: 0,
handicap: 0,
depth,
n_fuzz_entry: 0,
cycle_and_time: (Duration::default(), 0),
}
}
#[must_use]
pub fn with_n_fuzz_entry(depth: u64, n_fuzz_entry: usize) -> Self {
Self {
bitmap_size: 0,
handicap: 0,
depth,
n_fuzz_entry,
cycle_and_time: (Duration::default(), 0),
}
}
#[inline]
#[must_use]
pub fn bitmap_size(&self) -> u64 {
self.bitmap_size
}
#[inline]
pub fn set_bitmap_size(&mut self, val: u64) {
self.bitmap_size = val;
}
#[inline]
#[must_use]
pub fn handicap(&self) -> u64 {
self.handicap
}
#[inline]
pub fn set_handicap(&mut self, val: u64) {
self.handicap = val;
}
#[inline]
#[must_use]
pub fn depth(&self) -> u64 {
self.depth
}
#[inline]
pub fn set_depth(&mut self, val: u64) {
self.depth = val;
}
#[inline]
#[must_use]
pub fn n_fuzz_entry(&self) -> usize {
self.n_fuzz_entry
}
#[inline]
pub fn set_n_fuzz_entry(&mut self, val: usize) {
self.n_fuzz_entry = val;
}
#[inline]
#[must_use]
pub fn cycle_and_time(&self) -> (Duration, usize) {
self.cycle_and_time
}
#[inline]
pub fn set_cycle_and_time(&mut self, cycle_and_time: (Duration, usize)) {
self.cycle_and_time = cycle_and_time;
}
}
crate::impl_serdeany!(SchedulerTestcaseMetadata);
#[cfg(feature = "python")]
#[allow(missing_docs)]
pub mod pybind {
use alloc::{boxed::Box, vec::Vec};
use pyo3::{prelude::*, types::PyDict};
use super::{HasMetadata, Testcase};
use crate::{bolts::ownedref::OwnedMutPtr, inputs::BytesInput, pybind::PythonMetadata};
pub type PythonTestcase = Testcase<BytesInput>;
#[pyclass(unsendable, name = "Testcase")]
#[derive(Debug)]
pub struct PythonTestcaseWrapper {
pub inner: OwnedMutPtr<PythonTestcase>,
}
impl PythonTestcaseWrapper {
pub fn wrap(r: &mut PythonTestcase) -> Self {
Self {
inner: OwnedMutPtr::Ptr(r),
}
}
#[must_use]
pub fn unwrap(&self) -> &PythonTestcase {
self.inner.as_ref()
}
pub fn unwrap_mut(&mut self) -> &mut PythonTestcase {
self.inner.as_mut()
}
}
#[pymethods]
impl PythonTestcaseWrapper {
#[new]
fn new(input: Vec<u8>) -> Self {
Self {
inner: OwnedMutPtr::Owned(Box::new(PythonTestcase::new(BytesInput::new(input)))),
}
}
#[getter]
fn exec_time_ms(&self) -> Option<u128> {
self.inner.as_ref().exec_time().map(|t| t.as_millis())
}
#[getter]
fn executions(&self) -> usize {
*self.inner.as_ref().executions()
}
#[getter]
fn parent_id(&self) -> Option<usize> {
self.inner.as_ref().parent_id().map(|x| x.0)
}
#[getter]
fn scheduled_count(&self) -> usize {
self.inner.as_ref().scheduled_count()
}
fn metadata(&mut self) -> PyObject {
let meta = self.inner.as_mut().metadata_map_mut();
if !meta.contains::<PythonMetadata>() {
Python::with_gil(|py| {
let dict: Py<PyDict> = PyDict::new(py).into();
meta.insert(PythonMetadata::new(dict.to_object(py)));
});
}
meta.get::<PythonMetadata>().unwrap().map.clone()
}
}
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<PythonTestcaseWrapper>()?;
Ok(())
}
}