@@ -35,3 +35,95 @@ pub async fn upload_inputs(benchmark_name: &str, input_dir: &Path) {
3535 println ! ( "{}" , String :: from_utf8_lossy( & output. stdout) . trim( ) ) ;
3636 println ! ( "Input files uploaded successfully!" ) ;
3737}
38+
39+ /// Downloads all input files for a benchmark from Google Cloud Storage.
40+ ///
41+ /// Uses gcloud CLI to download files. Before running, authenticate with:
42+ /// `gcloud auth application-default login`
43+ ///
44+ /// Downloads from: `gs://{BENCHMARKS_BUCKET}/{benchmark_name}/input/` to the output directory.
45+ pub async fn download_inputs ( benchmark_name : & str , output_dir : & Path ) {
46+ println ! (
47+ "Downloading inputs from gs://{}/{}/input/ to {}" ,
48+ BENCHMARKS_BUCKET ,
49+ benchmark_name,
50+ output_dir. display( )
51+ ) ;
52+
53+ let source = format ! ( "gs://{}/{}/input/*" , BENCHMARKS_BUCKET , benchmark_name) ;
54+ let dest = output_dir. display ( ) . to_string ( ) ;
55+
56+ // Use gcloud storage cp command to download files.
57+ let output = tokio:: process:: Command :: new ( "gcloud" )
58+ . args ( [ "storage" , "cp" , "-r" , & source, & dest] )
59+ . output ( )
60+ . await
61+ . expect ( "Failed to cp inputs from GCS" ) ;
62+
63+ if !output. status . success ( ) {
64+ let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
65+ panic ! ( "Failed to download inputs from GCS: {}" , stderr) ;
66+ }
67+
68+ println ! ( "{}" , String :: from_utf8_lossy( & output. stdout) . trim( ) ) ;
69+ println ! ( "Input files downloaded successfully!" ) ;
70+ }
71+
72+ #[ cfg( test) ]
73+ mod tests {
74+ use std:: path:: PathBuf ;
75+
76+ use tempfile:: TempDir ;
77+ use tokio:: fs;
78+
79+ use super :: * ;
80+
81+ #[ tokio:: test]
82+ #[ ignore] // Run with: cargo test -p bench_tools -- --ignored
83+ async fn test_upload_and_download_inputs ( ) {
84+ let benchmark_name = "dummy_benchmark" ;
85+
86+ // Get paths relative to workspace root.
87+ let workspace_root = std:: env:: var ( "CARGO_MANIFEST_DIR" ) . unwrap ( ) ;
88+ let source_dir = PathBuf :: from ( & workspace_root) . join ( "data/dummy_bench_input" ) ;
89+
90+ // Ensure source files exist.
91+ assert ! ( source_dir. exists( ) , "Source directory does not exist: {}" , source_dir. display( ) ) ;
92+
93+ // Upload inputs/
94+ println ! ( "Testing upload..." ) ;
95+ upload_inputs ( benchmark_name, & source_dir) . await ;
96+
97+ // Create temp directory for download.
98+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
99+ let download_dir = temp_dir. path ( ) ;
100+
101+ println ! ( "\n Download directory: {}" , download_dir. display( ) ) ;
102+
103+ // Verify temp directory is empty initially.
104+ assert ! (
105+ fs:: read_dir( download_dir) . await . unwrap( ) . next_entry( ) . await . unwrap( ) . is_none( ) ,
106+ "Temp directory should be empty initially"
107+ ) ;
108+
109+ // Download inputs to temp directory.
110+ println ! ( "\n Testing download..." ) ;
111+ download_inputs ( benchmark_name, download_dir) . await ;
112+
113+ // Verify files were downloaded
114+ let small_input = download_dir. join ( "small_input.json" ) ;
115+ let large_input = download_dir. join ( "large_input.json" ) ;
116+
117+ assert ! ( small_input. exists( ) , "small_input.json was not downloaded" ) ;
118+ assert ! ( large_input. exists( ) , "large_input.json was not downloaded" ) ;
119+
120+ // Verify content matches original.
121+ let original_small = fs:: read_to_string ( source_dir. join ( "small_input.json" ) ) . await . unwrap ( ) ;
122+ let downloaded_small = fs:: read_to_string ( & small_input) . await . unwrap ( ) ;
123+ assert_eq ! ( original_small, downloaded_small, "small_input.json content does not match" ) ;
124+
125+ let original_large = fs:: read_to_string ( source_dir. join ( "large_input.json" ) ) . await . unwrap ( ) ;
126+ let downloaded_large = fs:: read_to_string ( & large_input) . await . unwrap ( ) ;
127+ assert_eq ! ( original_large, downloaded_large, "large_input.json content does not match" ) ;
128+ }
129+ }
0 commit comments