Skip to content

Commit db646d0

Browse files
authored
Add test helpers (#1718)
**Stack**: - #1686 - #1685 - #1684 - #1683 - #1718⚠️ *Part of a stack created by [spr](https://github.com/ejoffe/spr). Do not merge manually using the UI - doing so may have unexpected results.*
1 parent d58f2fd commit db646d0

File tree

4 files changed

+156
-0
lines changed

4 files changed

+156
-0
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

utils/scarb-test-support/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ indoc.workspace = true
1717
itertools.workspace = true
1818
scarb = { path = "../../scarb" }
1919
scarb-build-metadata = { path = "../scarb-build-metadata" }
20+
scarb-proc-macro-server-types = { path = "../scarb-proc-macro-server-types" }
2021
scarb-ui = { path = "../scarb-ui" }
2122
semver.workspace = true
2223
serde.workspace = true

utils/scarb-test-support/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod filesystem;
66
pub mod fsx;
77
pub mod gitx;
88
pub mod manifest_edit;
9+
pub mod proc_macro_server;
910
pub mod project_builder;
1011
pub mod registry;
1112
pub mod simple_http_server;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use crate::command::Scarb;
2+
use scarb_proc_macro_server_types::jsonrpc::RequestId;
3+
use scarb_proc_macro_server_types::jsonrpc::ResponseError;
4+
use scarb_proc_macro_server_types::jsonrpc::RpcRequest;
5+
use scarb_proc_macro_server_types::jsonrpc::RpcResponse;
6+
use scarb_proc_macro_server_types::methods::Method;
7+
use std::collections::HashMap;
8+
use std::io::BufRead;
9+
use std::io::BufReader;
10+
use std::io::Lines;
11+
use std::io::Write;
12+
use std::marker::PhantomData;
13+
use std::path::Path;
14+
use std::process::Child;
15+
use std::process::ChildStdin;
16+
use std::process::ChildStdout;
17+
use std::process::Stdio;
18+
19+
pub const SIMPLE_MACROS: &str = r#"
20+
use cairo_lang_macro::{
21+
ProcMacroResult,
22+
TokenStream,
23+
attribute_macro,
24+
inline_macro,
25+
derive_macro,
26+
executable_attribute
27+
};
28+
29+
executable_attribute!("some_executable");
30+
31+
#[attribute_macro]
32+
pub fn some(_attr: TokenStream, token_stream: TokenStream) -> ProcMacroResult {
33+
ProcMacroResult::new(token_stream)
34+
}
35+
36+
#[inline_macro]
37+
pub fn inline_some(token_stream: TokenStream) -> ProcMacroResult {
38+
ProcMacroResult::new(token_stream)
39+
}
40+
41+
#[derive_macro]
42+
fn some_derive(_token_stream: TokenStream)-> ProcMacroResult {
43+
ProcMacroResult::new(TokenStream::new("impl SomeImpl of SomeTrait {}".to_string()))
44+
}
45+
"#;
46+
47+
pub struct PendingRequest<M: Method> {
48+
id: RequestId,
49+
_method: PhantomData<M>,
50+
}
51+
52+
impl<M: Method> PendingRequest<M> {
53+
fn new(id: RequestId) -> Self {
54+
Self {
55+
id,
56+
_method: Default::default(),
57+
}
58+
}
59+
}
60+
61+
pub struct ProcMacroClient {
62+
requester: ChildStdin,
63+
responder: Lines<BufReader<ChildStdout>>,
64+
server_process: Child,
65+
id_counter: RequestId,
66+
responses: HashMap<RequestId, RpcResponse>,
67+
}
68+
69+
impl ProcMacroClient {
70+
pub fn new<P: AsRef<Path>>(path: P) -> Self {
71+
let mut server_process = Scarb::new()
72+
.std()
73+
.arg("--quiet")
74+
.arg("proc-macro-server")
75+
.stdout(Stdio::piped())
76+
.stdin(Stdio::piped())
77+
.stderr(Stdio::inherit())
78+
.current_dir(path)
79+
.spawn()
80+
.unwrap();
81+
82+
let requester = server_process.stdin.take().unwrap();
83+
let responder = BufReader::new(server_process.stdout.take().unwrap()).lines();
84+
85+
Self {
86+
requester,
87+
responder,
88+
server_process,
89+
id_counter: Default::default(),
90+
responses: Default::default(),
91+
}
92+
}
93+
94+
pub fn request<M: Method>(&mut self, params: M::Params) -> PendingRequest<M> {
95+
let id = self.id_counter;
96+
self.id_counter += 1;
97+
98+
let mut request = serde_json::to_vec(&RpcRequest {
99+
id,
100+
method: M::METHOD.to_string(),
101+
value: serde_json::to_value(params).unwrap(),
102+
})
103+
.unwrap();
104+
request.push(b'\n');
105+
106+
self.requester.write_all(&request).unwrap();
107+
self.requester.flush().unwrap();
108+
109+
PendingRequest::new(id)
110+
}
111+
112+
pub fn request_and_wait<M: Method>(
113+
&mut self,
114+
params: M::Params,
115+
) -> Result<M::Response, ResponseError> {
116+
let request = self.request(params);
117+
118+
self.wait_for_response::<M>(request)
119+
}
120+
121+
pub fn wait_for_response<M: Method>(
122+
&mut self,
123+
request: PendingRequest<M>,
124+
) -> Result<M::Response, ResponseError> {
125+
// If we already read this response, return it.
126+
if let Some(raw_response) = self.responses.remove(&request.id) {
127+
return raw_response
128+
.into_result()
129+
.map(|value| serde_json::from_value(value).unwrap());
130+
}
131+
132+
// Read responses until we get requested one, keeping all others in memory.
133+
loop {
134+
let response = self.responder.next().unwrap().unwrap();
135+
let raw_response: RpcResponse = serde_json::from_str(&response).unwrap();
136+
137+
if raw_response.id == request.id {
138+
return raw_response
139+
.into_result()
140+
.map(|value| serde_json::from_value(value).unwrap());
141+
} else {
142+
self.responses.insert(raw_response.id, raw_response);
143+
}
144+
}
145+
}
146+
}
147+
148+
impl Drop for ProcMacroClient {
149+
fn drop(&mut self) {
150+
self.server_process.kill().unwrap();
151+
self.server_process.wait().unwrap();
152+
}
153+
}

0 commit comments

Comments
 (0)