aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/util
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/tests/util')
-rw-r--r--crates/shirabe/tests/util/auth_helper_test.rs235
-rw-r--r--crates/shirabe/tests/util/error_handler_test.rs11
-rw-r--r--crates/shirabe/tests/util/process_executor_test.rs41
-rw-r--r--crates/shirabe/tests/util/remote_filesystem_test.rs56
-rw-r--r--crates/shirabe/tests/util/stream_context_factory_test.rs79
5 files changed, 387 insertions, 35 deletions
diff --git a/crates/shirabe/tests/util/auth_helper_test.rs b/crates/shirabe/tests/util/auth_helper_test.rs
index d719abf3..95cd3a2d 100644
--- a/crates/shirabe/tests/util/auth_helper_test.rs
+++ b/crates/shirabe/tests/util/auth_helper_test.rs
@@ -528,33 +528,241 @@ fn test_store_auth_with_prompt_no_answer() {
f.auth_helper.store_auth(origin, StoreAuth::Prompt).unwrap();
}
+// Mirrors AuthHelperTest::testStoreAuthWithPromptInvalidAnswer. The PHP test mocks
+// `askAndValidate` itself to invoke the validator directly with an invalid answer, so the
+// RuntimeException it raises propagates out of `storeAuth` without exercising any interactive
+// retry loop; `IOStub::with_ask_and_validate_answer` mirrors that directly.
#[test]
-#[ignore = "PHP catches a RuntimeException from the validator; the Rust QuestionHelper exhausts \
-input and panics via expect() rather than returning Err, so it cannot be represented as a \
-recoverable Result"]
fn test_store_auth_with_prompt_invalid_answer() {
- todo!()
+ use crate::io_stub::IOStub;
+ use shirabe_php_shim::RuntimeException;
+
+ let origin = "github.com";
+ let config_source_name = "https://api.gitlab.com/source";
+
+ let io = std::rc::Rc::new(std::cell::RefCell::new(
+ IOStub::new().with_ask_and_validate_answer(PhpMixed::String("invalid".to_string())),
+ ));
+ let config = ConfigStubBuilder::new().build_shared();
+
+ let mut source = MockConfigSource::new();
+ source
+ .expect_get_name()
+ .times(1)
+ .returning(|| "https://api.gitlab.com/source".to_string());
+ config.borrow_mut().set_auth_config_source(Box::new(source));
+
+ let auth_helper = AuthHelper::new(
+ io.clone() as std::rc::Rc<std::cell::RefCell<dyn IOInterface>>,
+ config,
+ );
+
+ let err = auth_helper
+ .store_auth(origin, StoreAuth::Prompt)
+ .expect_err("expected a RuntimeException");
+ assert!(err.downcast_ref::<RuntimeException>().is_some());
+
+ // Mirrors PHP's `->with('Do you want to store credentials for '.$origin.' in '.
+ // $configSourceName.' ? [Yn] ', $this->anything(), null, 'y')` verification on askAndValidate.
+ assert_eq!(
+ vec![(
+ format!(
+ "Do you want to store credentials for {origin} in {config_source_name} ? [Yn] "
+ ),
+ None,
+ PhpMixed::String("y".to_string()),
+ )],
+ io.borrow().ask_and_validate_calls()
+ );
}
+// Mirrors AuthHelperTest::testPromptAuthIfNeededGitLabNoAuthChange. `GitLab::authorizeOauth`
+// falls back to the `gitlab-token` config entry after real `git config gitlab.accesstoken` /
+// `gitlab.deploytoken.*` lookups miss (as they do in this repo's test checkout / CI, matching
+// the same host-git-state dependency the upstream PHP test already carries), and stores the
+// same username/password IOStub already returns. Since getAuthentication is a static stub, that
+// looks like "no auth change" and AuthHelper raises a TransportException.
#[test]
-#[ignore = "needs the extra prompt_auth_if_needed params (headers/retry_count/response_body) and \
-GitLab::authorize_oauth which shells out to real `git config`; depends on host git state and is a \
-design-level port concern"]
+#[ignore]
fn test_prompt_auth_if_needed_git_lab_no_auth_change() {
- todo!()
+ use crate::io_stub::IOStub;
+ use shirabe::downloader::TransportException;
+
+ let origin = "gitlab.com";
+
+ let mut auth = IndexMap::new();
+ auth.insert("username".to_string(), Some("gitlab-user".to_string()));
+ auth.insert("password".to_string(), Some("gitlab-password".to_string()));
+ let io = std::rc::Rc::new(std::cell::RefCell::new(
+ IOStub::new()
+ .with_has_authentication(true)
+ .with_get_authentication(auth),
+ ));
+
+ let mut gitlab_token = IndexMap::new();
+ let mut token_entry = IndexMap::new();
+ token_entry.insert(
+ "username".to_string(),
+ PhpMixed::String("gitlab-user".to_string()),
+ );
+ token_entry.insert(
+ "token".to_string(),
+ PhpMixed::String("gitlab-password".to_string()),
+ );
+ gitlab_token.insert("gitlab.com".to_string(), PhpMixed::Array(token_entry));
+
+ let config = ConfigStubBuilder::new()
+ .with("github-domains", PhpMixed::List(vec![]))
+ .with(
+ "gitlab-domains",
+ PhpMixed::List(vec![PhpMixed::String("gitlab.com".to_string())]),
+ )
+ .with("gitlab-token", PhpMixed::Array(gitlab_token))
+ .build_shared();
+
+ let mut auth_helper = AuthHelper::new(
+ io.clone() as std::rc::Rc<std::cell::RefCell<dyn IOInterface>>,
+ config,
+ );
+
+ let result = auth_helper.prompt_auth_if_needed(
+ "https://gitlab.com/acme/archive.zip",
+ origin,
+ 404,
+ Some("GitLab requires authentication and it was not provided"),
+ vec![],
+ 0,
+ None,
+ );
+
+ let err = result.expect_err("expected a TransportException");
+ assert!(err.downcast_ref::<TransportException>().is_some());
+
+ assert_eq!(
+ vec![(
+ "gitlab.com".to_string(),
+ "gitlab-user".to_string(),
+ Some("gitlab-password".to_string())
+ )],
+ io.borrow().set_authentication_calls()
+ );
}
+// Mirrors AuthHelperTest::testPromptAuthIfNeededMultipleBitbucketDownloads. The pre-seeded
+// `bitbucket-oauth` config entry has a non-expired access-token, so `Bitbucket::request_token`
+// resolves it from `get_token_from_config` without ever making a network call.
#[test]
-#[ignore = "drives Bitbucket::request_token over the network and relies on willReturnCallback \
-sequencing of getAuthentication; design-level port concern"]
fn test_prompt_auth_if_needed_multiple_bitbucket_downloads() {
- todo!()
+ use crate::io_stub::IOStub;
+
+ let origin = "bitbucket.org";
+
+ let mut token_entry = IndexMap::new();
+ token_entry.insert(
+ "access-token".to_string(),
+ PhpMixed::String("bitbucket_access_token".to_string()),
+ );
+ token_entry.insert(
+ "access-token-expiration".to_string(),
+ PhpMixed::Int(shirabe_php_shim::time() + 1800),
+ );
+ let mut bitbucket_oauth = IndexMap::new();
+ bitbucket_oauth.insert("bitbucket.org".to_string(), PhpMixed::Array(token_entry));
+
+ let config = ConfigStubBuilder::new()
+ .with("github-domains", PhpMixed::List(vec![]))
+ .with("gitlab-domains", PhpMixed::List(vec![]))
+ .with("bitbucket-oauth", PhpMixed::Array(bitbucket_oauth))
+ .build_shared();
+
+ let mut first_auth = IndexMap::new();
+ first_auth.insert(
+ "username".to_string(),
+ Some("bitbucket_client_id".to_string()),
+ );
+ first_auth.insert(
+ "password".to_string(),
+ Some("bitbucket_client_secret".to_string()),
+ );
+ let io = std::rc::Rc::new(std::cell::RefCell::new(
+ IOStub::new()
+ .with_has_authentication(true)
+ .with_get_authentication(first_auth),
+ ));
+
+ let mut auth_helper = AuthHelper::new(
+ io.clone() as std::rc::Rc<std::cell::RefCell<dyn IOInterface>>,
+ config,
+ );
+
+ let result1 = auth_helper
+ .prompt_auth_if_needed(
+ "https://bitbucket.org/workspace/repo1/get/hash1.zip",
+ origin,
+ 401,
+ Some("HTTP/2 401 "),
+ vec![],
+ 0,
+ None,
+ )
+ .unwrap();
+
+ let mut second_auth = IndexMap::new();
+ second_auth.insert("username".to_string(), Some("x-token-auth".to_string()));
+ second_auth.insert(
+ "password".to_string(),
+ Some("bitbucket_access_token".to_string()),
+ );
+ io.borrow_mut().set_get_authentication(second_auth);
+
+ let result2 = auth_helper
+ .prompt_auth_if_needed(
+ "https://bitbucket.org/workspace/repo2/get/hash2.zip",
+ origin,
+ 401,
+ Some("HTTP/2 401 "),
+ vec![],
+ 0,
+ None,
+ )
+ .unwrap();
+
+ assert!(result1.retry);
+ assert_eq!(StoreAuth::Bool(false), result1.store_auth);
+ assert!(result2.retry);
+ assert_eq!(StoreAuth::Bool(false), result2.store_auth);
+
+ assert_eq!(
+ vec![(
+ "bitbucket.org".to_string(),
+ "x-token-auth".to_string(),
+ Some("bitbucket_access_token".to_string())
+ )],
+ io.borrow().set_authentication_calls()
+ );
+
+ // Mirrors PHP's `->expects($this->exactly(2))->method('hasAuthentication')->with($origin)`
+ // and `->expects($this->exactly(2))->method('getAuthentication')` verification.
+ assert_eq!(
+ vec![origin.to_string(), origin.to_string()],
+ io.borrow().has_authentication_calls()
+ );
+ assert_eq!(
+ vec![origin.to_string(), origin.to_string()],
+ io.borrow().get_authentication_calls()
+ );
}
#[test]
#[ignore = "exercises the deprecated addAuthenticationHeader wrapper (not ported) which relies on \
trigger_error/E_USER_DEPRECATED; the PHP error-handler subsystem is not modeled"]
fn test_add_authentication_header_with_custom_headers() {
+ // TODO(phase-d): exercises AuthHelper::addAuthenticationHeader, a deprecated wrapper
+ // around addAuthenticationOptions that PHP implements via
+ // trigger_error(E_USER_DEPRECATED). It has not been ported to Rust (no
+ // add_authentication_header method exists on AuthHelper) because the PHP
+ // error-handler subsystem it relies on is not modeled — same limitation as
+ // error_handler_test.rs.
todo!()
}
@@ -562,6 +770,8 @@ fn test_add_authentication_header_with_custom_headers() {
#[ignore = "exercises the deprecated addAuthenticationHeader wrapper (not ported) which relies on \
trigger_error/E_USER_DEPRECATED; the PHP error-handler subsystem is not modeled"]
fn test_add_authentication_header_is_working() {
+ // TODO(phase-d): see test_add_authentication_header_with_custom_headers above — same
+ // unported addAuthenticationHeader deprecated wrapper.
todo!()
}
@@ -569,5 +779,8 @@ fn test_add_authentication_header_is_working() {
#[ignore = "exercises the deprecated addAuthenticationHeader wrapper (not ported) which relies on \
trigger_error/E_USER_DEPRECATED converted to a RuntimeException via set_error_handler; not modeled"]
fn test_add_authentication_header_deprecation() {
+ // TODO(phase-d): asserts that calling addAuthenticationHeader itself raises a
+ // RuntimeException via a custom set_error_handler converting E_USER_DEPRECATED; same
+ // unported wrapper and unmodeled error-handler subsystem as the two tests above.
todo!()
}
diff --git a/crates/shirabe/tests/util/error_handler_test.rs b/crates/shirabe/tests/util/error_handler_test.rs
index 7295cf2a..ed5e6a29 100644
--- a/crates/shirabe/tests/util/error_handler_test.rs
+++ b/crates/shirabe/tests/util/error_handler_test.rs
@@ -5,15 +5,15 @@
// trigger those by undefined-index access / array_merge misuse. There is no equivalent
// runtime mechanism in Rust to port faithfully.
+// TODO(phase-d): ErrorHandler::register() installs a PHP set_error_handler; no Rust equivalent.
#[allow(dead_code)]
fn set_up() {
- // ErrorHandler::register() installs a PHP set_error_handler; no Rust equivalent.
todo!()
}
+// TODO(phase-d): restore_error_handler() is PHP runtime machinery; no Rust equivalent.
#[allow(dead_code)]
fn tear_down() {
- // restore_error_handler() is PHP runtime machinery; no Rust equivalent.
todo!()
}
@@ -29,17 +29,24 @@ impl Drop for TearDown {
#[ignore = "depends on PHP runtime routing an undefined-index notice through set_error_handler; no Rust equivalent for $array['baz'] triggering ErrorHandler::handle"]
#[test]
fn test_error_handler_capture_notice() {
+ // TODO(phase-d): depends on PHP runtime routing an undefined-index notice through
+ // set_error_handler; no Rust equivalent for $array['baz'] triggering
+ // ErrorHandler::handle.
todo!()
}
#[ignore = "depends on PHP runtime emitting a TypeError/warning from array_merge([], 'string') via set_error_handler; no Rust equivalent"]
#[test]
fn test_error_handler_capture_warning() {
+ // TODO(phase-d): depends on PHP runtime emitting a TypeError/warning from
+ // array_merge([], 'string') via set_error_handler; no Rust equivalent.
todo!()
}
#[ignore = "depends on the PHP @ error-suppression operator and trigger_error routing through set_error_handler; no Rust equivalent"]
#[test]
fn test_error_handler_respects_at_operator() {
+ // TODO(phase-d): depends on the PHP @ error-suppression operator and trigger_error
+ // routing through set_error_handler; no Rust equivalent.
todo!()
}
diff --git a/crates/shirabe/tests/util/process_executor_test.rs b/crates/shirabe/tests/util/process_executor_test.rs
index e093a801..2ce43f3c 100644
--- a/crates/shirabe/tests/util/process_executor_test.rs
+++ b/crates/shirabe/tests/util/process_executor_test.rs
@@ -1,8 +1,8 @@
//! ref: composer/tests/Composer/Test/Util/ProcessExecutorTest.php
// These run real subprocesses (capturing output/stderr/timeout) and assert ProcessExecutor's
-// password hiding, line splitting and argument escaping; the subprocess execution and mocked
-// IO are not ported.
+// password hiding, line splitting and argument escaping. A few data points remain unportable —
+// see the individual `// TODO(phase-d)` comments below.
use shirabe::io::ConsoleIO;
use shirabe::io::IOInterface;
@@ -28,16 +28,34 @@ fn test_execute_captures_output() {
#[ignore = "requires PHP output buffering (ob_start/ob_get_clean) to capture stdout; no equivalent symbol"]
#[test]
fn test_execute_outputs_if_not_captured() {
+ // TODO(phase-d): requires PHP output buffering (ob_start/ob_get_clean) to capture
+ // stdout; no equivalent symbol. ProcessExecutor::execute with
+ // ProcessExecutor::FORWARD_OUTPUT and io=None writes straight to the real process
+ // stdout (see output_handler's `print!`), and there is no safe way to capture that
+ // from within a parallel cargo test process without redirecting the real stdout file
+ // descriptor, which is unsafe under `cargo test`'s default multi-threaded runner.
todo!()
}
-#[ignore = "requires getMockBuilder('IOInterface') with expects()->once()->method('writeRaw')->with() expectation verification; no mocking framework"]
#[test]
fn test_use_io_is_not_null_and_if_not_captured() {
- todo!()
+ use crate::io_stub::IOStub;
+
+ let io = std::rc::Rc::new(std::cell::RefCell::new(IOStub::new()));
+ let mut process = ProcessExecutor::new(Some(
+ io.clone() as std::rc::Rc<std::cell::RefCell<dyn IOInterface>>
+ ));
+
+ process
+ .execute("echo foo", ProcessExecutor::FORWARD_OUTPUT, None)
+ .unwrap();
+
+ assert_eq!(
+ vec![(format!("foo{}", PHP_EOL), false)],
+ io.borrow().write_raw_calls()
+ );
}
-#[ignore = "stderr capture works, but the test cwd (crates/shirabe) contains a `foo/` fixture dir, so `cat foo` reports \"Is a directory\" instead of \"No such file or directory\""]
#[test]
fn test_execute_captures_stderr() {
let mut process = ProcessExecutor::new(None);
@@ -124,6 +142,14 @@ fn test_doesnt_hide_ports() {
#[ignore = "splitLines is called with null in the PHP test, but split_lines accepts only &str (no ?string/Option overload)"]
#[test]
fn test_split_lines() {
+ // TODO(phase-d): splitLines is called with null in the PHP test
+ // ($process->splitLines(null)), but ProcessExecutor::split_lines here takes `&str`, not
+ // `Option<&str>` (PHP's `?string`). Porting this data point faithfully means widening
+ // split_lines's signature to Option<&str>, which touches every call site
+ // (package/version/version_guesser.rs, util/git.rs,
+ // repository/vcs/{hg,fossil,git,svn}_driver.rs — 13 call sites in total, all currently
+ // passing `&str`). That is a production API change beyond this test file; flagged for
+ // a design decision rather than made unilaterally.
todo!()
}
@@ -162,6 +188,11 @@ fn test_console_io_does_not_format_symfony_console_style() {
#[ignore = "executeAsync returns a Process, not a cancelable promise; no promise/cancel symbol exists"]
#[test]
fn test_execute_async_cancel() {
+ // TODO(phase-d): PHP's executeAsync returns a React\Promise\PromiseInterface with
+ // cancel(); Rust's execute_async returns anyhow::Result<Process> directly (see the
+ // comment on ProcessExecutor::execute_async: "no test seam in the external-packages
+ // crate"), so there is no promise/cancel symbol to drive this test's
+ // `$promise->cancel()` step.
todo!()
}
diff --git a/crates/shirabe/tests/util/remote_filesystem_test.rs b/crates/shirabe/tests/util/remote_filesystem_test.rs
index 9c1fe0bd..0fdc81b3 100644
--- a/crates/shirabe/tests/util/remote_filesystem_test.rs
+++ b/crates/shirabe/tests/util/remote_filesystem_test.rs
@@ -273,19 +273,71 @@ fn test_copy() {
#[test]
#[ignore = "requires a MockObject subclass of RemoteFilesystem overriding private get_remote_contents; no subclass-mocking infrastructure exists"]
fn test_copy_with_no_retry_on_failure() {
+ // TODO(phase-d): requires a MockObject subclass of RemoteFilesystem overriding the
+ // private get_remote_contents method. There is no subclass-mocking infrastructure in
+ // Rust for this, and get_remote_contents's http(s) branch is itself still a
+ // TODO(phase-c) stub (always returns Ok(None)), so there is nothing yet to intercept
+ // even with a seam.
todo!()
}
#[test]
#[ignore = "requires MockObject subclasses overriding RemoteFilesystem::get_remote_contents and AuthHelper::prompt_auth_if_needed; no subclass-mocking infrastructure exists"]
fn test_copy_with_success_on_retry() {
+ // TODO(phase-d): requires MockObject subclasses overriding
+ // RemoteFilesystem::get_remote_contents and AuthHelper::prompt_auth_if_needed to
+ // simulate a first failure and a retried success; same missing-subclass-mocking-
+ // infrastructure and TODO(phase-c) http(s)-stub blockers as
+ // test_copy_with_no_retry_on_failure above.
todo!()
}
#[test]
-#[ignore = "get_tls_defaults validates the (nonexistent) cafile and errors; constructor swallows it, so no ssl defaults are produced. Faithful porting needs CaBundle::validate_ca_file semantics for a missing file"]
fn test_get_options_for_url_creates_secure_tls_defaults() {
- todo!()
+ let io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> =
+ std::rc::Rc::new(std::cell::RefCell::new(IOStub::new()));
+
+ let mut ssl: IndexMap<String, PhpMixed> = IndexMap::new();
+ ssl.insert(
+ "cafile".to_string(),
+ PhpMixed::String("/some/path/file.crt".to_string()),
+ );
+ let mut additional_options: IndexMap<String, PhpMixed> = IndexMap::new();
+ additional_options.insert("ssl".to_string(), PhpMixed::Array(ssl));
+
+ let res = call_get_options_for_url(
+ io,
+ "example.org",
+ additional_options,
+ IndexMap::new(),
+ "http://www.example.org",
+ );
+
+ let ssl_res = res.get("ssl").and_then(|v| v.as_array()).unwrap();
+ let ciphers = ssl_res.get("ciphers").and_then(|v| v.as_string()).unwrap();
+ assert!(ciphers.contains(
+ "!aNULL:!eNULL:!EXPORT:!DES:!3DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
+ ));
+ assert_eq!(
+ Some(true),
+ ssl_res.get("verify_peer").and_then(|v| v.as_bool())
+ );
+ assert_eq!(
+ Some(true),
+ ssl_res.get("SNI_enabled").and_then(|v| v.as_bool())
+ );
+ assert_eq!(
+ Some(7),
+ ssl_res.get("verify_depth").and_then(|v| v.as_int())
+ );
+ assert_eq!(
+ Some("/some/path/file.crt"),
+ ssl_res.get("cafile").and_then(|v| v.as_string())
+ );
+ assert_eq!(
+ Some(true),
+ ssl_res.get("disable_compression").and_then(|v| v.as_bool())
+ );
}
// Mirrors RemoteFilesystemTest::provideBitbucketPublicDownloadUrls.
diff --git a/crates/shirabe/tests/util/stream_context_factory_test.rs b/crates/shirabe/tests/util/stream_context_factory_test.rs
index 284077b7..2ef2b6b7 100644
--- a/crates/shirabe/tests/util/stream_context_factory_test.rs
+++ b/crates/shirabe/tests/util/stream_context_factory_test.rs
@@ -1,8 +1,13 @@
//! ref: composer/tests/Composer/Test/Util/StreamContextFactoryTest.php
// These build a stream context and assert proxy/option handling driven by HTTP(S)_PROXY /
-// no_proxy environment variables; the env-dependent setup (without its setUp/tearDown
-// isolation) is not ported.
+// no_proxy environment variables. PHP's setUp/tearDown isolation (which resets the env vars and
+// the ProxyManager singleton before/after every test method) is emulated per-test via
+// set_up()/tear_down()+TearDown below; since env vars and the ProxyManager singleton are global
+// process state, and cargo runs tests in parallel by default (unlike PHPUnit's default serial
+// execution), every test here is also tagged `#[serial_test::serial]` to avoid racing other
+// serial-tagged tests in this binary that touch the same global state (see
+// util/http/proxy_manager_test.rs and http_downloader_test.rs).
use indexmap::IndexMap;
use shirabe::util::http::proxy_manager::ProxyManager;
use shirabe::util::platform::Platform;
@@ -35,6 +40,38 @@ fn map(entries: Vec<(&str, PhpMixed)>) -> IndexMap<String, PhpMixed> {
.collect()
}
+// `PhpMixed`'s `PartialEq` models PHP's `===` (order-sensitive for associative arrays). These
+// tests port PHPUnit's `assertEquals`, which compares associative arrays by key/value regardless
+// of insertion order (PHP `List`/sequential arrays are still position-sensitive, since reordering
+// them changes which value is at which index). This mirrors that PHPUnit semantics for the
+// `IndexMap<String, PhpMixed>` results `stream_context_get_options` returns.
+fn php_equals(a: &PhpMixed, b: &PhpMixed) -> bool {
+ match (a, b) {
+ (PhpMixed::Array(a), PhpMixed::Array(b)) => {
+ a.len() == b.len()
+ && a.iter()
+ .all(|(k, v)| b.get(k).is_some_and(|bv| php_equals(v, bv)))
+ }
+ (PhpMixed::List(a), PhpMixed::List(b)) => {
+ a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| php_equals(x, y))
+ }
+ _ => a == b,
+ }
+}
+
+#[track_caller]
+fn assert_options_eq(expected: &IndexMap<String, PhpMixed>, actual: &IndexMap<String, PhpMixed>) {
+ let matches = expected.len() == actual.len()
+ && expected
+ .iter()
+ .all(|(k, v)| actual.get(k).is_some_and(|av| php_equals(v, av)));
+ assert!(
+ matches,
+ "options mismatch (order-insensitive):\n expected: {:?}\n actual: {:?}",
+ expected, actual
+ );
+}
+
fn set_up() {
Platform::clear_env("HTTP_PROXY");
Platform::clear_env("http_proxy");
@@ -63,18 +100,25 @@ impl Drop for TearDown {
}
}
-// PHP's dataGetContext second data set passes a `notification` closure in both the default and
-// expected params; PhpMixed has no closure variant, so that data set (and thus the all-or-nothing
-// testGetContext) cannot be expressed.
+// TODO(phase-d): PHP's dataGetContext second data set passes a `notification` closure in both
+// the default and expected params; PhpMixed has no closure variant, so that data set (and thus
+// the all-or-nothing testGetContext, which a data provider test cannot partially skip) cannot be
+// expressed.
#[test]
+#[serial_test::serial]
#[ignore = "dataGetContext passes a notification closure in params; PhpMixed cannot represent a PHP closure, so the data set is unportable"]
fn test_get_context() {
let _tear_down = TearDown;
set_up();
+ // TODO(phase-d): dataGetContext's second data set passes a `notification` closure in
+ // params; PhpMixed cannot represent a PHP closure, so that data set (and thus the
+ // all-or-nothing testGetContext, which a data provider test cannot partially skip) is
+ // unportable.
todo!()
}
#[test]
+#[serial_test::serial]
#[ignore]
fn test_http_proxy() {
let _tear_down = TearDown;
@@ -114,11 +158,11 @@ fn test_http_proxy() {
("follow_location", PhpMixed::Int(1)),
]),
)]);
- assert_eq!(expected, options);
+ assert_options_eq(&expected, &options);
}
#[test]
-#[ignore]
+#[serial_test::serial]
fn test_http_proxy_with_no_proxy() {
let _tear_down = TearDown;
set_up();
@@ -146,11 +190,11 @@ fn test_http_proxy_with_no_proxy() {
("header", list(vec![s("User-Agent: foo")])),
]),
)]);
- assert_eq!(expected, options);
+ assert_options_eq(&expected, &options);
}
#[test]
-#[ignore]
+#[serial_test::serial]
fn test_http_proxy_with_no_proxy_wildcard() {
let _tear_down = TearDown;
set_up();
@@ -178,10 +222,11 @@ fn test_http_proxy_with_no_proxy_wildcard() {
("header", list(vec![s("User-Agent: foo")])),
]),
)]);
- assert_eq!(expected, options);
+ assert_options_eq(&expected, &options);
}
#[test]
+#[serial_test::serial]
#[ignore]
fn test_options_are_preserved() {
let _tear_down = TearDown;
@@ -225,10 +270,11 @@ fn test_options_are_preserved() {
("follow_location", PhpMixed::Int(1)),
]),
)]);
- assert_eq!(expected, options);
+ assert_options_eq(&expected, &options);
}
#[test]
+#[serial_test::serial]
#[ignore]
fn test_http_proxy_without_port() {
let _tear_down = TearDown;
@@ -263,11 +309,11 @@ fn test_http_proxy_without_port() {
("follow_location", PhpMixed::Int(1)),
]),
)]);
- assert_eq!(expected, options);
+ assert_options_eq(&expected, &options);
}
#[test]
-#[ignore]
+#[serial_test::serial]
fn test_https_proxy_override() {
let _tear_down = TearDown;
set_up();
@@ -293,7 +339,7 @@ fn test_https_proxy_override() {
}
#[test]
-#[ignore]
+#[serial_test::serial]
fn test_ssl_proxy() {
let _tear_down = TearDown;
for (expected, proxy) in [
@@ -322,7 +368,7 @@ fn test_ssl_proxy() {
("header", list(vec![s("User-Agent: foo")])),
]),
)]);
- assert_eq!(expected_options, options);
+ assert_options_eq(&expected_options, &options);
} else {
// The catch in PHP asserts the exception is a TransportException; the return type
// here already guarantees that.
@@ -339,6 +385,7 @@ fn test_ssl_proxy() {
}
#[test]
+#[serial_test::serial]
fn test_ensure_thatfix_http_header_field_moves_content_type_to_end_of_options() {
let _tear_down = TearDown;
set_up();
@@ -370,6 +417,7 @@ fn test_ensure_thatfix_http_header_field_moves_content_type_to_end_of_options()
}
#[test]
+#[serial_test::serial]
#[ignore]
fn test_init_options_does_include_proxy_auth_headers() {
let _tear_down = TearDown;
@@ -397,6 +445,7 @@ fn test_init_options_does_include_proxy_auth_headers() {
}
#[test]
+#[serial_test::serial]
#[ignore]
fn test_init_options_for_curl_does_not_include_proxy_auth_headers() {
let _tear_down = TearDown;