Restore missing required asset/flow packages from the pinned nigig revision; retain upstream script isolation, X11 hover, and widget-core fixes. Validated makepad-widgets data feature with Rust 1.97.1.
152 lines
5.9 KiB
Rust
152 lines
5.9 KiB
Rust
#![cfg(not(target_arch = "wasm32"))]
|
|
|
|
use makepad_asset_client::ApiEndpoints;
|
|
use makepad_flow::engine::executors::publish::{
|
|
AssetListQuery, AssetStoreConfig, AssetWorker, PublishExecutor,
|
|
};
|
|
use makepad_flow::engine::executors::{Executor, Poll};
|
|
use makepad_flow::{Literal, Loc, Node, Port, PortType, Value};
|
|
use std::path::PathBuf;
|
|
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
|
|
|
struct Roots(Vec<PathBuf>);
|
|
|
|
impl Roots {
|
|
fn new() -> Self { Self(Vec::new()) }
|
|
|
|
fn make(&mut self, label: &str) -> PathBuf {
|
|
let nonce = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos();
|
|
let path = std::env::temp_dir().join(format!("flow-publish-{}-{label}-{nonce}", std::process::id()));
|
|
std::fs::create_dir_all(&path).unwrap();
|
|
self.0.push(path.clone());
|
|
path
|
|
}
|
|
}
|
|
|
|
impl Drop for Roots {
|
|
fn drop(&mut self) {
|
|
for path in &self.0 { let _ = std::fs::remove_dir_all(path); }
|
|
}
|
|
}
|
|
|
|
fn start_store(root: PathBuf) -> (makepad_asset_store::AssetServer, String) {
|
|
let mut config = makepad_asset_store::ServerConfig::new(root.clone());
|
|
config.control_addr = "127.0.0.1:0".parse().unwrap();
|
|
config.data_addr = "127.0.0.1:0".parse().unwrap();
|
|
config.bootstrap_admin = true;
|
|
config.discovery = None;
|
|
config.log = false;
|
|
let server = makepad_asset_store::AssetServer::start(config).unwrap();
|
|
let token = std::fs::read_to_string(root.join("admin-token")).unwrap().trim().to_string();
|
|
(server, token)
|
|
}
|
|
|
|
fn node() -> Node {
|
|
Node {
|
|
id: "library".into(),
|
|
kind: "publish".into(),
|
|
type_name: "Publish".into(),
|
|
params: vec![
|
|
("title".into(), Literal::Str("Sunset result".into())),
|
|
("namespace".into(), Literal::Str("flows".into())),
|
|
("tags".into(), Literal::Arr(vec![Literal::Str("flow".into()), Literal::Str("sunset".into())])),
|
|
("description".into(), Literal::Str("painted from a prompt".into())),
|
|
("alias".into(), Literal::Str("flows/sunset-result".into())),
|
|
],
|
|
inputs: Vec::new(),
|
|
outputs: vec![Port { name: "asset".into(), ty: PortType::Json }],
|
|
at: None,
|
|
size: None,
|
|
flip: false,
|
|
loc: Loc::default(),
|
|
fn_src: None,
|
|
face_src: None,
|
|
on_fail: "fail".into(),
|
|
label: None,
|
|
domain: None,
|
|
doc: None,
|
|
}
|
|
}
|
|
|
|
fn picture(fill: u8) -> Value {
|
|
let mut pixels = vec![fill; 256 * 256 * 4];
|
|
for alpha in pixels.iter_mut().skip(3).step_by(4) { *alpha = 255; }
|
|
let png = makepad_ai_hub::testpattern::encode_png_rgba(&pixels, 256, 256).unwrap();
|
|
Value::media(PortType::Image, "image/png", png)
|
|
}
|
|
|
|
fn publish(worker: &AssetWorker, value: Value) -> makepad_strict_json::Value {
|
|
let mut executor = PublishExecutor::new(
|
|
Some(worker.handle()),
|
|
"prompt-to-library".into(),
|
|
"instance-1".into(),
|
|
String::new(),
|
|
);
|
|
executor.start(&node(), &[("value".into(), value)]).unwrap();
|
|
let deadline = Instant::now() + Duration::from_secs(10);
|
|
loop {
|
|
match executor.poll() {
|
|
Poll::Done(outputs) => {
|
|
let value = outputs.into_iter().find(|(port, _)| port == "asset").unwrap().1;
|
|
return makepad_strict_json::parse(&value.bytes).unwrap();
|
|
}
|
|
Poll::Failed(error) => panic!("publish failed: {error}"),
|
|
_ if Instant::now() < deadline => std::thread::sleep(Duration::from_millis(20)),
|
|
other => panic!("publish timed out after {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn picture_publish_and_alias_republish_create_revisions() {
|
|
let mut roots = Roots::new();
|
|
let (mut server, token) = start_store(roots.make("store"));
|
|
let config = AssetStoreConfig {
|
|
archive_outputs: false,
|
|
cache_dir: roots.make("cache"),
|
|
token: Some(token),
|
|
endpoints: Some(ApiEndpoints { control: server.control_addr(), data: server.data_addr() }),
|
|
server_id: Some(server.server_id()),
|
|
discovery_port: 0,
|
|
discovery_wait_ms: 0,
|
|
};
|
|
let mut worker = AssetWorker::start(config).unwrap();
|
|
let first = publish(&worker, picture(30));
|
|
let second = publish(&worker, picture(90));
|
|
assert_eq!(first.get("id").and_then(|v| v.as_str()), second.get("id").and_then(|v| v.as_str()));
|
|
assert_ne!(first.get("revision").and_then(|v| v.as_str()), second.get("revision").and_then(|v| v.as_str()));
|
|
assert_eq!(second.get("alias").and_then(|v| v.as_str()), Some("flows/sunset-result"));
|
|
assert_eq!(second.get("namespace").and_then(|v| v.as_str()), Some("flows"));
|
|
assert_eq!(second.get("title").and_then(|v| v.as_str()), Some("Sunset result"));
|
|
assert_eq!(second.get("kind").and_then(|v| v.as_str()), Some("texture"));
|
|
|
|
let rows = worker.handle().list(AssetListQuery { text: String::new(), namespace: Some("flows".into()), limit: 10, cursor: None }).unwrap();
|
|
assert_eq!(rows.len(), 1);
|
|
assert_eq!(rows[0].title, "Sunset result");
|
|
assert_eq!(rows[0].namespace, "flows");
|
|
assert_eq!(rows[0].tags, vec!["flow", "sunset"]);
|
|
worker.stop();
|
|
server.shutdown();
|
|
}
|
|
|
|
#[test]
|
|
fn missing_store_failure_names_discovery() {
|
|
let mut roots = Roots::new();
|
|
let mut config = AssetStoreConfig::new(roots.make("cache"));
|
|
config.discovery_wait_ms = 0;
|
|
let mut worker = AssetWorker::start(config).unwrap();
|
|
let mut executor = PublishExecutor::new(Some(worker.handle()), "demo".into(), "one".into(), String::new());
|
|
executor.start(&node(), &[("value".into(), picture(10))]).unwrap();
|
|
let deadline = Instant::now() + Duration::from_secs(2);
|
|
loop {
|
|
match executor.poll() {
|
|
Poll::Failed(error) => {
|
|
assert!(error.contains("no asset server discovered on this LAN"), "{error}");
|
|
break;
|
|
}
|
|
_ if Instant::now() < deadline => std::thread::sleep(Duration::from_millis(10)),
|
|
_ => panic!("missing store failure timed out"),
|
|
}
|
|
}
|
|
worker.stop();
|
|
}
|