mirror of
https://github.com/icewind1991/ivory.git
synced 2026-06-03 18:54:07 +02:00
tests and optional arguments
This commit is contained in:
parent
aa1e5d7dca
commit
b8cd60fff5
10 changed files with 356 additions and 37 deletions
|
|
@ -15,4 +15,4 @@ ivory-sys = { version = "7.3", path = "sys" }
|
||||||
[lib]
|
[lib]
|
||||||
name = "ivory"
|
name = "ivory"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,11 @@ fn export_fn(item: ItemFn) -> TokenStream {
|
||||||
let arg_ident = Ident::new(name, span.clone());
|
let arg_ident = Ident::new(name, span.clone());
|
||||||
quote!(
|
quote!(
|
||||||
let #arg_ident: #ty = {
|
let #arg_ident: #ty = {
|
||||||
let opt: Option<#ty> = args.next().unwrap().into();
|
let result: Result<#ty, ::ivory::CastError> = args.next().unwrap().into();
|
||||||
match opt {
|
match result {
|
||||||
Some(val) => val,
|
Ok(val) => val,
|
||||||
None => {
|
Err(err) => {
|
||||||
::ivory::externs::error(::ivory::externs::ErrorLevel::Error, "invalid argument type,");
|
::ivory::externs::error(::ivory::externs::ErrorLevel::Error, format!("{}", err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +69,9 @@ fn export_fn(item: ItemFn) -> TokenStream {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn #name(data: *const ::ivory::zend::ExecuteData, retval: *mut ::ivory::zend::ZVal) {
|
pub extern "C" fn #name(data: *const ::ivory::zend::ExecuteData, retval: *mut ::ivory::zend::ZVal) {
|
||||||
let data: &::ivory::zend::ExecuteData = unsafe { data.as_ref() }.unwrap();
|
let data: &::ivory::zend::ExecuteData = unsafe { data.as_ref() }.unwrap();
|
||||||
if data.num_args() != #arg_count {
|
// the less than case is handled during argument casting
|
||||||
|
// this is needed for optional arguments
|
||||||
|
if data.num_args() > #arg_count {
|
||||||
::ivory::externs::error(::ivory::externs::ErrorLevel::Error, format!("unexpected number of arguments, expected {}, got {}", #arg_count, data.num_args()));
|
::ivory::externs::error(::ivory::externs::ErrorLevel::Error, format!("unexpected number of arguments, expected {}, got {}", #arg_count, data.num_args()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,5 @@ pub mod macros;
|
||||||
pub mod externs;
|
pub mod externs;
|
||||||
pub mod info;
|
pub mod info;
|
||||||
pub mod zend;
|
pub mod zend;
|
||||||
|
pub use crate::zend::{ArgError, ArrayKey, CastError, PhpVal};
|
||||||
pub use ivory_macro::{ivory_export, ivory_module};
|
pub use ivory_macro::{ivory_export, ivory_module};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
pub use self::module::*;
|
|
||||||
pub use self::function::*;
|
pub use self::function::*;
|
||||||
pub use self::zval::{ExecuteData, ZVal, PhpVal};
|
pub use self::module::*;
|
||||||
|
pub use self::zval::{ArgError, ArrayKey, CastError, ExecuteData, PhpVal, ZVal};
|
||||||
|
|
||||||
mod module;
|
|
||||||
mod function;
|
mod function;
|
||||||
|
mod module;
|
||||||
mod zval;
|
mod zval;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Display;
|
||||||
use std::intrinsics::transmute;
|
use std::intrinsics::transmute;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use ivory_sys::{zend_execute_data, zval, zend_string};
|
use ivory_sys::{zend_execute_data, zend_string, zval};
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct ExecuteData(zend_execute_data);
|
pub struct ExecuteData(zend_execute_data);
|
||||||
|
|
@ -17,9 +20,7 @@ impl ExecuteData {
|
||||||
fn get_arg_base(&self) -> *const ZVal {
|
fn get_arg_base(&self) -> *const ZVal {
|
||||||
let offset = (size_of::<zend_execute_data>() + size_of::<zval>() - 1) / size_of::<zval>();
|
let offset = (size_of::<zend_execute_data>() + size_of::<zval>() - 1) / size_of::<zval>();
|
||||||
let self_ptr: *const zend_execute_data = &self.0;
|
let self_ptr: *const zend_execute_data = &self.0;
|
||||||
unsafe {
|
unsafe { transmute::<_, *const ZVal>(self_ptr).add(offset) }
|
||||||
transmute::<_, *const ZVal>(self_ptr).add(offset)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn get_arg(&self, i: u32) -> &ZVal {
|
pub unsafe fn get_arg(&self, i: u32) -> &ZVal {
|
||||||
|
|
@ -52,7 +53,7 @@ impl Iterator for IntoArgIterator {
|
||||||
self.item += 1;
|
self.item += 1;
|
||||||
Some(val)
|
Some(val)
|
||||||
} else {
|
} else {
|
||||||
None
|
Some(PhpVal::Undef)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +100,7 @@ impl ZVal {
|
||||||
let val: PhpVal = ZVal(elem.val).as_php_val();
|
let val: PhpVal = ZVal(elem.val).as_php_val();
|
||||||
match val {
|
match val {
|
||||||
PhpVal::Undef => {}
|
PhpVal::Undef => {}
|
||||||
_ => result.push((key, val))
|
_ => result.push((key, val)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
|
|
@ -115,7 +116,7 @@ impl ZVal {
|
||||||
ZValType::Double => PhpVal::Double(unsafe { self.as_f64() }),
|
ZValType::Double => PhpVal::Double(unsafe { self.as_f64() }),
|
||||||
ZValType::String => PhpVal::String(unsafe { self.as_str() }),
|
ZValType::String => PhpVal::String(unsafe { self.as_str() }),
|
||||||
ZValType::Array => PhpVal::Array(unsafe { self.as_array() }),
|
ZValType::Array => PhpVal::Array(unsafe { self.as_array() }),
|
||||||
_ => PhpVal::Undef
|
_ => PhpVal::Undef,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +137,24 @@ pub enum ZValType {
|
||||||
Reference = 10,
|
Reference = 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for ZValType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ZValType::Undef => write!(f, "undefined"),
|
||||||
|
ZValType::Null => write!(f, "null"),
|
||||||
|
ZValType::False => write!(f, "bool"),
|
||||||
|
ZValType::True => write!(f, "bool"),
|
||||||
|
ZValType::Long => write!(f, "long"),
|
||||||
|
ZValType::Double => write!(f, "double"),
|
||||||
|
ZValType::String => write!(f, "string"),
|
||||||
|
ZValType::Array => write!(f, "array"),
|
||||||
|
ZValType::Object => write!(f, "object"),
|
||||||
|
ZValType::Resource => write!(f, "resource"),
|
||||||
|
ZValType::Reference => write!(f, "reference"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u8> for ZValType {
|
impl From<u8> for ZValType {
|
||||||
fn from(val: u8) -> Self {
|
fn from(val: u8) -> Self {
|
||||||
if val > 10 {
|
if val > 10 {
|
||||||
|
|
@ -151,6 +170,24 @@ pub enum ArrayKey {
|
||||||
Int(u64),
|
Int(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<String> for ArrayKey {
|
||||||
|
fn from(input: String) -> Self {
|
||||||
|
ArrayKey::String(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for ArrayKey {
|
||||||
|
fn from(input: u64) -> Self {
|
||||||
|
ArrayKey::Int(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for ArrayKey {
|
||||||
|
fn from(input: usize) -> Self {
|
||||||
|
ArrayKey::Int(input as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PhpVal {
|
pub enum PhpVal {
|
||||||
Undef,
|
Undef,
|
||||||
|
|
@ -165,44 +202,164 @@ pub enum PhpVal {
|
||||||
Reference(),
|
Reference(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PhpVal {
|
||||||
|
pub fn get_type(&self) -> ZValType {
|
||||||
|
match self {
|
||||||
|
PhpVal::Undef => ZValType::Undef,
|
||||||
|
PhpVal::Null => ZValType::Null,
|
||||||
|
PhpVal::Bool(true) => ZValType::True,
|
||||||
|
PhpVal::Bool(false) => ZValType::False,
|
||||||
|
PhpVal::Long(_) => ZValType::Long,
|
||||||
|
PhpVal::Double(_) => ZValType::Double,
|
||||||
|
PhpVal::String(_) => ZValType::String,
|
||||||
|
PhpVal::Array(_) => ZValType::Array,
|
||||||
|
PhpVal::Object(_) => ZValType::Object,
|
||||||
|
PhpVal::Resource(_) => ZValType::Resource,
|
||||||
|
PhpVal::Reference() => ZValType::Reference,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for PhpVal {
|
impl Default for PhpVal {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
PhpVal::Undef
|
PhpVal::Undef
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PhpVal> for Option<i64> {
|
impl From<PhpVal> for Result<PhpVal, CastError> {
|
||||||
fn from(val: PhpVal) -> Self {
|
fn from(val: PhpVal) -> Self {
|
||||||
match val {
|
Ok(val)
|
||||||
PhpVal::Long(val) => Some(val),
|
}
|
||||||
_ => None
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_from_phpval {
|
||||||
|
($type:ty, $variant:ident) => {
|
||||||
|
// non nullable version
|
||||||
|
impl From<PhpVal> for Result<$type, CastError> {
|
||||||
|
fn from(val: PhpVal) -> Self {
|
||||||
|
match val {
|
||||||
|
PhpVal::$variant(val) => Ok(val),
|
||||||
|
_ => Err(CastError {
|
||||||
|
actual: val.get_type(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nullable version
|
||||||
|
impl From<PhpVal> for Result<Option<$type>, CastError> {
|
||||||
|
fn from(val: PhpVal) -> Self {
|
||||||
|
match val {
|
||||||
|
PhpVal::Null => Ok(None),
|
||||||
|
PhpVal::Undef => Ok(None),
|
||||||
|
PhpVal::$variant(val) => Ok(Some(val)),
|
||||||
|
_ => Err(CastError {
|
||||||
|
actual: val.get_type(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_phpval!(i64, Long);
|
||||||
|
impl_from_phpval!(f64, Double);
|
||||||
|
impl_from_phpval!(bool, Bool);
|
||||||
|
impl_from_phpval!(String, String);
|
||||||
|
|
||||||
|
impl From<i64> for PhpVal {
|
||||||
|
fn from(input: i64) -> Self {
|
||||||
|
PhpVal::Long(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for PhpVal {
|
||||||
|
fn from(input: String) -> Self {
|
||||||
|
PhpVal::String(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for PhpVal {
|
||||||
|
fn from(input: bool) -> Self {
|
||||||
|
PhpVal::Bool(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Into<PhpVal>> From<Option<T>> for PhpVal {
|
||||||
|
fn from(input: Option<T>) -> Self {
|
||||||
|
match input {
|
||||||
|
Some(inner) => inner.into(),
|
||||||
|
None => PhpVal::Null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PhpVal> for Option<f64> {
|
impl<T: Into<PhpVal>> From<Vec<T>> for PhpVal {
|
||||||
fn from(val: PhpVal) -> Self {
|
fn from(input: Vec<T>) -> Self {
|
||||||
match val {
|
PhpVal::Array(
|
||||||
PhpVal::Double(val) => Some(val),
|
input
|
||||||
_ => None
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(key, value)| (key.into(), value.into()))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Into<ArrayKey>, T: Into<PhpVal>> From<Vec<(K, T)>> for PhpVal {
|
||||||
|
fn from(input: Vec<(K, T)>) -> Self {
|
||||||
|
PhpVal::Array(
|
||||||
|
input
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, value)| (key.into(), value.into()))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CastError {
|
||||||
|
actual: ZValType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ArgError {
|
||||||
|
CastError(CastError),
|
||||||
|
NotEnoughArguments,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CastError> for ArgError {
|
||||||
|
fn from(from: CastError) -> Self {
|
||||||
|
ArgError::CastError(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CastError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Incorrect variable type, got {}", self.actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ArgError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ArgError::CastError(err) => err.fmt(f),
|
||||||
|
ArgError::NotEnoughArguments => write!(f, "Not enough arugments"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PhpVal> for Option<bool> {
|
impl Error for CastError {
|
||||||
fn from(val: PhpVal) -> Self {
|
fn cause(&self) -> Option<&Error> {
|
||||||
match val {
|
None
|
||||||
PhpVal::Bool(val) => Some(val),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PhpVal> for Option<String> {
|
impl Error for ArgError {
|
||||||
fn from(val: PhpVal) -> Self {
|
fn cause(&self) -> Option<&Error> {
|
||||||
match val {
|
match self {
|
||||||
PhpVal::String(val) => Some(val),
|
ArgError::CastError(err) => Some(err),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
tests/Cargo.toml
Normal file
16
tests/Cargo.toml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "ivory-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ivory = { path = "../ivory", version = "0.1.0" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
maplit = "1.0"
|
||||||
|
pretty_assertions = "0.6"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "tests"
|
||||||
|
crate-type = ["dylib"]
|
||||||
1
tests/README.md
Normal file
1
tests/README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# Tests
|
||||||
53
tests/src/lib.rs
Normal file
53
tests/src/lib.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use ivory::externs::printf;
|
||||||
|
use ivory::PhpVal;
|
||||||
|
use ivory::{ivory_export, ivory_module};
|
||||||
|
|
||||||
|
fn dump<T: Debug>(arg: T) {
|
||||||
|
printf(format!("{:?}", arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn dump_arg(arg: PhpVal) {
|
||||||
|
dump(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn expect_long(arg: i64) {
|
||||||
|
dump(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn expect_double(arg: f64) {
|
||||||
|
dump(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn expect_string(arg: String) {
|
||||||
|
dump(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn expect_bool(arg: bool) {
|
||||||
|
dump(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ivory_export]
|
||||||
|
fn expect_option_bool(arg: Option<bool>) {
|
||||||
|
dump(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ivory_module!({
|
||||||
|
name: "tests",
|
||||||
|
version: "0.0.1",
|
||||||
|
functions: &[
|
||||||
|
dump_arg,
|
||||||
|
expect_long,
|
||||||
|
expect_double,
|
||||||
|
expect_string,
|
||||||
|
expect_bool,
|
||||||
|
expect_option_bool
|
||||||
|
],
|
||||||
|
info: &[("test extension", "enabled")]
|
||||||
|
});
|
||||||
89
tests/tests/tests.rs
Normal file
89
tests/tests/tests.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use ivory::{ArrayKey, PhpVal};
|
||||||
|
use maplit::hashmap;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zval_parsing() {
|
||||||
|
let inputs: HashMap<&str, PhpVal> = hashmap! {
|
||||||
|
"1" => PhpVal::Long(1),
|
||||||
|
"1.1" => PhpVal::Double(1.1),
|
||||||
|
"\"test\"" => PhpVal::String("test".into()),
|
||||||
|
"true" => PhpVal::Bool(true),
|
||||||
|
"false" => PhpVal::Bool(false),
|
||||||
|
"null" => PhpVal::Null,
|
||||||
|
"[1,2,3]" => vec![1, 2, 3].into(),
|
||||||
|
"[1,2,\"foo\"]" => vec![
|
||||||
|
PhpVal::Long(1),
|
||||||
|
PhpVal::Long(2),
|
||||||
|
PhpVal::String("foo".into())
|
||||||
|
].into(),
|
||||||
|
"[1,2, 4 => 3]" => vec![
|
||||||
|
(0u64, 1),
|
||||||
|
(1, 2),
|
||||||
|
(4, 3)
|
||||||
|
].into(),
|
||||||
|
"[1,2, \"foo\" => 3]" => vec![
|
||||||
|
(ArrayKey::from(0u64), 1),
|
||||||
|
(ArrayKey::from(1u64), 2),
|
||||||
|
(ArrayKey::from("foo".to_string()), 3)
|
||||||
|
].into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (input, expected) in inputs {
|
||||||
|
let code = format!("dump_arg({})", input);
|
||||||
|
let result = run_php(&code).unwrap();
|
||||||
|
assert_debug_eq(expected, &result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! test_cast {
|
||||||
|
($name:ident, $method:expr, $in:expr, $fail:expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $name() {
|
||||||
|
let result = run_php(&format!("{}({})", $method, $in)).unwrap();
|
||||||
|
assert_debug_eq($in, &result);
|
||||||
|
assert_eq!(true, run_php(&format!("{}({})", $method, $fail)).is_err());
|
||||||
|
assert_eq!(true, run_php(&format!("{}(null)", $method)).is_err());
|
||||||
|
assert_eq!(true, run_php(&format!("{}()", $method)).is_err());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test_cast!(test_cast_long, "expect_long", 1, false);
|
||||||
|
test_cast!(test_cast_double, "expect_double", 1.1, false);
|
||||||
|
test_cast!(test_cast_string, "expect_string", "foo".to_string(), false);
|
||||||
|
test_cast!(test_cast_bool, "expect_bool", true, 17);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cast_option() {
|
||||||
|
let result = run_php("expect_option_bool(true)").unwrap();
|
||||||
|
assert_debug_eq(Some(true), &result);
|
||||||
|
assert_eq!(true, run_php("expect_option_bool(17)").is_err());
|
||||||
|
let result = run_php("expect_option_bool(null)").unwrap();
|
||||||
|
assert_debug_eq::<Option<bool>>(None, &result);
|
||||||
|
let result = run_php("expect_option_bool()").unwrap();
|
||||||
|
assert_debug_eq::<Option<bool>>(None, &result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that the result is the debug formatting of expected
|
||||||
|
fn assert_debug_eq<T: Debug>(expected: T, result: &str) {
|
||||||
|
assert_eq!(format!("{:?}", expected), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run some php code and return it's output
|
||||||
|
fn run_php(code: &str) -> Result<String, String> {
|
||||||
|
let code = format!("{};", code);
|
||||||
|
let output = Command::new("php")
|
||||||
|
.args(&["-d", "extension=target/debug/libtests.so", "-r", &code])
|
||||||
|
.output()
|
||||||
|
.expect("Failed to run php script");
|
||||||
|
if output.status.success() {
|
||||||
|
Ok(String::from_utf8(output.stdout).expect("invalid utf8"))
|
||||||
|
} else {
|
||||||
|
Err(String::from_utf8(output.stderr).expect("invalid utf8"))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue