Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2023 The Gleam contributors 3 4use ecow::{EcoString, eco_format}; 5use hexpm::version::Version; 6 7use super::*; 8use crate::{ 9 Warning, 10 build::SourceFingerprint, 11 io::{FileSystemWriter, memory::InMemoryFileSystem}, 12 line_numbers, 13 parse::extra::ModuleExtra, 14 warning::NullWarningEmitterIO, 15}; 16 17use std::time::Duration; 18 19#[derive(Debug)] 20struct LoaderTestOutput { 21 to_compile: Vec<EcoString>, 22 cached: Vec<EcoString>, 23 warnings: Vec<Warning>, 24} 25 26const TEST_SOURCE_1: &'static str = "const x = 1"; 27const TEST_SOURCE_2: &'static str = "const x = 2"; 28 29fn write_src(fs: &InMemoryFileSystem, path: &str, seconds: u64, src: &str) { 30 let path = Utf8Path::new(path); 31 fs.write(&path, src).unwrap(); 32 fs.set_modification_time(&path, SystemTime::UNIX_EPOCH + Duration::from_secs(seconds)); 33} 34 35fn write_cache( 36 fs: &InMemoryFileSystem, 37 name: &str, 38 seconds: u64, 39 deps: Vec<(EcoString, SrcSpan)>, 40 src: &str, 41) { 42 let line_numbers = line_numbers::LineNumbers::new(src); 43 let mtime = SystemTime::UNIX_EPOCH + Duration::from_secs(seconds); 44 let cache_metadata = CacheMetadata { 45 mtime, 46 codegen_performed: true, 47 dependencies: deps, 48 fingerprint: SourceFingerprint::new(src), 49 line_numbers: line_numbers.clone(), 50 }; 51 52 let artefact_name = name.replace("/", "@"); 53 let path = Utf8Path::new("/artefact").join(format!("{artefact_name}.cache_meta")); 54 fs.write_bytes(&path, &cache_metadata.to_binary()).unwrap(); 55 56 let cache = crate::type_::ModuleInterface { 57 name: name.into(), 58 origin: Origin::Src, 59 package: "my_package".into(), 60 types: Default::default(), 61 types_value_constructors: Default::default(), 62 values: Default::default(), 63 accessors: Default::default(), 64 line_numbers: line_numbers.clone(), 65 is_internal: false, 66 src_path: Utf8PathBuf::from(format!("/src/{}.gleam", name)), 67 warnings: vec![], 68 minimum_required_version: Version::new(0, 1, 0), 69 type_aliases: Default::default(), 70 documentation: Default::default(), 71 contains_echo: false, 72 references: Default::default(), 73 inline_functions: Default::default(), 74 }; 75 let path = Utf8Path::new("/artefact").join(format!("{artefact_name}.cache")); 76 fs.write_bytes(&path, &metadata::encode(&cache).unwrap()) 77 .unwrap(); 78} 79 80fn run_loader(fs: InMemoryFileSystem, root: &Utf8Path, artefact: &Utf8Path) -> LoaderTestOutput { 81 let mut defined = im::HashMap::new(); 82 let ids = UniqueIdGenerator::new(); 83 let (emitter, warnings) = WarningEmitter::vector(); 84 85 let loader = PackageLoader { 86 io: fs.clone(), 87 ids, 88 mode: Mode::Dev, 89 paths: ProjectPaths::new(root.into()), 90 warnings: &emitter, 91 codegen: CodegenRequired::Yes, 92 artefact_directory: &artefact, 93 package_name: &"my_package".into(), 94 target: Target::JavaScript, 95 stale_modules: &mut StaleTracker::default(), 96 already_defined_modules: &mut defined, 97 incomplete_modules: &mut HashSet::new(), 98 cached_warnings: CachedWarnings::Ignore, 99 }; 100 let loaded = loader.run().unwrap(); 101 102 LoaderTestOutput { 103 to_compile: loaded.to_compile.into_iter().map(|m| m.name).collect(), 104 cached: loaded.cached.into_iter().map(|m| m.name).collect(), 105 warnings: warnings.take(), 106 } 107} 108 109#[test] 110fn no_modules() { 111 let fs = InMemoryFileSystem::new(); 112 let root = Utf8Path::new("/"); 113 let artefact = Utf8Path::new("/artefact"); 114 115 let loaded = run_loader(fs, root, artefact); 116 assert!(loaded.to_compile.is_empty()); 117 assert!(loaded.cached.is_empty()); 118} 119 120#[test] 121fn one_src_module() { 122 let fs = InMemoryFileSystem::new(); 123 let root = Utf8Path::new("/"); 124 let artefact = Utf8Path::new("/artefact"); 125 126 write_src(&fs, "/src/main.gleam", 0, "const x = 1"); 127 128 let loaded = run_loader(fs, root, artefact); 129 assert_eq!(loaded.to_compile, vec![EcoString::from("main")]); 130 assert!(loaded.cached.is_empty()); 131} 132 133#[test] 134fn one_test_module() { 135 let fs = InMemoryFileSystem::new(); 136 let root = Utf8Path::new("/"); 137 let artefact = Utf8Path::new("/artefact"); 138 139 write_src(&fs, "/test/main.gleam", 0, "const x = 1"); 140 141 let loaded = run_loader(fs, root, artefact); 142 assert_eq!(loaded.to_compile, vec![EcoString::from("main")]); 143 assert!(loaded.cached.is_empty()); 144} 145 146#[test] 147fn one_dev_module() { 148 let fs = InMemoryFileSystem::new(); 149 let root = Utf8Path::new("/"); 150 let artefact = Utf8Path::new("/artefact"); 151 152 write_src(&fs, "/dev/main.gleam", 0, "const x = 1"); 153 154 let loaded = run_loader(fs, root, artefact); 155 assert_eq!(loaded.to_compile, vec![EcoString::from("main")]); 156 assert!(loaded.cached.is_empty()); 157} 158 159#[test] 160fn importing() { 161 let fs = InMemoryFileSystem::new(); 162 let root = Utf8Path::new("/"); 163 let artefact = Utf8Path::new("/artefact"); 164 165 write_src(&fs, "/src/three.gleam", 0, "import two"); 166 write_src(&fs, "/src/one.gleam", 0, ""); 167 write_src(&fs, "/src/two.gleam", 0, "import one"); 168 169 let loaded = run_loader(fs, root, artefact); 170 assert_eq!( 171 loaded.to_compile, 172 vec![ 173 EcoString::from("one"), 174 EcoString::from("two"), 175 EcoString::from("three") 176 ] 177 ); 178 assert!(loaded.cached.is_empty()); 179} 180 181#[test] 182fn reading_cache() { 183 let fs = InMemoryFileSystem::new(); 184 let root = Utf8Path::new("/"); 185 let artefact = Utf8Path::new("/artefact"); 186 187 write_src(&fs, "/src/one.gleam", 0, TEST_SOURCE_1); 188 write_cache(&fs, "one", 0, vec![], TEST_SOURCE_1); 189 190 let loaded = run_loader(fs, root, artefact); 191 assert!(loaded.to_compile.is_empty()); 192 assert_eq!(loaded.cached, vec![EcoString::from("one")]); 193} 194 195#[test] 196fn module_is_stale_if_cache_older() { 197 let fs = InMemoryFileSystem::new(); 198 let root = Utf8Path::new("/"); 199 let artefact = Utf8Path::new("/artefact"); 200 201 write_src(&fs, "/src/one.gleam", 1, TEST_SOURCE_2); 202 write_cache(&fs, "one", 0, vec![], TEST_SOURCE_1); 203 204 let loaded = run_loader(fs, root, artefact); 205 assert_eq!(loaded.to_compile, vec![EcoString::from("one")]); 206 assert!(loaded.cached.is_empty()); 207} 208 209#[test] 210fn module_is_stale_if_deps_are_stale() { 211 let fs = InMemoryFileSystem::new(); 212 let root = Utf8Path::new("/"); 213 let artefact = Utf8Path::new("/artefact"); 214 215 // Cache is stale 216 write_src(&fs, "/src/one.gleam", 1, TEST_SOURCE_2); 217 write_cache(&fs, "one", 0, vec![], TEST_SOURCE_1); 218 219 // Cache is fresh but dep is stale 220 write_src(&fs, "/src/two.gleam", 1, "import one"); 221 write_cache( 222 &fs, 223 "two", 224 2, 225 vec![(EcoString::from("one"), SrcSpan { start: 0, end: 0 })], 226 "import one", 227 ); 228 229 // Cache is fresh 230 write_src(&fs, "/src/three.gleam", 1, TEST_SOURCE_1); 231 write_cache(&fs, "three", 2, vec![], TEST_SOURCE_1); 232 233 let loaded = run_loader(fs, root, artefact); 234 assert_eq!( 235 loaded.to_compile, 236 vec![EcoString::from("one"), EcoString::from("two")] 237 ); 238 assert_eq!(loaded.cached, vec![EcoString::from("three")]); 239} 240 241#[test] 242fn module_is_stale_if_deps_removed() { 243 let fs = InMemoryFileSystem::new(); 244 let root = Utf8Path::new("/"); 245 let artefact = Utf8Path::new("/artefact"); 246 247 // Source is removed, cache is present 248 write_cache(&fs, "nested/one", 0, vec![], TEST_SOURCE_1); 249 250 // Cache is fresh but dep is removed 251 write_src(&fs, "/src/two.gleam", 1, "import one"); 252 write_cache( 253 &fs, 254 "two", 255 2, 256 vec![(EcoString::from("nested/one"), SrcSpan { start: 0, end: 0 })], 257 "import nested/one", 258 ); 259 260 let loaded = run_loader(fs, root, artefact); 261 assert_eq!(loaded.to_compile, vec![EcoString::from("two")]); 262} 263 264#[test] 265fn module_continues_to_be_stale_if_deps_get_updated() { 266 let fs = InMemoryFileSystem::new(); 267 let root = Utf8Path::new("/"); 268 let artefact = Utf8Path::new("/artefact"); 269 270 // Cache is stale 271 write_src(&fs, "/src/one.gleam", 1, TEST_SOURCE_2); 272 write_cache(&fs, "one", 0, vec![], TEST_SOURCE_1); 273 274 // Cache is fresh but dep is stale 275 write_src(&fs, "/src/two.gleam", 1, "import one"); 276 write_cache( 277 &fs, 278 "two", 279 2, 280 vec![(EcoString::from("one"), SrcSpan { start: 0, end: 0 })], 281 "import one", 282 ); 283 284 // Cache is fresh 285 write_src(&fs, "/src/three.gleam", 1, TEST_SOURCE_1); 286 write_cache(&fs, "three", 2, vec![], TEST_SOURCE_1); 287 288 let _loaded1 = run_loader(fs.clone(), root, artefact); 289 290 // update the dependency 291 write_cache(&fs, "one", 3, vec![], TEST_SOURCE_2); 292 let loaded2 = run_loader(fs, root, artefact); 293 294 assert_eq!(loaded2.to_compile, vec![EcoString::from("two")]); 295 assert_eq!( 296 loaded2.cached, 297 vec![EcoString::from("one"), EcoString::from("three")] 298 ); 299} 300 301#[test] 302fn invalid_module_name() { 303 let fs = InMemoryFileSystem::new(); 304 let root = Utf8Path::new("/"); 305 let artefact = Utf8Path::new("/artefact"); 306 307 // Cache is stale 308 write_src(&fs, "/src/One.gleam", 1, TEST_SOURCE_2); 309 310 let loaded = run_loader(fs, root, artefact); 311 assert!(loaded.to_compile.is_empty()); 312 assert!(loaded.cached.is_empty()); 313 assert_eq!( 314 loaded.warnings, 315 vec![Warning::InvalidSource { 316 path: Utf8PathBuf::from("/src/One.gleam"), 317 }], 318 ); 319} 320 321#[test] 322fn invalid_nested_module_name() { 323 let fs = InMemoryFileSystem::new(); 324 let root = Utf8Path::new("/"); 325 let artefact = Utf8Path::new("/artefact"); 326 327 // Cache is stale 328 write_src(&fs, "/src/1/one.gleam", 1, TEST_SOURCE_2); 329 330 let loaded = run_loader(fs, root, artefact); 331 assert!(loaded.to_compile.is_empty()); 332 assert!(loaded.cached.is_empty()); 333 assert_eq!( 334 loaded.warnings, 335 vec![Warning::InvalidSource { 336 path: Utf8PathBuf::from("/src/1/one.gleam"), 337 }], 338 ); 339} 340 341#[test] 342fn invalid_module_name_in_test() { 343 let fs = InMemoryFileSystem::new(); 344 let root = Utf8Path::new("/"); 345 let artefact = Utf8Path::new("/artefact"); 346 347 // Cache is stale 348 write_src(&fs, "/test/One.gleam", 1, TEST_SOURCE_2); 349 350 let loaded = run_loader(fs, root, artefact); 351 assert!(loaded.to_compile.is_empty()); 352 assert!(loaded.cached.is_empty()); 353 assert_eq!( 354 loaded.warnings, 355 vec![Warning::InvalidSource { 356 path: Utf8PathBuf::from("/test/One.gleam"), 357 }], 358 ); 359} 360 361#[test] 362fn invalid_nested_module_name_in_test() { 363 let fs = InMemoryFileSystem::new(); 364 let root = Utf8Path::new("/"); 365 let artefact = Utf8Path::new("/artefact"); 366 367 // Cache is stale 368 write_src(&fs, "/test/1/one.gleam", 1, TEST_SOURCE_2); 369 370 let loaded = run_loader(fs, root, artefact); 371 assert!(loaded.to_compile.is_empty()); 372 assert!(loaded.cached.is_empty()); 373 assert_eq!( 374 loaded.warnings, 375 vec![Warning::InvalidSource { 376 path: Utf8PathBuf::from("/test/1/one.gleam"), 377 }], 378 ); 379} 380 381#[test] 382fn invalid_module_name_in_dev() { 383 let fs = InMemoryFileSystem::new(); 384 let root = Utf8Path::new("/"); 385 let artefact = Utf8Path::new("/artefact"); 386 387 // Cache is stale 388 write_src(&fs, "/dev/One.gleam", 1, TEST_SOURCE_2); 389 390 let loaded = run_loader(fs, root, artefact); 391 assert!(loaded.to_compile.is_empty()); 392 assert!(loaded.cached.is_empty()); 393 assert_eq!( 394 loaded.warnings, 395 vec![Warning::InvalidSource { 396 path: Utf8PathBuf::from("/dev/One.gleam"), 397 }], 398 ); 399} 400 401#[test] 402fn invalid_nested_module_name_in_dev() { 403 let fs = InMemoryFileSystem::new(); 404 let root = Utf8Path::new("/"); 405 let artefact = Utf8Path::new("/artefact"); 406 407 // Cache is stale 408 write_src(&fs, "/dev/1/one.gleam", 1, TEST_SOURCE_2); 409 410 let loaded = run_loader(fs, root, artefact); 411 assert!(loaded.to_compile.is_empty()); 412 assert!(loaded.cached.is_empty()); 413 assert_eq!( 414 loaded.warnings, 415 vec![Warning::InvalidSource { 416 path: Utf8PathBuf::from("/dev/1/one.gleam"), 417 }], 418 ); 419} 420 421#[test] 422fn cache_files_are_removed_when_source_removed() { 423 let fs = InMemoryFileSystem::new(); 424 let root = Utf8Path::new("/"); 425 let artefact = Utf8Path::new("/artefact"); 426 427 // Source is removed, cache is present 428 write_cache(&fs, "nested/one", 0, vec![], TEST_SOURCE_1); 429 430 _ = run_loader(fs.clone(), root, artefact); 431 432 assert_eq!(fs.files().len(), 0); 433}