Rust · 8735 bytes Raw Blame History
1 use std::path::{Path, PathBuf};
2 use std::process::Command;
3 use std::sync::atomic::{AtomicUsize, Ordering};
4
5 static NEXT_TEMP_ID: AtomicUsize = AtomicUsize::new(0);
6
7 fn compiler(name: &str) -> PathBuf {
8 if let Some(path) = std::env::var_os(format!("CARGO_BIN_EXE_{}", name)) {
9 return PathBuf::from(path);
10 }
11 let candidate = PathBuf::from("target/debug").join(name);
12 if candidate.exists() {
13 return std::fs::canonicalize(candidate).expect("cannot canonicalize debug compiler path");
14 }
15 let candidate = PathBuf::from("target/release").join(name);
16 if candidate.exists() {
17 return std::fs::canonicalize(candidate)
18 .expect("cannot canonicalize release compiler path");
19 }
20 panic!(
21 "compiler binary '{}' not built — run `cargo build --bins` first",
22 name
23 );
24 }
25
26 fn unique_path(stem: &str, ext: &str) -> PathBuf {
27 let pid = std::process::id();
28 let id = NEXT_TEMP_ID.fetch_add(1, Ordering::Relaxed);
29 std::env::temp_dir().join(format!(
30 "afs_alloc_validate_{}_{}_{}.{}",
31 stem, pid, id, ext
32 ))
33 }
34
35 fn unique_dir(stem: &str) -> PathBuf {
36 let dir = unique_path(stem, "dir");
37 std::fs::create_dir_all(&dir).expect("cannot create allocate-validation test directory");
38 dir
39 }
40
41 fn write_program_in(dir: &Path, name: &str, text: &str) -> PathBuf {
42 let path = dir.join(name);
43 std::fs::write(&path, text).expect("cannot write allocate-validation test source");
44 path
45 }
46
47 fn compile_program(source: &Path, output: &Path) -> std::process::Output {
48 Command::new(compiler("armfortas"))
49 .args([source.to_str().unwrap(), "-o", output.to_str().unwrap()])
50 .output()
51 .expect("failed to spawn armfortas compile")
52 }
53
54 fn compile_with_args(args: &[&str]) -> std::process::Output {
55 Command::new(compiler("armfortas"))
56 .args(args)
57 .output()
58 .expect("failed to spawn armfortas compile")
59 }
60
61 #[test]
62 fn bare_allocate_array_requires_shape_or_source_or_mold() {
63 let dir = unique_dir("bare_array");
64 let src = write_program_in(
65 &dir,
66 "main.f90",
67 "program p\n implicit none\n integer, allocatable :: a(:)\n allocate(a)\nend program\n",
68 );
69 let exe = dir.join("bare_array.bin");
70 let compile = compile_program(&src, &exe);
71 assert!(
72 !compile.status.success(),
73 "compile unexpectedly succeeded:\nstdout:\n{}\nstderr:\n{}",
74 String::from_utf8_lossy(&compile.stdout),
75 String::from_utf8_lossy(&compile.stderr)
76 );
77 let stderr = String::from_utf8_lossy(&compile.stderr);
78 assert!(
79 stderr.contains("array ALLOCATE requires bounds or SOURCE=/MOLD="),
80 "unexpected compile failure for bare array allocate: {}",
81 stderr
82 );
83
84 let _ = std::fs::remove_dir_all(&dir);
85 }
86
87 #[test]
88 fn bare_allocate_component_array_requires_shape_or_source_or_mold() {
89 let dir = unique_dir("bare_component_array");
90 let src = write_program_in(
91 &dir,
92 "main.f90",
93 "program p\n implicit none\n type :: box_t\n integer, allocatable :: vals(:)\n end type box_t\n type(box_t) :: box\n allocate(box%vals)\nend program\n",
94 );
95 let exe = dir.join("bare_component_array.bin");
96 let compile = compile_program(&src, &exe);
97 assert!(
98 !compile.status.success(),
99 "compile unexpectedly succeeded:\nstdout:\n{}\nstderr:\n{}",
100 String::from_utf8_lossy(&compile.stdout),
101 String::from_utf8_lossy(&compile.stderr)
102 );
103 let stderr = String::from_utf8_lossy(&compile.stderr);
104 assert!(
105 stderr.contains("array ALLOCATE requires bounds or SOURCE=/MOLD="),
106 "unexpected compile failure for bare component array allocate: {}",
107 stderr
108 );
109
110 let _ = std::fs::remove_dir_all(&dir);
111 }
112
113 #[test]
114 fn bare_allocate_scalar_allocatable_still_works() {
115 let dir = unique_dir("bare_scalar");
116 let src = write_program_in(
117 &dir,
118 "main.f90",
119 "program p\n implicit none\n integer, allocatable :: x\n allocate(x)\n x = 7\n print *, allocated(x)\n print *, x\nend program\n",
120 );
121 let exe = dir.join("bare_scalar.bin");
122 let compile = compile_program(&src, &exe);
123 assert!(
124 compile.status.success(),
125 "compile failed: {}",
126 String::from_utf8_lossy(&compile.stderr)
127 );
128
129 let run = Command::new(&exe)
130 .output()
131 .expect("scalar allocate runtime failed");
132 assert!(
133 run.status.success(),
134 "scalar allocate runtime failed: status={:?}\nstdout:\n{}\nstderr:\n{}",
135 run.status,
136 String::from_utf8_lossy(&run.stdout),
137 String::from_utf8_lossy(&run.stderr)
138 );
139 let stdout = String::from_utf8_lossy(&run.stdout);
140 assert!(
141 stdout.contains("T") && stdout.contains("7"),
142 "expected allocated scalar output: {}",
143 stdout
144 );
145
146 let _ = std::fs::remove_dir_all(&dir);
147 }
148
149 #[test]
150 fn imported_allocatable_component_array_requires_shape_or_source_or_mold() {
151 let dir = unique_dir("imported_component_array");
152 let mod_src = write_program_in(
153 &dir,
154 "m.f90",
155 "module m\n implicit none\n type :: box_t\n integer, allocatable :: vals(:)\n end type box_t\nend module\n",
156 );
157 let mod_obj = dir.join("m.o");
158 let compile_mod = compile_with_args(&[
159 "-c",
160 mod_src.to_str().unwrap(),
161 "-J",
162 dir.to_str().unwrap(),
163 "-o",
164 mod_obj.to_str().unwrap(),
165 ]);
166 assert!(
167 compile_mod.status.success(),
168 "module compile failed:\nstdout:\n{}\nstderr:\n{}",
169 String::from_utf8_lossy(&compile_mod.stdout),
170 String::from_utf8_lossy(&compile_mod.stderr)
171 );
172
173 let main_src = write_program_in(
174 &dir,
175 "main.f90",
176 "program p\n use m, only: box_t\n implicit none\n type(box_t) :: box\n allocate(box%vals)\nend program\n",
177 );
178 let exe = dir.join("imported_component_array.bin");
179 let compile_main = compile_with_args(&[
180 main_src.to_str().unwrap(),
181 "-I",
182 dir.to_str().unwrap(),
183 "-o",
184 exe.to_str().unwrap(),
185 ]);
186 assert!(
187 !compile_main.status.success(),
188 "compile unexpectedly succeeded:\nstdout:\n{}\nstderr:\n{}",
189 String::from_utf8_lossy(&compile_main.stdout),
190 String::from_utf8_lossy(&compile_main.stderr)
191 );
192 let stderr = String::from_utf8_lossy(&compile_main.stderr);
193 assert!(
194 stderr.contains("array ALLOCATE requires bounds or SOURCE=/MOLD="),
195 "unexpected compile failure for imported component array allocate: {}",
196 stderr
197 );
198
199 let _ = std::fs::remove_dir_all(&dir);
200 }
201
202 #[test]
203 fn imported_allocatable_component_scalar_still_works() {
204 let dir = unique_dir("imported_component_scalar");
205 let mod_src = write_program_in(
206 &dir,
207 "m.f90",
208 "module m\n implicit none\n type :: box_t\n integer, allocatable :: val\n end type box_t\nend module\n",
209 );
210 let mod_obj = dir.join("m.o");
211 let compile_mod = compile_with_args(&[
212 "-c",
213 mod_src.to_str().unwrap(),
214 "-J",
215 dir.to_str().unwrap(),
216 "-o",
217 mod_obj.to_str().unwrap(),
218 ]);
219 assert!(
220 compile_mod.status.success(),
221 "module compile failed:\nstdout:\n{}\nstderr:\n{}",
222 String::from_utf8_lossy(&compile_mod.stdout),
223 String::from_utf8_lossy(&compile_mod.stderr)
224 );
225
226 let main_src = write_program_in(
227 &dir,
228 "main.f90",
229 "program p\n use m, only: box_t\n implicit none\n type(box_t) :: box\n allocate(box%val)\n box%val = 9\n print *, box%val\nend program\n",
230 );
231 let exe = dir.join("imported_component_scalar.bin");
232 let compile_main = compile_with_args(&[
233 main_src.to_str().unwrap(),
234 "-I",
235 dir.to_str().unwrap(),
236 "-o",
237 exe.to_str().unwrap(),
238 ]);
239 assert!(
240 compile_main.status.success(),
241 "compile failed:\nstdout:\n{}\nstderr:\n{}",
242 String::from_utf8_lossy(&compile_main.stdout),
243 String::from_utf8_lossy(&compile_main.stderr)
244 );
245
246 let run = Command::new(&exe)
247 .output()
248 .expect("imported scalar allocate runtime failed");
249 assert!(
250 run.status.success(),
251 "imported scalar allocate runtime failed: status={:?}\nstdout:\n{}\nstderr:\n{}",
252 run.status,
253 String::from_utf8_lossy(&run.stdout),
254 String::from_utf8_lossy(&run.stderr)
255 );
256 let stdout = String::from_utf8_lossy(&run.stdout);
257 assert!(
258 stdout.contains("9"),
259 "expected imported scalar allocate output: {}",
260 stdout
261 );
262
263 let _ = std::fs::remove_dir_all(&dir);
264 }
265