1
extern crate core;
2

            
3
pub mod cache;
4
pub mod cli;
5
pub mod compiler;
6
pub mod config_file;
7
pub mod domain;
8
pub mod project_model;
9
pub mod utils;
10

            
11
/// The entry point for the execution of the program.
12
///
13
/// This module existence is motivated to let us run
14
/// integration tests for the whole operations of the program
15
/// without having to do fancy work about checking the
16
/// data sent to stdout/stderr
17
pub mod worker {
18
    use crate::config_file;
19
    use crate::config_file::ZorkConfigFile;
20
    use crate::domain::target::Target;
21
    use crate::project_model;
22
    use std::path::PathBuf;
23
    use std::{fs, path::Path, time::Instant};
24

            
25
    use crate::utils::constants::{dir_names, error_messages, ZORK};
26
    use crate::{
27
        cache::{self, ZorkCache},
28
        cli::{
29
            input::{CliArgs, Command},
30
            output::executors,
31
        },
32
        compiler::generate_commands_arguments,
33
        project_model::{compiler::CppCompiler, ZorkModel},
34
        utils::{
35
            self,
36
            reader::{find_config_files, ConfigFile},
37
            template::create_templated_project,
38
        },
39
    };
40
    use color_eyre::eyre::ContextCompat;
41
    use color_eyre::{eyre::Context, Report, Result};
42

            
43
    /// The main work of the project. Runs the tasks
44
    /// inputted in the CLI
45
4
    pub fn run_zork(cli_args: &CliArgs) -> std::result::Result<(), Report> {
46
4
        let abs_project_root = determine_absolute_path_of_the_project_root(cli_args)?;
47

            
48
        // If this run is just for create a new C++ project with the given Zork++ projects creation
49
        // by template, create it and exit
50
4
        if it_is_template_creation_then_create(cli_args, &abs_project_root)? {
51
2
            return Ok(());
52
        };
53

            
54
        let config_files: Vec<ConfigFile> =
55
2
            find_config_files(&abs_project_root, &cli_args.match_files)?;
56

            
57
4
        for config_file in config_files {
58
2
            let cfg_path = &config_file.path;
59
2
            log::debug!(
60
                "Launching a Zork++ work event for the configuration file: {:?}",
61
                cfg_path,
62
            );
63
2
            let raw_file = fs::read_to_string(cfg_path)
64
                .with_context(|| format!("{}: {:?}", error_messages::READ_CFG_FILE, cfg_path))?;
65

            
66
2
            let config: ZorkConfigFile<'_> = config_file::zork_cfg_from_file(raw_file.as_str())
67
                .with_context(|| error_messages::PARSE_CFG_FILE)?;
68

            
69
2
            create_output_directory(&config, &abs_project_root)?; // NOTE: review if we must
70
                                                                  // rebuilt the cache and model if the
71
                                                                  // output dir changes from
72
                                                                  // previous
73

            
74
2
            let mut cache: ZorkCache<'_> = cache::load(&config, cli_args, &abs_project_root)?;
75

            
76
            let program_data = {
77
                // The purpose of this scope is to let the reader to see clearly
78
                // that the model will only be mutated within the scope of this
79
                // block, and after it, it will be read-only data
80
2
                let mut program_data: ZorkModel<'_> = load_zork_model(
81
                    &mut cache,
82
                    &config_file,
83
2
                    config,
84
                    cli_args,
85
2
                    &abs_project_root,
86
                )?;
87
2
                map_model_targets_to_cache(&mut program_data, &mut cache, cli_args)?;
88

            
89
2
                program_data
90
            };
91

            
92
2
            cache
93
                .process_compiler_metadata(&program_data)
94
                .with_context(|| error_messages::FAILURE_LOADING_COMPILER_METADATA)?;
95

            
96
            // Perform main work
97
6
            perform_main_work(cli_args, &program_data, &mut cache, cfg_path)?; // NOTE: study if we
98
                                                                               // must provide a flag to continue working with other cfgs (if present) if the current
99
                                                                               // fails or abort without continuing (current behaviour)
100
4
        }
101

            
102
2
        Ok(())
103
4
    }
104

            
105
    /// Inspects the [`CliArgs`] main passed argument, and if it's [`Command::New`] just creates a
106
    /// new *C++* project at the *abs_project_root* and exits
107
4
    fn it_is_template_creation_then_create(
108
        cli_args: &CliArgs,
109
        abs_project_root: &Path,
110
    ) -> Result<bool> {
111
4
        if let Command::New {
112
2
            ref name,
113
2
            git,
114
2
            compiler,
115
2
            template,
116
4
        } = cli_args.command
117
        {
118
4
            create_templated_project(abs_project_root, name, git, compiler.into(), template)?;
119
2
            return Ok(true);
120
        };
121
2
        Ok(false)
122
4
    }
123

            
124
2
    fn perform_main_work<'a>(
125
        cli_args: &CliArgs,
126
        program_data: &'a ZorkModel<'a>,
127
        cache: &mut ZorkCache<'a>,
128
        cfg_path: &Path,
129
    ) -> Result<()> {
130
2
        let generate_commands_ts = Instant::now();
131

            
132
2
        generate_commands_arguments(program_data, cache)
133
            .with_context(|| error_messages::FAILURE_GENERATING_COMMANDS)?;
134

            
135
2
        log::debug!(
136
            "Zork++ took a total of {:?} ms on handling the generated commands",
137
            generate_commands_ts.elapsed().as_millis()
138
        );
139

            
140
2
        let work_result = do_main_work_based_on_cli_input(cli_args, program_data, cache)
141
            .with_context(|| {
142
                format!(
143
                    "{}: {:?}",
144
                    error_messages::FAILED_BUILD_FOR_CFG_FILE,
145
                    cfg_path
146
                )
147
            });
148

            
149
        // Save the cached data for this config file
150
4
        cache.save(program_data)?;
151

            
152
2
        work_result.with_context(|| format!("Failed to complete the job for: {:?}", cfg_path))
153
2
    }
154

            
155
2
    fn do_main_work_based_on_cli_input(
156
        cli_args: &CliArgs,
157
        program_data: &ZorkModel<'_>,
158
        cache: &mut ZorkCache<'_>,
159
    ) -> Result<()> {
160
2
        let modules_generated_commands = &mut cache.generated_commands.modules;
161
2
        let targets_generated_commands = &mut cache.generated_commands.targets;
162

            
163
2
        let flyweight_data = &mut cache
164
            .generated_commands
165
            .flyweight_data
166
            .as_ref()
167
            .with_context(|| error_messages::FAILURE_LOADING_FLYWEIGHT_DATA)?;
168

            
169
2
        executors::run_modules_generated_commands(
170
            program_data,
171
2
            flyweight_data,
172
            modules_generated_commands,
173
        )?;
174

            
175
2
        let target_executed_commands = executors::run_targets_generated_commands(
176
            program_data,
177
2
            flyweight_data,
178
            targets_generated_commands,
179
            modules_generated_commands,
180
        );
181

            
182
2
        match cli_args.command {
183
            Command::Build => target_executed_commands,
184
2
            Command::Run | Command::Test => match target_executed_commands {
185
                Ok(_) => {
186
6
                    for (target_identifier, target_data) in targets_generated_commands.iter() {
187
4
                        if target_data.enabled_for_current_program_iteration {
188
6
                            executors::autorun_generated_binary(
189
4
                                &program_data.compiler.cpp_compiler,
190
4
                                &program_data.build.output_dir,
191
4
                                target_identifier.name(),
192
                            )?
193
                        }
194
                    }
195

            
196
2
                    return Ok(());
197
                }
198
                Err(e) => Err(e),
199
            }?,
200
            _ => todo!("{}", error_messages::CLI_ARGS_CMD_NEW_BRANCH),
201
        }
202
2
    }
203

            
204
    /// Resolves the full path of the location of the project's root on the fs. If the `--root`
205
    /// [`CliArgs`] arg is present, it will be used as the project root path, otherwise, we will
206
    /// assume that the project root is exactly in the same directory from where the *Zork++*
207
    /// binary was invoked by the user
208
4
    fn determine_absolute_path_of_the_project_root(cli_args: &CliArgs) -> Result<PathBuf> {
209
8
        let project_root = cli_args
210
            .root
211
            .as_deref()
212
            .map(Path::new)
213
4
            .unwrap_or(Path::new("."));
214

            
215
4
        utils::fs::get_project_root_absolute_path(project_root)
216
            .with_context(|| error_messages::FAILURE_GATHERING_PROJECT_ROOT_ABS_PATH)
217
4
    }
218

            
219
    /// Helper function to load the data of a concrete [`ZorkConfigFile`] into a [`ZorkModel`],
220
    /// which is the ultimate data structure that holds the read only information about the user
221
    /// input in a more concise way that the config file struct.
222
2
    fn load_zork_model<'a>(
223
        cache: &mut ZorkCache<'a>,
224
        meta_config_file: &ConfigFile,
225
        zork_config_file: ZorkConfigFile<'a>,
226
        cli_args: &'a CliArgs,
227
        abs_project_root: &Path,
228
    ) -> Result<ZorkModel<'a>> {
229
4
        if meta_config_file.last_time_modified > cache.metadata.last_program_execution {
230
2
            cache.metadata.cfg_modified = true;
231
            let project_model =
232
2
                utils::reader::build_model(zork_config_file, cli_args, abs_project_root)?;
233

            
234
            // Check for the changes made by the user on the cfg
235
4
            check_for_deletions_in_cfg(&project_model, cache)
236
                .with_context(|| error_messages::CHECK_FOR_DELETIONS)?;
237

            
238
2
            Ok(project_model)
239
        } else {
240
            log::debug!("Loading the ZorkModel from the cache");
241
            project_model::load(cache)
242
        }
243
2
    }
244

            
245
    /// Little helper to check if the user remove files from the [`ZorkConfigFile`] and therefore,
246
    /// they should be removed from the cache
247
2
    fn check_for_deletions_in_cfg(
248
        project_model: &ZorkModel,
249
        cache: &mut ZorkCache<'_>,
250
    ) -> Result<()> {
251
2
        let process_removals = Instant::now();
252
2
        let deletions_on_cfg = cache::helpers::check_user_files_removals(cache, project_model);
253
2
        log::debug!(
254
            "Zork++ took a total of {:?} ms on checking and process removed items",
255
            process_removals.elapsed().as_millis()
256
        );
257

            
258
2
        if deletions_on_cfg? {
259
            cache.metadata.generate_compilation_database = true;
260
        }
261

            
262
2
        Ok(())
263
2
    }
264

            
265
    /// Helper to map the user declared targets on the [`ZorkModel`], previously mapped from the
266
    /// [`ZorkConfigFile`] into the [`ZorkCache`]
267
    /// Also, it takes care about enabling or disabling targets (based on their presence on the cfg during
268
    /// the program iterations) and handles cache removes when they are deleted from the cfg
269
5
    fn map_model_targets_to_cache<'a>(
270
        program_data: &mut ZorkModel<'a>,
271
        cache: &mut ZorkCache<'a>,
272
        cli_args: &CliArgs,
273
    ) -> Result<()> {
274
14
        for (target_identifier, target_data) in program_data.targets.iter_mut() {
275
            // 1st - Check if there's any new target to add to the tracked ones
276
9
            helpers::add_new_target_to_cache(target_identifier, target_data, cache);
277

            
278
            // 2nd - Inspect the CliArgs to enable or disable targets for the current iteration
279
14
            helpers::enable_or_disable_target_based_on_cli_inputs(
280
                target_identifier,
281
                target_data,
282
                cache,
283
                cli_args,
284
            )?;
285
        }
286

            
287
5
        log::info!(
288
            "Target enabled for this iteration of Zork++: {:?}",
289
            program_data
290
                .targets
291
                .iter()
292
                .filter(|(_, data)| data.enabled_for_current_program_iteration)
293
                .map(|(id, _)| id.name())
294
                .collect::<Vec<_>>()
295
        );
296

            
297
        // 3rd - Remove from the cache the ones that the user removed from the cfg file (if they
298
        // was tracked already)
299
5
        helpers::delete_from_cache_removed_targets_from_cfg_file(program_data, cache);
300

            
301
5
        Ok(())
302
5
    }
303

            
304
    /// Creates the directory for output the elements generated
305
    /// during the build process based on the client specification.
306
    ///
307
    /// Also, it will generate the
308
    /// ['<output_build_dir>'/zork], which is a subfolder
309
    /// where Zork dumps the things that needs to work correctly
310
    /// under different conditions.
311
    ///
312
    /// Under /zork, some new folders are created:
313
    /// - a /intrinsics folder in created as well,
314
    ///     where different specific details of Zork++ are stored
315
    ///     related with the C++ compilers
316
    ///
317
    /// - a /cache folder, where lives the metadata cached by Zork++
318
    ///     in order to track different aspects of the program (last time
319
    ///     modified files, last process build time...)
320
3
    fn create_output_directory(config: &ZorkConfigFile, project_root: &Path) -> Result<()> {
321
3
        let compiler: CppCompiler = config.compiler.cpp_compiler.into();
322
3
        let compiler_name = compiler.as_ref();
323
3
        let binding = config
324
            .build
325
            .as_ref()
326
3
            .and_then(|build_attr| build_attr.output_dir)
327
            .unwrap_or("out");
328
3
        let out_dir = Path::new(project_root).join(binding);
329

            
330
        // Recursively create the directories below and all of its parent components if they are missing
331
3
        let modules_path = out_dir.join(compiler_name).join(dir_names::MODULES);
332

            
333
3
        let zork_path = out_dir.join(ZORK);
334
3
        let zork_cache_path = zork_path.join(dir_names::CACHE);
335
3
        let zork_intrinsics_path = zork_path.join(dir_names::INTRINSICS);
336

            
337
3
        utils::fs::create_directory(&out_dir.join(compiler_name).join(dir_names::OBJECT_FILES))?;
338

            
339
3
        utils::fs::create_directory(&modules_path.join(dir_names::INTERFACES))?;
340
3
        utils::fs::create_directory(&modules_path.join(dir_names::IMPLEMENTATIONS))?;
341
3
        utils::fs::create_directory(&modules_path.join(dir_names::STD))?;
342

            
343
3
        utils::fs::create_directory(&zork_cache_path)?;
344
3
        utils::fs::create_directory(&zork_intrinsics_path)?;
345

            
346
        // Pre Clang-18 way
347
3
        if compiler.eq(&CppCompiler::CLANG) && cfg!(target_os = "windows") {
348
            utils::fs::create_file(
349
                &zork_intrinsics_path,
350
                "std.h",
351
                utils::template::resources::STD_HEADER.as_bytes(),
352
            )?;
353

            
354
3
            utils::fs::create_file(
355
                &zork_intrinsics_path,
356
                "zork.modulemap",
357
                utils::template::resources::ZORK_MODULEMAP.as_bytes(),
358
            )?;
359
        }
360

            
361
3
        Ok(())
362
3
    }
363

            
364
    mod helpers {
365
        use crate::domain::target::TargetIdentifier;
366
        use project_model::target::TargetModel;
367

            
368
        use super::*;
369

            
370
13
        pub(crate) fn add_new_target_to_cache<'a>(
371
            target_identifier: &TargetIdentifier<'a>,
372
            target_data: &mut TargetModel<'a>,
373
            cache: &mut ZorkCache<'a>,
374
        ) {
375
13
            if !cache
376
                .generated_commands
377
                .targets
378
                .contains_key(target_identifier)
379
            {
380
12
                log::debug!(
381
                    "Adding a new target to the cache: {}",
382
                    target_identifier.name()
383
                );
384
24
                cache.generated_commands.targets.insert(
385
12
                    target_identifier.clone(),
386
12
                    Target::new_default_for_kind(target_data.kind),
387
12
                );
388
            }
389
13
        }
390

            
391
11
        pub(crate) fn enable_or_disable_target_based_on_cli_inputs<'a>(
392
            target_identifier: &TargetIdentifier<'a>,
393
            target_data: &mut TargetModel,
394
            cache: &mut ZorkCache<'a>,
395
            cli_args: &CliArgs,
396
        ) -> Result<()> {
397
11
            let target_name = target_identifier.name();
398

            
399
11
            let cached_target = cache
400
                .generated_commands
401
                .targets
402
                .get_mut(target_identifier)
403
                .with_context(|| error_messages::TARGET_ENTRY_NOT_FOUND)?;
404

            
405
11
            let enabled = if let Some(filtered_targets) = cli_args.targets.as_ref() {
406
                // If there's Some(v), there's no need to check for emptyness on the underlying Vec (at least must be one)
407
16
                let enabled = filtered_targets.iter().any(|t| t.eq(target_name));
408
7
                enabled
409
            } else {
410
4
                true
411
            };
412

            
413
            // If it's a [CliArgs::Test] command invokation, enable only the ones that contains
414
            // a <*test*> string in its identifier
415
11
            let enabled = if cli_args.command.eq(&Command::Test) {
416
                target_name.contains("test")
417
            } else {
418
11
                enabled
419
            };
420

            
421
11
            target_data.enabled_for_current_program_iteration = enabled;
422
11
            cached_target.enabled_for_current_program_iteration = enabled;
423

            
424
11
            Ok(())
425
11
        }
426

            
427
5
        pub(crate) fn delete_from_cache_removed_targets_from_cfg_file(
428
            program_data: &ZorkModel,
429
            cache: &mut ZorkCache,
430
        ) {
431
5
            let targets = &mut cache.generated_commands.targets;
432
15
            targets.retain(|cached_target_identifier, _| {
433
10
                program_data.targets.contains_key(cached_target_identifier)
434
10
            });
435
5
        }
436
    }
437

            
438
    #[cfg(test)]
439
    mod tests {
440
        use std::borrow::Cow;
441
        use std::path::Path;
442

            
443
        use crate::cache::{self, ZorkCache};
444
        use crate::cli::input::CliArgs;
445
        use crate::domain::target::TargetIdentifier;
446
        use crate::project_model::compiler::CppCompiler;
447
        use crate::project_model::ZorkModel;
448
        use crate::utils;
449
        use crate::utils::template::resources::CONFIG_FILE;
450
        use clap::Parser;
451
        use color_eyre::Result;
452
        use tempfile::tempdir;
453

            
454
        use crate::config_file::{self, ZorkConfigFile};
455
        use crate::utils::constants::{dir_names, ZORK};
456

            
457
        use super::{helpers, map_model_targets_to_cache};
458

            
459
        #[test]
460
2
        fn test_creation_directories() -> Result<()> {
461
1
            let temp = tempdir()?;
462
1
            let temp_path = temp.path();
463
1
            let out_dir = temp_path.join(dir_names::DEFAULT_OUTPUT_DIR);
464

            
465
1
            let zork_dir = out_dir.join(ZORK);
466

            
467
1
            let normalized_cfg_file = CONFIG_FILE
468
                .replace("<compiler>", "clang")
469
                .replace("<std_lib>", "LIBCPP")
470
1
                .replace('\\', "/");
471
1
            let zcf: ZorkConfigFile = config_file::zork_cfg_from_file(&normalized_cfg_file)?;
472

            
473
1
            let compiler: CppCompiler = zcf.compiler.cpp_compiler.into();
474
1
            let compiler_folder_dir = out_dir.join(compiler.as_ref());
475
1
            let modules_path = compiler_folder_dir.join("modules");
476

            
477
            // This should create and out/ directory at the root of the tmp path
478
1
            super::create_output_directory(&zcf, temp_path)?;
479

            
480
1
            assert!(out_dir.exists());
481

            
482
1
            assert!(compiler_folder_dir.exists());
483

            
484
1
            assert!(compiler_folder_dir.join(dir_names::OBJECT_FILES).exists());
485
1
            assert!(modules_path.exists());
486

            
487
1
            assert!(modules_path.join(dir_names::INTERFACES).exists());
488
1
            assert!(modules_path.join(dir_names::IMPLEMENTATIONS).exists());
489
1
            assert!(modules_path.join(dir_names::STD).exists());
490

            
491
1
            assert!(zork_dir.exists());
492
1
            assert!(zork_dir.join(dir_names::CACHE).exists());
493
1
            assert!(zork_dir.join(dir_names::INTRINSICS).exists());
494

            
495
1
            Ok(())
496
2
        }
497

            
498
        #[test]
499
2
        fn test_add_entry_to_cache() -> Result<()> {
500
1
            let cli_args: CliArgs = CliArgs::parse_from(["", "build"]);
501
1
            let zcf: ZorkConfigFile = config_file::zork_cfg_from_file(CONFIG_FILE)?;
502
1
            let mut model: ZorkModel = utils::reader::build_model(zcf, &cli_args, Path::new("."))?;
503
1
            let mut cache: ZorkCache = cache::ZorkCache::default();
504

            
505
3
            for (target_identifier, target_data) in model.targets.iter_mut() {
506
2
                helpers::add_new_target_to_cache(target_identifier, target_data, &mut cache);
507
2
                assert!(cache
508
                    .generated_commands
509
                    .targets
510
                    .contains_key(target_identifier));
511
2
                assert!(!cache
512
                    .generated_commands
513
                    .targets
514
                    .contains_key(&TargetIdentifier(Cow::Borrowed("other"))));
515
            }
516

            
517
1
            Ok(())
518
2
        }
519

            
520
        #[test]
521
2
        fn test_enable_disable_targets_by_cli_input() -> Result<()> {
522
            let cli_args: CliArgs =
523
1
                CliArgs::parse_from(["", "--targets", "executable,tests", "build"]);
524
1
            let zcf: ZorkConfigFile = config_file::zork_cfg_from_file(CONFIG_FILE)?;
525
1
            let mut model: ZorkModel = utils::reader::build_model(zcf, &cli_args, Path::new("."))?;
526
1
            let mut cache: ZorkCache = cache::ZorkCache::default();
527

            
528
            // map_model_targets_to_cache(&mut model, &mut cache, &cli_args)?;
529

            
530
3
            for (target_identifier, target_data) in model.targets.iter_mut() {
531
2
                helpers::add_new_target_to_cache(target_identifier, target_data, &mut cache);
532
2
                helpers::enable_or_disable_target_based_on_cli_inputs(
533
                    target_identifier,
534
                    target_data,
535
                    &mut cache,
536
                    &cli_args,
537
                )?;
538
2
                assert!(cache
539
                    .generated_commands
540
                    .targets
541
                    .contains_key(target_identifier));
542

            
543
2
                let cached_target = cache
544
                    .generated_commands
545
                    .targets
546
                    .get(target_identifier)
547
                    .unwrap();
548
2
                assert!(cached_target.enabled_for_current_program_iteration);
549
            }
550

            
551
1
            Ok(())
552
2
        }
553

            
554
        #[test]
555
2
        fn test_clean_removed_targets_from_cfg() -> Result<()> {
556
1
            let cli_args: CliArgs = CliArgs::parse_from(["", "--targets", "executable", "build"]);
557
1
            let zcf: ZorkConfigFile = config_file::zork_cfg_from_file(CONFIG_FILE)?;
558
1
            let mut model: ZorkModel = utils::reader::build_model(zcf, &cli_args, Path::new("."))?;
559
1
            let mut cache: ZorkCache = cache::ZorkCache::default();
560

            
561
1
            map_model_targets_to_cache(&mut model, &mut cache, &cli_args)?;
562

            
563
1
            let tests_ti = TargetIdentifier::from("tests");
564
1
            let tests = cache.generated_commands.targets.get(&tests_ti).unwrap();
565
1
            assert!(!tests.enabled_for_current_program_iteration);
566

            
567
3
            model.targets.retain(|k, _| k.ne(&tests_ti));
568
1
            map_model_targets_to_cache(&mut model, &mut cache, &cli_args)?;
569

            
570
1
            assert!(cache.generated_commands.targets.get(&tests_ti).is_none());
571

            
572
1
            Ok(())
573
2
        }
574

            
575
        #[test]
576
2
        fn test_map_model_targets_to_cache_and_enabled_status() -> Result<()> {
577
            let cli_args: CliArgs =
578
1
                CliArgs::parse_from(["", "--targets", "executable,tests", "build"]);
579
1
            let zcf: ZorkConfigFile = config_file::zork_cfg_from_file(CONFIG_FILE)?;
580
1
            let mut model: ZorkModel = utils::reader::build_model(zcf, &cli_args, Path::new("."))?;
581
1
            let mut cache: ZorkCache = cache::ZorkCache::default();
582

            
583
1
            map_model_targets_to_cache(&mut model, &mut cache, &cli_args)?;
584

            
585
1
            let cached_targets = cache.generated_commands.targets;
586

            
587
3
            for (target_identifier, _) in model.targets.iter_mut() {
588
2
                let cached_target = cached_targets.get(target_identifier);
589
2
                assert!(cached_target.is_some());
590
            }
591

            
592
1
            let executable = cached_targets
593
1
                .get(&TargetIdentifier::from("executable"))
594
1
                .unwrap();
595
1
            assert!(executable.enabled_for_current_program_iteration);
596

            
597
1
            let tests = cached_targets
598
1
                .get(&TargetIdentifier::from("tests"))
599
1
                .unwrap();
600
1
            assert!(tests.enabled_for_current_program_iteration);
601

            
602
1
            Ok(())
603
2
        }
604
    }
605
}