stanhope/
read_ebml.rs

1//! *Read a markup file and return a robust Process struct*
2//! 
3//! All information in the markup file is stored in the Process struct, including information for every step of the Process and the metadata about the Process document's place in the organizations document library.
4//! 
5//! Structs and Enums are public, but associated fields are private. "Get" and "Set"/"Add" functions exist throughout the implementations such that downstream users can query (or modify) data. Note: changes to process information should occur at the source markup file for it to be re-read, as to retain the truth in the author's source file.
6
7use std::{
8	fs::File,
9	io::{self, BufRead, BufReader},
10	path::Path,
11};
12use crate::globify_document_number;
13
14//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
15// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
16// ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░▌       ▐░▌▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
17// ▐░▌               ▐░▌     ▐░▌       ▐░▌▐░▌       ▐░▌▐░▌               ▐░▌     
18// ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     ▐░█▄▄▄▄▄▄▄█░▌▐░▌       ▐░▌▐░▌               ▐░▌     
19// ▐░░░░░░░░░░░▌     ▐░▌     ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌               ▐░▌     
20//  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     ▐░█▀▀▀▀█░█▀▀ ▐░▌       ▐░▌▐░▌               ▐░▌     
21//           ▐░▌     ▐░▌     ▐░▌     ▐░▌  ▐░▌       ▐░▌▐░▌               ▐░▌     
22//  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     ▐░▌      ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
23// ▐░░░░░░░░░░░▌     ▐░▌     ▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
24//  ▀▀▀▀▀▀▀▀▀▀▀       ▀       ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀      
25                                                                              
26/// **Top-level struct containing all information read from the markup file**
27/// 
28/// All fields are private, but are accessible with "get" functions, e.g. Process.get_number() returns a reference to the Process's number (&String).
29pub struct Process {
30
31	/// Path to the EBML file for this process
32	process_file: String,
33
34	/// Document Number, in whatever format makes sense for the organization's document control system
35	number: String,
36
37	/// A stack of Revision lines, with an important assumption that the first Revision line represents the current Rev of the document, and the last Revision line represents the first released Rev of the document
38	/// * First String: "Rev" text, e.g. "A" for a document's Revision A
39	/// * Second String: "What changed?" text, e.g. "New section added to initialize the system prior to start"
40	all_revisions: Vec<(String,String)>,
41	
42	/// Human-readable Title of the process document, hopefully with enough meaning for the team to understand the purpose of the process
43	title: String,
44
45	/// Human-readable category of process document, such as "Test Procedure" or "Work Instruction" or "Maintenance Procedure" etc.
46	process_type: String,
47
48	/// Human that composed the markup file in its current Revision, i.e. who currently owns the knowledge in this process
49	author: String,
50
51	/// Human responsible for being the second set of eyes for this Revision, and who is the gatekeeper to publishing this Revision prior to its use in the field
52	reviewer: String,
53
54	/// The thing that this process applies to, i.e. the primary input into the Process, be it tangible or abstract
55	subject: String,
56
57	/// File name for an image of the Subject
58	subject_image: String,
59
60	/// If this process changes the *Subject* into something else, then that something else is the *Product*
61	product: String,
62
63	/// File name for an image of the Product
64	product_image: String,
65
66	/// Compiled list of Objective statements found throughout the document when they are achieved
67	all_objectives: Vec<(String,String)>,
68
69	/// Compiled list of Out-of-Scope statements found throughout the document when they are achieved
70	all_out_of_scopes: Vec<(String,String)>,
71
72	/// Ordered set of all Step-filled sections of the document
73	all_sections: Vec<Section>,
74
75	/// Cummulative list of all Resources identified in all Steps
76	all_resources: Vec<(Resource,String)>,
77
78	/// Unique list of all resources that need calibration to be used in this process
79	all_calibrated_resources: Vec<Resource>,
80
81	/// Cummulative list of all Verifications of Requirements performed in all Steps
82	all_verifications: Vec<(Requirement,String)>,
83
84	/// File names for custom templates (CSS) to use that augment the vanilla template
85	/// * Company-specific
86	/// * Project-specific
87	/// * Security environment-specific
88	/// * etc.
89	all_templates: Vec<String>,
90
91	/// Entire source EBML file for this Process, stored unprocessed in a Vec of Strings (one per line of text)
92	full_source: Vec<String>,
93}
94
95/// Each Section struct contains all Steps within that document section
96pub struct Section {
97
98	/// What the section is named, hopefully descriptive enough that operators know what to expect, and can anticipate operations
99	title: String,
100
101	/// Ordered set of all Steps in the section (each Step is rich with Sub-Step information as well)
102	all_steps: Vec<Step>,
103
104	/// Relative path from the Process folder to the Process folder where the Section's source exists, empty string unless SECREF is fetched
105	relative_path: String,
106}
107
108/// Each Step struct contains all Actions, Images, Commands, Resources, Verifications, and Warnings contained in that step
109pub struct Step {
110
111	/// Text of the step, analogous to the Title of a Section except at the Step-level
112	text: String,
113
114	/// Ordered set of Resources identified in the Step, wherever they are mentioned
115	resources: Vec<Resource>,
116
117	/// Ordered set of SubSteps identified, be them sets of Action lines, Commands, Warnings, or Verification events
118	/// 
119	/// *Note: Resources are not included as SubSteps themselves, although other lines within a Step are*
120	all_sub_steps: Vec<SubStep>,
121}
122
123/// Each Action struct is a single operation to perform, with an expected result, and an optional Two-Party Verification flag
124pub struct Action {
125
126	/// What action the operator is to perform
127	perform: String,
128
129	/// What operator is to expect after performing the action
130	expect: String,
131
132	/// Is two party verification (TPV) required for this step?
133	tpv: bool,
134}
135
136/// Each Requirement struct represents a formal "shall" statement whose evidence of verification is produced in a Step of a Process
137pub struct Requirement {
138
139	/// Unique ID for the requirement
140	id: String,
141
142	/// Full text of the requirement, which includes the word "shall."
143	/// 
144	/// Example of *good requirement* wording:
145	/// 
146	/// (When \[SUB/SYSTEM or VARIABLE\] is \[MODE/STATE or VALUE or RANGE\],) \[AGENT\] shall \[BEHAVE\] (within \[TOLERANCE\]). (Note: [CLARIFYING NOTES OR REFERENCES])
147	text: String,
148
149	/// Method of verification--how is this requirement to be verified? This is important for processes, because steps to produce evidence of verification should be steps that somehow resemble this verificaiton method.
150	method: VerificationMethod,
151}
152
153/// Resoures are named things that are needed to perform a Step - without them the Step can't be performed
154pub struct Resource { 
155
156	/// Descriptive name of the resource, with enough information to be unambiguous to the operator. For instance, if a specific screwdriver is needed, then "Screwdriver" is probably insufficient as the name of this resource.
157	name: String,
158
159	/// Does this resource need to be calibrated to be used in this process?
160	calibration: bool,
161}
162
163/// A flexible table of arbitrary number of rows and columns, with a caption to describe the table within the process
164pub struct Table {
165
166	/// Descriptive string to display alongside the table
167	caption: String,
168
169	/// The complete array of table data, with the Vec<Vec<>> representing Row<Col<>>
170	/// 
171	/// Example: to access the entire header (first) row of the table
172	/// > array\[0\]
173	/// 
174	/// Example: to access the 8th column of the 10th row of the table
175	/// > array\[9\]\[7\]
176	/// 
177	/// All data in the table are stored as strings, as the ultimate destination for these data are in a formatted process document's HTML text. These data are not intended to be numerically manipulated from this Table structure, but rather the assumption is that all of the numerical processing is already complete when these data are read into this structure.
178	array: Vec<Vec<String>>,
179
180}
181
182//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄        ▄  ▄         ▄  ▄▄       ▄▄ 
183// ▐░░░░░░░░░░░▌▐░░▌      ▐░▌▐░▌       ▐░▌▐░░▌     ▐░░▌
184// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌░▌     ▐░▌▐░▌       ▐░▌▐░▌░▌   ▐░▐░▌
185// ▐░▌          ▐░▌▐░▌    ▐░▌▐░▌       ▐░▌▐░▌▐░▌ ▐░▌▐░▌
186// ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌   ▐░▌▐░▌       ▐░▌▐░▌ ▐░▐░▌ ▐░▌
187// ▐░░░░░░░░░░░▌▐░▌  ▐░▌  ▐░▌▐░▌       ▐░▌▐░▌  ▐░▌  ▐░▌
188// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌   ▐░▌ ▐░▌▐░▌       ▐░▌▐░▌   ▀   ▐░▌
189// ▐░▌          ▐░▌    ▐░▌▐░▌▐░▌       ▐░▌▐░▌       ▐░▌
190// ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌     ▐░▐░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌       ▐░▌
191// ▐░░░░░░░░░░░▌▐░▌      ▐░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌
192//  ▀▀▀▀▀▀▀▀▀▀▀  ▀        ▀▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀ 
193
194/// Each row within a step contributes to a SubStep. Some SubStep types (e.g. ActionSequence) contain information from multiple consecutive rows, whereas others (all others?) contain information from a single markup file row.
195pub enum SubStep {
196
197	/// Each objective of the process achieved in this step has a single SubStep of type Objective, simply passing through the string found
198	Objective(String),
199
200	/// Each out-of-scope declaration tells what this procedure **doesn't** do, and should tell where in the process library such scope can be found
201	OutOfScope(String),
202
203	/// Each element of the ActionSequence vector is the information from a single Action row. Consecutive Action rows in a markup file are bundled into the ActionSequence type of SubStep.
204	ActionSequence(Vec<Action>),
205
206	/// Each command row has a String that is verbatim what the operator should type (or paste) into a computer system
207	Command(String),
208
209	/// Each Image row has a file name of an image to display on screen (first tuple String) and the caption text to display under the image (second tuple String)
210	Image(String,String),
211
212	/// Warning text is something the author wants to force the operator to read prior to proceeding
213	Warning(String),
214
215	/// Each Verification row includes sufficient information to form a Verification struct
216	Verification(Requirement),
217
218	/// Each Resource row has a String describing a single resource needed to perform the current step, plus a bool field to indicate if it's calibrated equipment
219	Resource(Resource),
220
221	/// Vanilla text blocks, to be used sparingly lest the process document get too wordy
222	Context(String),
223
224	/// Arbitrary-sized table from comma-separated data
225	Table(Table),
226}
227
228/// Verification methods include one-of-a-kind systems as well as high-volume production (e.g. Sampling)
229pub enum VerificationMethod {
230
231	/// *From NASA:* **Demonstration:** Showing that the use of an end product achieves the individual specified requirement. It is generally a basic confirmation of performance capability, differentiated from testing by the lack of detailed data gathering. Demonstrations can involve the use of physical models or mock-ups; for example, a requirement that all controls shall be reachable by the pilot could be verified by having a pilot perform flight-related tasks in a cockpit mock-up or simulator. A demonstration could also be the actual operation of the end product by highly qualified personnel, such as test pilots, who perform a one-time event that demonstrates a capability to operate at extreme limits of system performance, an operation not normally expected from a representative operational pilot.
232	Demonstration,
233
234	/// *From NASA:* **Inspection:** The visual examination of a realized end product. Inspection is generally used to verify physical design features or specific manufacturer identification. For example, if there is a requirement that the safety arming pin has a red flag with the words “Remove Before Flight” stenciled on the flag in black letters, a visual inspection of the arming pin flag can be used to determine if this requirement was met. Inspection can include inspection of drawings, documents, or other records. 
235	Inspection,
236
237	/// *From NASA:* **Analysis:** The use of mathematical modeling and analytical techniques to predict the suitability of a design to stakeholder expectations based on calculated data or data derived from lower system structure end product verifications. Analysis is generally used when a prototype; engineering model; or fabricated, assembled, and integrated product is not available. Analysis includes the use of modeling and simulation as analytical tools. A model is a mathematical representation of reality. A simulation is the manipulation of a model. Analysis can include verification by similarity of a heritage product.
238	Analysis,
239
240	/// *From NASA:* **Test:** The use of an end product to obtain detailed data needed to verify performance or provide sufficient information to verify performance through further analysis. Testing can be conducted on final end products, breadboards, brassboards, or prototypes. Testing produces data at discrete points for each specified requirement under controlled conditions and is the most resource-intensive verification technique. As the saying goes, “Test as you fly, and fly as you test.” (See Section 5.3.2.5 in the NASA Expanded Guidance for Systems Engineering at [<https://nen.nasa.gov/web/se/doc-repository>])
241	Test,
242
243	/// From SEBoK: Technique based on verification of characteristics using samples. The number, tolerance, and other characteristics must be specified to be in agreement with the experience feedback.
244	/// 
245	/// [<https://sebokwiki.org/wiki/System_Verification#Methods_and_Techniques>]
246	Sampling,
247
248}
249
250//  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄        ▄  ▄▄▄▄▄▄▄▄▄▄▄ 
251// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░▌      ▐░▌▐░░░░░░░░░░░▌
252// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░▌░▌     ▐░▌▐░█▀▀▀▀▀▀▀▀▀ 
253// ▐░▌          ▐░▌       ▐░▌▐░▌▐░▌    ▐░▌▐░▌          
254// ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌       ▐░▌▐░▌ ▐░▌   ▐░▌▐░▌          
255// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌  ▐░▌  ▐░▌▐░▌          
256// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░▌   ▐░▌ ▐░▌▐░▌          
257// ▐░▌          ▐░▌       ▐░▌▐░▌    ▐░▌▐░▌▐░▌          
258// ▐░▌          ▐░█▄▄▄▄▄▄▄█░▌▐░▌     ▐░▐░▌▐░█▄▄▄▄▄▄▄▄▄ 
259// ▐░▌          ▐░░░░░░░░░░░▌▐░▌      ▐░░▌▐░░░░░░░░░░░▌
260//  ▀            ▀▀▀▀▀▀▀▀▀▀▀  ▀        ▀▀  ▀▀▀▀▀▀▀▀▀▀▀ 
261                                                    
262/// Read and interpret an entire Easy Button Markup Language (EBML) file, and return a "Process" struct.
263/// 
264/// A single Process struct holds all found information about a process, and is itself enough to extract it and produce complex documents.
265pub fn read_ebml(file_name:&String) -> Process {
266
267	// empty string input (instead of the name of a file to read) returns a blank Process with no further processing
268	if file_name==&String::from("") {
269		return Process::new();
270	}
271
272	/*
273	// function to return all the lines in a file back as a vec of strings
274	fn lines_from_file(filename: impl AsRef<Path>) -> io::Result<Vec<String>> {
275		BufReader::new(File::open(filename)?).lines().collect()
276	}
277	*/
278
279	// Instantiate a blank Process to be filled with found information
280	let mut new_proc = Process::new();
281
282	// Use the function to pull all the lines out of the markup file
283	let lines = lines_from_file(file_name).expect("Could not load lines");
284	new_proc.set_process_file(&file_name);
285	new_proc.set_full_source(lines.clone());
286
287	// initialize a couple of counters that become the basis of Section numbering and Step numbering below
288	let mut sec_cnt = 0;
289    let mut stp_cnt = 0;
290
291    // Now loop through all the lines of the input file
292    for (ii,line) in lines.iter().enumerate() {
293
294    	// if it's a comment line, then skip this instance of the loop
295    	if line.trim().find("//") == Some(0) { continue }
296
297    	// delimit the line by the bar/pipe character
298    	let all_parts: Vec<&str> = line.split('|').collect();
299
300    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
301    	let first_part: &str = all_parts[0];
302
303    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
304    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
305
306    	// "remainder" is everything to the right of the first bar, and a special case is handled here for a blank string (nothing) when there isn't anything to the right of the first bar
307    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
308    	
309    	// Here's where the actual processing goes. At the PROCESS level, we're looking for process metadata (author, revision, etc.)
310    	// When we hit a SECTION it becomes a little different: we increment the counter and we extract all the info we can from that Section
311    	// We don't actually process "Step" here, nor any of the line types that comprise Steps/SubSteps, but we still increment the Step counter
312    	// For simple line types, we just pass the entire "remainder" into the struct field
313    	// For more complex line types, specialized functions take over and perform further processing of the remainder (and possibly the Section/Step counters)
314        match trim_whitespace_make_uppercase(first_part).as_str() {
315			"TEMPLATE" | "CSS" => new_proc.add_template(remainder),
316			"NUMBER"	 	=> new_proc.set_number(remainder),
317			"TITLE"		 	=> new_proc.set_title(remainder),
318			"PROCESSTYPE"	=> new_proc.set_process_type(remainder),
319			"AUTHOR"	 	=> new_proc.set_author(remainder),
320			"REVIEWER"		=> new_proc.set_reviewer(remainder),
321			"REVISION" | "REV" => new_proc.add_revision(remainder),
322			"SUBJECT" 		=> new_proc.set_subject(remainder),
323			"SUBJECTIMAGE"	=> new_proc.set_subject_image(remainder),
324			"PRODUCT" 		=> new_proc.set_product(remainder),
325			"PRODUCTIMAGE"	=> new_proc.set_product_image(remainder),
326
327			"SECTION" | "SEC" => {
328				new_proc.add_section(extract_next_section(&lines[ii..lines.len()],&file_name));
329				sec_cnt += 1;
330				stp_cnt = 0;
331			},
332			"SECTIONREFERENCE" | "SECREF" => {
333				let (fetched_section,success) = fetch_section_reference(remainder);
334				if success {
335					new_proc.add_section(fetched_section);
336					sec_cnt += 1;
337					stp_cnt = 0;
338				}
339			},
340			"STEP" | "STP" => {
341				stp_cnt += 1;
342			}
343
344			"ACTION" | "DO"	=> (), //println!("Valid \"{}\", skipping...",&line[0..index]),
345			"COMMAND" | "CMD" | ">" | "%" | "$" | "#"  => (), //println!("Valid \"{}\", skipping...",&line[0..index]),
346			"CONTEXT" | "COMMENT" | "TXT" | "CMT" => (), 
347			"IMAGE" | "IMG" | "PICTURE" | "PIC" | "FIGURE" | "FIG" => (), //println!("Valid \"{}\", skipping...",&line[0..index]),
348			"WARNING" | "WARN" | "WAR" | "WRN" | "ALERT" | "!" => (), //println!("Valid \"{}\", skipping...",&line[0..index]),
349			"CSVSTART"		=> (),
350			"CSVEND"		=> (),
351			"CSVFILE"		=> (),
352
353			"VERIFICATION"| "VER" | "REQUIREMENT" | "REQ" => {
354				new_proc.add_verification(parse_verification_line(remainder),"Step ".to_string()+&sec_cnt.to_string()+"."+&stp_cnt.to_string());
355			},
356			"RESOURCE" | "RES" => {
357				new_proc.add_resource(parse_resource_line(remainder),"Step ".to_string()+&sec_cnt.to_string()+"."+&stp_cnt.to_string());
358				new_proc.add_calibrated_resource(parse_resource_line(remainder));
359			},
360			"OBJECTIVE"	| "OBJ" => {
361				new_proc.add_objective(remainder.to_string(),"Step ".to_string()+&sec_cnt.to_string()+"."+&stp_cnt.to_string());
362			},
363			"OUTOFSCOPE" | "OOS" => {
364				new_proc.add_out_of_scope(remainder.to_string(),"Step ".to_string()+&sec_cnt.to_string()+"."+&stp_cnt.to_string());
365			},
366			""	=> (),
367    		_ 	=> (),
368        }
369
370    }
371
372    // Now that the Process information is extracted and placed into the struct, it's returned
373    new_proc
374
375}
376
377/// Robustify string (slice) matching by trimming out all of the whitespace
378/// 
379/// Use it like this:
380/// 
381/// match **trim_whitespace_make_uppercase**(slice_to_compare)**.as_str()** {<br/>
382/// 	"THING" => do_thing,<br/>
383/// 	""		=> (),<br/>
384/// 	_ 		=> (),<br/>
385/// }
386fn trim_whitespace_make_uppercase(s: &str) -> String { s.split_whitespace().collect::<Vec<_>>().join("").to_uppercase() }
387
388/// Examine the remainder of lines in a file, and return the next Section
389/// A Section is composed of a series of steps, so this is but one layer in that hierarchy
390fn extract_next_section(some_lines:&[String],ebml_file:&str) -> Section {
391
392	// Create a new, blank Section struct to fill with process information (Steps)
393	let mut new_section:Section = Section::new();
394	
395	// We are scanning through lines looking for content within this section. Basically:
396	//
397	// Section|This Section
398	// Step|First step in this section
399	// ...|...
400	// Section|Start of the NEXT Section--the line above this should be the last
401	//
402	// The hackey way we're doing this is with a counter, and after the counter gets to 1 (first Section) we're ready to terminate at the next one
403	let mut section_count = 0;
404	
405	// Loop to break when we have more than one section, called 'one_section
406	'one_section: for (ii,line) in some_lines.iter().enumerate() {
407
408		// delimit the line by the bar/pipe character
409    	let all_parts: Vec<&str> = line.split('|').collect();
410    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
411    	let first_part: &str = all_parts[0];
412    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
413    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
414    	// "remainder" is everything to the right of the first bar, and a special case is handled here for a blank string (nothing) when there isn't anything to the right of the first bar
415    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
416
417    	match trim_whitespace_make_uppercase(first_part).as_str() {
418    		// First encounter: name this Section with the content
419			// Second encounter: stop!
420			"SECTION" | "SEC" => { 
421				section_count +=1;
422				if section_count > 1 { break 'one_section };
423				new_section.set_title(remainder);
424				},
425			"STEP" | "STP" => new_section.add_step(extract_next_step(&some_lines[ii..some_lines.len()],ebml_file)),
426			// Conscious decision to ignore anything that isn't within a step (can change in the future?)
427			// This logic also ignores comment lines, as the comment characters would polute the "Section" or "Step" text left of the bar/pipe
428			_ => (),
429		};
430	}
431	
432	// Return the completed Section
433	new_section
434
435}
436
437/// The Process contains a Section Reference, and this function carefully traverses the Library with best-effort to fetch it.
438/// 
439/// The sequence looks like this:
440/// - Use globby finding to match folders to whatever the Process reference is
441/// - If there is a match, read the file in a more careful way that [read_ebml] (don't follow further Section References)
442/// - If the reference is to another reference, follow that reference to the next file, carefully that is...
443/// - Limit the length of the daisy-chain to an arbitrary value
444/// - If you successfully find the source "Section" that isn't itself a "Section Reference", then read that Section and return it
445/// - Throw a flag to indicate success or failure
446fn fetch_section_reference(remainder:&str) -> (Section,bool) {
447
448	let max_layers:u8 = 4;
449	let mut ref_layer:u8 = 0;
450	let mut new_section = Section::new();
451	let mut success:bool = false;
452	let (first_stop, section_title) = parse_section_reference(remainder);
453
454	let mut code:u8;
455
456	// println!("'first_stop' --> [{}]",&first_stop);
457	let mut next_stop = first_stop;
458
459	while ref_layer < max_layers {
460		ref_layer += 1;
461
462		// println!("'ref_layer' --> [{}]",ref_layer);
463		// println!("Inspecting [{}] for [{}] ... ",&next_stop,&section_title);
464				
465		(code,next_stop) = inspect_ebml_for_reference(&next_stop,&section_title);
466		
467		// println!("Code [{}] and 'next_stop' of [{}]",code,next_stop);
468				
469		match  code {
470			0 => {
471				new_section = take_section(&next_stop,&section_title);
472				success = true;
473
474				let folder_list = globify_document_number(&next_stop.to_string());
475				if folder_list.len() > 0 {
476					let rel_path = "../".to_owned() + &folder_list[0] + "/";
477					// println!("Setting this section's relative_path field to [{}]",&rel_path);
478					new_section.set_relative_path(&rel_path);
479					// println!("Proof that it was done: get_relative_path() -> [{}]",new_section.get_relative_path());
480				}
481				break;
482			},
483			1 => {
484				// do nothing in this case, because the loop will handle the next layer
485			},
486			2 => {
487				new_section.set_title("ERR[Section Doesn't Exist in File]");
488				success = false;
489			},
490			_ => {
491				new_section.set_title("ERR[File Not Found]");
492				success = false;
493			}
494		}
495		if ref_layer == max_layers {
496			new_section.set_title("ERR[Max Reference Depth Exceeded]");
497			success = false;
498		}
499
500		// println!("");
501	} 
502	
503	return (new_section,success);
504}
505
506/// For the "Section Reference" line type, there are two fields
507/// 
508/// - First field: external Process to reference
509/// - Second field: Section 'title' to reference in that external file
510/// 
511/// This function expects 2 fields, but handles all cases.
512fn parse_section_reference(remainder:&str) -> (String,String) {
513	let all_parts: Vec<&str> = remainder.split('|').collect();
514	match all_parts.len() {
515		0 => ("ERROR: NO FILE FOUND".to_string(),"ERROR: NO SECTION TITLE FOUND".to_string()),
516		1 => (all_parts[0].to_string().trim().to_string(),"ERROR: NO SECTION TITLE FOUND".to_string()),
517		_ => (all_parts[0].to_string().trim().to_string(),all_parts[1].to_string().trim().to_string()),
518	}
519}
520
521/// Inspect an external Process file to understand a specific Section Reference
522/// 
523/// This function returns a numeric code based on what it finds:
524/// - 0 if section exists and is not a reference,
525/// - 1 if section exists and is a reference,
526/// - 2 if section doesn't exist in the file
527/// - 3 if the file doesn't exist or can't be found
528/// 
529/// It also returns a String indicating what process to look into to find the referenced section. This is only necessary because the "daisy chaining" of references is in fact allowed. So for code "1" the returned String will be the *next* place to look for the source material.
530fn inspect_ebml_for_reference(next_stop:&str,section_title:&str) -> (u8,String) {
531	
532	let folder_list = globify_document_number(&next_stop.to_string());
533
534	if folder_list.len() > 0 {
535
536		let file_to_read = "./".to_owned() + &folder_list[0] + "/" + &folder_list[0] + ".ebml";
537
538		// println!("from 'inspect_ebml_for_reference' --> [{}]",&file_to_read);
539		
540		let lines = lines_from_file(file_to_read).expect("Could not load lines from Section Reference file");
541
542		for line in lines {
543	    	if line.trim().find("//") == Some(0) { continue }
544	    	let all_parts: Vec<&str> = line.split('|').collect();
545	    	let first_part: &str = all_parts[0];
546	    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
547	    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
548	    	
549	    	match trim_whitespace_make_uppercase(first_part).as_str() {
550				"SECTION" | "SEC" => {
551					if trim_whitespace_make_uppercase(remainder) == trim_whitespace_make_uppercase(section_title) {
552						// We found a valid reference, and it's the source we're looking for!
553						return (0,next_stop.to_string());
554					}
555				},
556				"SECTIONREFERENCE" | "SECREF" => {
557					let (next_stop_candidate,section_title_candidate) = parse_section_reference(remainder);
558					if trim_whitespace_make_uppercase(&section_title_candidate) == trim_whitespace_make_uppercase(section_title) {
559						// We found a valid reference, and it's also a reference...
560						return (1,next_stop_candidate.to_string());
561					}
562				},
563				_ 	=> (),
564	        }
565		}
566
567		// We didn't find the Section title
568		return (2,"Section not found in reference file".to_string());
569
570	// If you got this far, the folder list must be empty, e.g. folder_list.len() = 0
571	} else {
572		// The Process wasn't found
573		return (3,"No Folder".to_string());
574	}
575}
576
577/// Open a text file and return its lines as a Vec of String... well, wrapped in some Rust-y Result thing
578fn lines_from_file(filename: impl AsRef<Path> + std::fmt::Debug) -> io::Result<Vec<String>> {
579	// println!("from 'lines_from_file' --> [{:?}]\n",filename);
580	BufReader::new(File::open(filename)?).lines().collect()
581}
582
583/// Attempt to find and return a single Section from a Process, *without* invoking [read_ebml]
584/// 
585/// Why avoid [read_ebml]? If a library is full of "Section Reference" lines, then there are certain cases that could branch infinitely if read_ebml tries to expand every Section Reference found. If *this* function did what [read_ebml] does, then it could spawn itself ad infinitum, and possibly recursively, and possibly circularly.
586/// 
587/// Therefore, we'll mimic what read_ebml does in this function, but *only* act on a single "Section" line (ignore "Section Reference" !!), being the first one found that matches the title we're looking for.
588fn take_section(next_stop:&str,section_title:&str) -> Section {
589
590	let folder_list = globify_document_number(&next_stop.to_string());
591
592	let file_to_read = "./".to_owned() + &folder_list[0] + "/" + &folder_list[0] + ".ebml";
593	
594	// println!("Taking a Section from --> [{}]",&file_to_read);
595	
596	let lines = lines_from_file(file_to_read).expect("Could not load lines from Section Reference file");
597
598		for (ii,line) in lines.iter().enumerate() {
599			if line.trim().find("//") == Some(0) { continue }
600	    	let all_parts: Vec<&str> = line.split('|').collect();
601	    	let first_part: &str = all_parts[0];
602	    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
603	    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
604	    	
605	    	match trim_whitespace_make_uppercase(first_part).as_str() {
606				"SECTION" | "SEC" => {
607					// Only act on the first Section whose title matches the one we're looking for
608					if trim_whitespace_make_uppercase(remainder) == trim_whitespace_make_uppercase(section_title) {
609
610						// build section like normal, just how read_ebml does it
611						let ebml_file_proxy = "./".to_owned() + &folder_list[0].to_string() + "/proxy_file";
612						return extract_next_section(&lines[ii..lines.len()],&ebml_file_proxy);
613					}
614				},
615				// don't act on any other line type
616				_ => (),
617			}
618		}
619
620	// Fallback, though this should not ever occur.
621	// If you get to 'take_section' then you have already confirmed that the Section you want is in this file.
622	// You *should* be able to read the file and get the section...
623	let mut new_section = Section::new();
624	new_section.set_title("ERR[Unable to Reference Section, Cause Unknown]");
625	return new_section; // should never happen...
626}
627
628/// When a "Step" line is encountered within a Section, this function performs an analogous extraction to "extract_next_section" but adapted for SubStep mining.
629fn extract_next_step(few_lines:&[String],ebml_file:&str) -> Step {
630
631	// Initialize the output, which is a single Step struct
632	// The remainder of this routine will be filling this struct with the lines between the first Step and the next Step
633	let mut new_step:Step = Step::new();
634	
635	// In order to know where to start and stop, initialize a counter for the Step
636	let mut step_count = 0;
637
638	// Consecutive Action rows are grouped into ActionSequence items, so this flag answers, "is the Action line I'm reading a FIRST action line?"
639	// Every non-action line resets this to TRUE
640	// When an Action line is encountered this is set to FALSE
641	let mut allow_action = true;
642
643	// Similar trick for "CSV Start"
644	let mut allow_csv_start = true;
645	
646	// Named loop 'one_step is so named because when we find the second instance of Step then we're done
647	'one_step: for (ii,line) in few_lines.iter().enumerate() {
648
649		// delimit the line by the bar/pipe character
650    	let all_parts: Vec<&str> = line.split('|').collect();
651    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
652    	let first_part: &str = all_parts[0];
653    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
654    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
655    	// "remainder" is everything to the right of the first bar, and a special case is handled here for a blank string (nothing) when there isn't anything to the right of the first bar
656    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
657
658    	match trim_whitespace_make_uppercase(first_part).as_str() {
659			// First encounter: name this step with the content
660			// Second encounter: stop!
661			"STEP" | "STP" => { 
662				step_count +=1;
663				if step_count > 1 { break 'one_step };
664				new_step.set_text(remainder);
665				allow_action = true;
666				},
667			// If the previous row was NOT an Action row, then start an ActionSequence fetch, which is analogous to this function
668			// Otherwise, skip the Action row because it was already processed by the function that produces ActionSequence objects
669			"ACTION" | "DO" => {
670				if allow_action {
671					new_step.add_sub_step(extract_next_action_sequence(&few_lines[ii..few_lines.len()]));
672					allow_action = false;
673				} else {()};
674				},
675			// Commands are simple: assume the entire text is the verbatim text to display as a command
676			"COMMAND" | "CMD" | ">" | "%" | "$" | "#" => {
677				new_step.add_sub_step(SubStep::Command(remainder.to_string()));
678				allow_action = true;
679				},
680			// Image lines go to a subroutine that finds the image file name as well as the caption
681			"IMAGE" | "IMG" | "PICTURE" | "PIC" | "FIGURE" | "FIG" => {
682				new_step.add_sub_step(parse_image_line(remainder));
683				allow_action = true;
684				},
685			// Warning lines are simple like Command lines: just use all the text as the Warning text
686			"WARNING" | "WARN" | "WAR" | "WRN" | "ALERT" | "!" => {
687				new_step.add_sub_step(SubStep::Warning(remainder.to_string()));
688				allow_action = true;
689				},
690			// Verification lines are multi-part, so a subroutine is needed to make sense of the information
691			"VERIFICATION"| "VER" | "REQUIREMENT" | "REQ" => {
692				new_step.add_sub_step(SubStep::Verification(parse_verification_line(remainder)));
693				allow_action = true;
694				},
695			// Resource is another simple line: all the text is the Resource name text
696			// However, at the Step level there's a field that pulls in all resoureces from all steps, so we need to push that, too.
697			"RESOURCE" | "RES" => {
698				new_step.add_sub_step(SubStep::Resource(parse_resource_line(remainder)));
699				// the line below is what pushes this found Resouce line up to the Step level field, as well as adding the SubStep
700				new_step.add_resource(parse_resource_line(remainder));
701				allow_action = true;
702				},
703			// Objective is another simple line of text
704			"OBJECTIVE"	| "OBJ" => {
705				new_step.add_sub_step(SubStep::Objective(remainder.to_string()));
706				allow_action = true;
707				},
708			// "Out of Scope" is another simple line of text
709			"OUTOFSCOPE" | "OOS" => {
710				new_step.add_sub_step(SubStep::OutOfScope(remainder.to_string()));
711				allow_action = true;
712				},
713			// Context is another simple line of text
714			"CONTEXT" | "COMMENT" | "TXT" | "CMT" => {
715				new_step.add_sub_step(SubStep::Context(remainder.to_string()));
716				allow_action = true;
717				},
718			// Opening of a block of comma-separated values embedded in the EBML file to display as a table
719			"CSVSTART" => {
720				if allow_csv_start {
721					new_step.add_sub_step(SubStep::Table(extract_embedded_csv(&few_lines[ii..few_lines.len()])));
722					allow_csv_start = false;
723				} else {()};
724				},
725			// Closing of a block of comma-separated values embedded in the EBML file
726			"CSVEND" => allow_csv_start = true,
727			// One-line call to reference an external file that contains comma-separated values to display as a table
728			"CSVFILE" => new_step.add_sub_step(SubStep::Table(extract_external_csv(remainder,ebml_file))),
729			// If there's any other kind of line, skip
730			_ => (), 
731		};
732	}
733	
734	// Return the filled Step struct
735	new_step
736
737}
738
739/// When an "Action" line is encountered within a Step, we want to group it with all consecutive Action lines that follow
740/// 
741/// The result is a type of SubStep called an ActionSequence
742/// 
743/// Action Sequences can be a single Action, or many actions
744fn extract_next_action_sequence(couple_lines:&[String]) -> SubStep {
745
746	// Initialize the vector of Actions that make up the ActionSequence type of SubStep
747	let mut new_action_sequence:Vec<Action> = vec![];
748	
749	// Named loop 'consecutive_actions so named because the first non-Action will break the loop
750	'consecutive_actions: for line in couple_lines.iter() {
751
752		// delimit the line by the bar/pipe character
753    	let all_parts: Vec<&str> = line.split('|').collect();
754    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
755    	let first_part: &str = all_parts[0];
756    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
757    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
758    	// "remainder" is everything to the right of the first bar, and a special case is handled here for a blank string (nothing) when there isn't anything to the right of the first bar
759    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
760
761		match trim_whitespace_make_uppercase(first_part).as_str() {    	
762			"ACTION" | "DO" => new_action_sequence.push(parse_action_line(remainder)),
763			_ => break 'consecutive_actions,
764		};
765	}
766	
767	// Return the SubStep enum of type ActionSequence, with the Vec<Action> as the data
768	SubStep::ActionSequence(new_action_sequence)
769
770}
771
772/// When "CSV Start" line is encountered, this function reads this first line all the way through the "CSV End" line that terminates that CSV block.
773/// 
774/// Additional information is contained within the "CSV Start" line itself in EBML bar delimiting syntax.
775/// 
776/// All lines between "CSV Start" and "CSV End" are assumed to be actual comma-separated value data rows, with the first row serving as a header row.
777fn extract_embedded_csv(couple_lines:&[String]) -> Table {
778	
779	// Initialize the output
780	let mut new_table = Table::new();
781
782	// This flag is to fix an issue found in testing. Yay testing!
783	let mut already_started = false;
784
785	// Named loop 'csv_lines
786	'csv_lines: for line in couple_lines.iter() {
787
788		// delimit the line by the bar/pipe character
789    	let all_parts: Vec<&str> = line.split('|').collect();
790    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
791    	let first_part: &str = all_parts[0];
792    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
793    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
794    	// "remainder" is everything to the right of the first bar, and a special case is handled here for a blank string (nothing) when there isn't anything to the right of the first bar
795    	let _remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
796
797		match (trim_whitespace_make_uppercase(first_part).as_str(),already_started) {    	
798			("CSVSTART",false) => {
799				match all_parts.len() {
800					2 => new_table.set_caption(all_parts[1]),
801					1 => (),
802					_ => new_table.set_caption(all_parts[1]),
803				}
804			},
805			("CSVEND",true) => break 'csv_lines,
806			_ => {
807				let csv_parts_str:Vec<&str> = line.split(',').collect();
808				let mut new_row:Vec<String> = vec![];
809				for ii in 0..csv_parts_str.len() {
810					new_row.push(csv_parts_str[ii].to_string());
811				}
812				new_table.add_row(new_row);
813			},
814		};
815		already_started = true;
816	}
817
818	// Return the Table struct
819	new_table
820}
821
822/// When a single "CSV File" line is encountered, this function reads the informational parts, parsed with the pipe character, particularly the external file to reference and read in.
823/// 
824/// This function returns a Table object, and gets the "array" information from the referenced external file, and the "caption" information from the piped-through text in the single EBML line.
825/// 
826/// Critical assumption: the external CSV file is in the same folder as the EBML file that references it. This path is passed in as "ebml_file" and parsed within the function as such.
827fn extract_external_csv(line:&str,ebml_file:&str) -> Table {
828	
829	/// Single-use private function within a private function to return the "array" information from a CSV file reference
830	fn open_csv_file_and_return_data_array(file_name:&str) -> Vec<Vec<String>> {
831
832		// empty string input (instead of the name of a file to read) returns a blank Process with no further processing
833		if file_name==&String::from("") {
834			return vec![];
835		}
836
837		match std::fs::exists(&file_name) {
838			Ok(true) => (),
839			_ => return vec![],
840		}
841
842		// Use the function to pull all the lines out of the markup file
843		let lines = lines_from_file(file_name).expect("Could not load lines");
844
845		let mut data_array:Vec<Vec<String>> = vec![];
846		for line in lines {
847			let mut new_csv_data_line:Vec<String> = vec![];
848			let all_parts: Vec<&str> = line.split(',').collect();
849			for part in all_parts {
850				new_csv_data_line.push(part.trim_start().trim_end().to_string());
851			}
852			data_array.push(new_csv_data_line);
853		}
854		data_array
855	}
856
857	// Initialize the output
858	let mut new_table = Table::new();
859	let all_parts: Vec<&str> = line.split('|').collect();
860
861	// TODO: robustify the lines below, because it's built on an unstable house of cards
862	// println!("'ebml_file' --> [{}]",&ebml_file);
863	let path_parts = ebml_file.split('/').collect::<Vec<_>>();
864	// println!("'path_parts' --> [{:?}]",&path_parts);
865
866	let csv_file_to_open = path_parts[0].to_owned() + "/" + path_parts[1] + "/" + all_parts[0].trim_start().trim_end();
867	// println!("'csv_file_to_open' --> [{}]",csv_file_to_open);
868	
869	// One 'part' in the remainder means there's only a file reference in the CSV File line, e.g. "CSV File | file.csv"
870	// Two 'parts' in the remainder means there's also a caption, e.g. "CSV File | file.csv | Caption text"
871	// There can't be ZERO parts, and there isn't a definition of what third (or more) parts would be (yet), so treat the generic case the same as the "Two" case.
872	match all_parts.len() {
873		1 => {
874			for row in open_csv_file_and_return_data_array(&csv_file_to_open) { new_table.add_row(row) };
875		},
876		2 => {
877			for row in open_csv_file_and_return_data_array(&csv_file_to_open) { new_table.add_row(row) };
878			new_table.set_caption(all_parts[1]);
879		},
880		_ => {
881			for row in open_csv_file_and_return_data_array(&csv_file_to_open) { new_table.add_row(row) };
882			new_table.set_caption(all_parts[1]);
883		},
884	}
885	new_table // return the completed Table struct, complete with "array" and "caption" information
886}
887
888/// For a single Action line, extract the information and return an Action struct
889fn parse_action_line(line:&str) -> Action {
890	// Default value of "Two Party Verification" field is FALSE
891	// delimit the line by the bar/pipe character
892	let a: Vec<&str> = line.split('|').collect();
893	match a.len() {
894		2 => Action { perform:a[0].to_string(), expect:a[1].to_string(), tpv:false },
895		1 => Action { perform:a[0].to_string(), expect:"ERROR: NO EXPECTED VALUE PROVIDED".to_string(), tpv:false },
896		0 => Action { perform:"ERROR: NO ACTION PROVIDED".to_string(), expect:"ERROR: NO EXPECTED VALUE PROVIDED".to_string(), tpv:false },
897		// 3 is expected, and the wildcard case covers 3 or more
898		_ => Action { perform:a[0].to_string(), expect:a[1].to_string(), tpv: 
899			match trim_whitespace_make_uppercase(a[2]).as_str() {
900				"TPV"|"T"|"TRUE"|"Y"|"YES"|"TWOPARTYVERIFICATION"|"TWO-PARTYVERIFICATION" => true,
901				_ => false,
902			}
903		},
904	}
905}
906
907
908/// For an "Image" line, parse the two output pieces of information and return as an Image type SubStep
909fn parse_image_line(line:&str) -> SubStep {
910
911	// Delimit the input string by bar/pipe characters and respond based on the number of substrings
912	let all_parts: Vec<&str> = line.split('|').collect();
913	match all_parts.len() {
914		
915		// If only one string, then the image file and caption are the same
916		// If only one string but it's empty, then include a default path and file name for a placeholder image
917		1 => {
918			if all_parts[0]=="" {
919				return SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string())
920			} else {
921				return SubStep::Image(all_parts[0].to_string().trim().to_string(),all_parts[0].to_string().trim().to_string())
922			}
923		},
924		// If for some reason the length of the segment vector is zero (??), then placeholder
925		0 => return SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string()),
926		// Two is expected here: the first is the image file name, and the second is the caption
927		// Wildcard means 3 or more string segments, so use first two as in nominal path
928		_ => {
929			if all_parts[0]=="" {
930				if all_parts[1]=="" {
931					return SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string());
932				} else {
933					return SubStep::Image("../assets/placeholderImage-small.png".to_string(),all_parts[1].to_string().trim().to_string());
934				}
935			} else {
936				if all_parts[1]=="" {
937					return SubStep::Image(all_parts[0].to_string().trim().to_string(),all_parts[0].to_string().trim().to_string())
938				} else {
939					return SubStep::Image(all_parts[0].to_string().trim().to_string(),all_parts[1].to_string().trim().to_string())
940				}				
941			}
942		},
943	}
944
945}
946
947/// Verification lines have three expected pieces of information; this parsing function needs to be robust to all cases
948fn parse_verification_line(line:&str) -> Requirement {
949
950	// Delimit the input string by bar/pipe characters and respond based on the number of substrings
951	let all_parts: Vec<&str> = line.split('|').collect();
952	match all_parts.len() {
953		// Two means we will pass an unknown string to the method, leaving that handling to the other function
954		2 => return Requirement{id:all_parts[0].trim().to_string(),text:all_parts[1].trim().to_string(),method:which_method("???"),},
955		// One string part means we don't have requirement text or method, so unknown values are filled
956		1 => return Requirement{id:all_parts[0].trim().to_string(),text:"???".to_string(),method:which_method("???"),},
957		0 => return Requirement{id:"???".to_string(),text:"???".to_string(),method:which_method("???"),},
958		// Three is expected: ID field is first, then Requirement text, then the method of verificaiton (derived in another function)
959		_ => return Requirement{id:all_parts[0].trim().to_string(),text:all_parts[1].trim().to_string(),method:which_method(all_parts[2].trim()),},
960	}
961
962}
963
964/// Interpret a user string into an enum variant
965fn which_method(m:&str) -> VerificationMethod {
966	// Be robust and case insensitive
967	// Commonly used terms for these verification methods are single letter e.g. D, I, A, S, T
968	match trim_whitespace_make_uppercase(m).as_str() {
969		"DEMONSTRATION"|"DEMO"|"D"	=> VerificationMethod::Demonstration,
970		"INSPECTION"|"I"			=> VerificationMethod::Inspection,
971		"ANALYSIS"|"A"				=> VerificationMethod::Analysis,
972		"SAMPLING"|"SAMPLE"|"S"		=> VerificationMethod::Sampling,
973		"TEST"|"T"					=> VerificationMethod::Test,
974		// Sampling is the least expected of these for Stanhope processes
975		// Making Sampling the default is a pseudo-flag that something is wrong
976		// TODO: add an enum variant that covers the ??? case
977		_ => VerificationMethod::Sampling,
978	}
979}
980
981/// Pass the first part as the descriptive string for the resource
982/// The second part is a flag to determine if this resource needs to be calibrated to be used in this process
983fn parse_resource_line(line:&str) -> Resource {
984	let all_parts: Vec<&str> = line.split('|').collect();
985
986	let cal:bool = match all_parts.len() {
987		0|1 => false,
988		_ => match trim_whitespace_make_uppercase(all_parts[1]).as_str() {
989				"C"|"CAL"|"CALIB"|"CALIBRATE"|"CALIBRATED"|"CALIBRATION"|"Y"|"YES"|"T"|"TRUE" => true,
990				_ => false,
991			}
992	};
993
994	match all_parts[0] {
995		""	=> Resource { name: "ERROR: NO RESOURCE IDENTIFIED".to_string(), calibration:cal, },
996		_ 	=> Resource { name: all_parts[0].to_string(), calibration:cal, },
997	}
998}
999
1000
1001//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄       ▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄           
1002// ▐░░░░░░░░░░░▌▐░░▌     ▐░░▌▐░░░░░░░░░░░▌▐░▌          
1003//  ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌   ▐░▐░▌▐░█▀▀▀▀▀▀▀█░▌▐░▌          
1004//      ▐░▌     ▐░▌▐░▌ ▐░▌▐░▌▐░▌       ▐░▌▐░▌          
1005//      ▐░▌     ▐░▌ ▐░▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌          
1006//      ▐░▌     ▐░▌  ▐░▌  ▐░▌▐░░░░░░░░░░░▌▐░▌          
1007//      ▐░▌     ▐░▌   ▀   ▐░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌          
1008//      ▐░▌     ▐░▌       ▐░▌▐░▌          ▐░▌          
1009//  ▄▄▄▄█░█▄▄▄▄ ▐░▌       ▐░▌▐░▌          ▐░█▄▄▄▄▄▄▄▄▄ 
1010// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌          ▐░░░░░░░░░░░▌
1011//  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀            ▀▀▀▀▀▀▀▀▀▀▀ 
1012
1013/// Functions necessary to create ::new() structs, as well as "get" every private field publicly, and to change or append private fields publicly
1014impl Process {
1015
1016	/// Return Process struct with placeholder text in String fields, and empty vectors for Vec fields
1017	pub fn new() -> Process {
1018		Process {
1019			process_file: "".to_string(),
1020			number: "NO NUMBER IN EBML FILE - THIS IS PLACEHOLDER TEXT".to_string(),
1021			all_revisions: vec![],
1022			title: "NO TITLE IN EBML FILE".to_string(),
1023			process_type: "NO PROCESS TYPE IDENTIFIED IN EBML FILE".to_string(),
1024			author: "NO AUTHOR IN EBML FILE".to_string(),
1025			reviewer: "NO REVIEWER IN EBML FILE".to_string(),
1026			subject: "N/A".to_string(),
1027			subject_image: "N/A".to_string(),
1028			product: "N/A".to_string(),
1029			product_image: "N/A".to_string(),
1030			all_objectives: vec![],
1031			all_out_of_scopes: vec![],
1032			all_sections: vec![],
1033			all_resources: vec![],
1034			all_calibrated_resources: vec![],
1035			all_verifications: vec![],
1036			all_templates: vec![],
1037			full_source: vec![],
1038		}
1039	}
1040
1041	/// Return reference to the **Process File** string
1042	pub fn get_process_file(&self) -> &String { &self.process_file }
1043	/// Return reference to the **Document Number** string
1044	pub fn get_number(&self) -> &String { &self.number }
1045	/// Return reference to the LATEST **Revision** (first listed in the markup file)
1046	pub fn get_revision(&self) -> &String { if self.all_revisions.len()==0 { &self.number } else { &self.all_revisions[0].0 } }
1047	/// Return reference to the vector of touples describing each Revision and what changed
1048	pub fn get_all_revisions(&self) -> &Vec<(String,String)> { &self.all_revisions }
1049	/// Return reference to the **Title** string
1050	pub fn get_title(&self) -> &String { &self.title }
1051	/// Return reference to the **Process Type** string
1052	pub fn get_process_type(&self) -> &String { &self.process_type }
1053	/// Return reference to the **Author** string
1054	pub fn get_author(&self) -> &String { &self.author }
1055	/// Return reference to the **Reviewer** string
1056	pub fn get_reviewer(&self) -> &String { &self.reviewer }
1057	/// Return reference to the **Subject** string
1058	pub fn get_subject(&self) -> &String { &self.subject }
1059	/// Return reference to the file name of an **Image of the Subject** as string
1060	pub fn get_subject_image(&self) -> &String { &self.subject_image }
1061	/// Return reference to the **Product** string
1062	pub fn get_product(&self) -> &String { &self.product }
1063	/// Return reference to the file name of an **Image of the Product** as string
1064	pub fn get_product_image(&self) -> &String { &self.product_image }
1065	/// Return reference to a **Vector of Sections**, in the order they appear in the markup
1066	pub fn get_all_sections(&self) -> &Vec<Section> { &self.all_sections }
1067	/// Return the total number of Steps throughout the process, including all Sections and Section References
1068	pub fn get_step_count(&self) -> usize {
1069		let mut stp_cnt:usize = 0;
1070		for sec in &self.all_sections {
1071			stp_cnt += sec.get_all_steps().len();
1072		}
1073		return stp_cnt;
1074	}
1075	/// Return reference to a **Vector of Requirement Tuples**, each with a Requirement struct and a string containing the Step number
1076	pub fn get_all_verifications(&self) -> &Vec<(Requirement,String)> { &self.all_verifications }
1077	/// Return reference to a **Vector of Tuples** describing each objective and at what step it is achieved
1078	pub fn get_all_objectives(&self) -> &Vec<(String,String)> { &self.all_objectives }
1079	/// Return reference to a **Vector of Tuples** describing each out-of-scope declaration and at from which step
1080	pub fn get_all_out_of_scopes(&self) -> &Vec<(String,String)> { &self.all_out_of_scopes }
1081	/// Return reference to a **Vector of Templates** as strings, one for each CSS file needed for the process
1082	pub fn get_all_templates(&self) -> &Vec<String> { &self.all_templates }
1083	/// Return reference to a **Vector of Resource Tuples**, each with a Resource struct and a string containing the Step number
1084	pub fn get_all_resources(&self) -> &Vec<(Resource,String)> { &self.all_resources }
1085	/// Return reference to a **Vector of Resources** (a unique set of only those that need calibration)
1086	pub fn get_all_calibrated_resources(&self) -> &Vec<Resource> { &self.all_calibrated_resources }
1087	/// Return the total number of Action lines that require Two-Party Verification (TPV)
1088	pub fn get_tpv_count(&self) -> usize {
1089		let mut counter:usize = 0;
1090		for sec in self.get_all_sections() {
1091			for stp in sec.get_all_steps() {
1092				for sub in stp.get_all_sub_steps() {
1093					match sub {
1094						SubStep::ActionSequence(acts) => {
1095							for act in acts {
1096								if act.get_tpv().clone() { counter += 1; }
1097							}
1098						},
1099						_ => (),
1100					}
1101				}
1102			}
1103		}
1104		counter
1105	}
1106	/// Return the total number of Action lines that don't require Two-Party Verification (no TPV)
1107	pub fn get_non_tpv_count(&self) -> usize {
1108		let mut counter:usize = 0;
1109		for sec in self.get_all_sections() {
1110			for stp in sec.get_all_steps() {
1111				for sub in stp.get_all_sub_steps() {
1112					match sub {
1113						SubStep::ActionSequence(acts) => {
1114							for act in acts {
1115								if !act.get_tpv().clone() { counter += 1; }
1116							}
1117						},
1118						_ => (),
1119					}
1120				}
1121			}
1122		}
1123		counter
1124	}
1125	/// Return a vector of string tuples, with every "Command" line's text (first element), with the step number (second element) in order of appearance in the process
1126	pub fn get_all_command_lines(&self) -> Vec<(String,String)> {
1127    	let mut all_command_lines = vec![];
1128    	let mut sec_count:u16 = 0;
1129		for sec in self.get_all_sections() {
1130			sec_count += 1;
1131			let mut step_count:u16 = 0;
1132			for stp in sec.get_all_steps() {
1133				step_count += 1;
1134				for sub in stp.get_all_sub_steps() {
1135					match sub {
1136						SubStep::Command(txt) => {
1137							all_command_lines.push((txt.to_string(),("Step ".to_owned()+&sec_count.to_string()+"."+&step_count.to_string()).to_string()));
1138						},
1139						_ => (),
1140					}
1141				}
1142			}
1143		}
1144		all_command_lines
1145	}
1146	/// Return a vector of string tuples, with every "Context" line's text (first element), with the step number (second element) in order of appearance in the process
1147	pub fn get_all_context_lines(&self) -> Vec<(String,String)> {
1148    	let mut all_context_lines = vec![];
1149    	let mut sec_count:u16 = 0;
1150		for sec in self.get_all_sections() {
1151			sec_count += 1;
1152			let mut step_count:u16 = 0;
1153			for stp in sec.get_all_steps() {
1154				step_count += 1;
1155				for sub in stp.get_all_sub_steps() {
1156					match sub {
1157						SubStep::Context(txt) => {
1158							all_context_lines.push((txt.to_string(),("Step ".to_owned()+&sec_count.to_string()+"."+&step_count.to_string()).to_string()));
1159						},
1160						_ => (),
1161					}
1162				}
1163			}
1164		}
1165		all_context_lines
1166	}
1167	/// Return extracted information from all SubStep "Image" variants as Vec of String tuples: (image file, caption text)
1168	pub fn get_all_images(&self) -> Vec<(String,String)> {
1169		let mut all_images = vec![];
1170		for sec in self.get_all_sections() { for stp in sec.get_all_steps() { for sub in stp.get_all_sub_steps() {
1171			match sub { SubStep::Image(file,caption) => all_images.push(((sec.get_relative_path().to_owned() + &file.to_string()).to_string(),caption.to_string())), _ => (), }
1172		}}}
1173		match self.get_subject_image().as_str() {
1174			"DEFAULT-PLACEHOLDER-IMAGE.png" | "N/A" | "" => (),
1175			_ => all_images.push((self.get_subject_image().to_string(),"Subject Image".to_string())),
1176		};
1177		match self.get_product_image().as_str() {
1178			"DEFAULT-PLACEHOLDER-IMAGE.png" | "N/A" | "" => (),
1179			_ => all_images.push((self.get_product_image().to_string(),"Product Image".to_string())),
1180		};
1181		return all_images;
1182	}
1183	/// Return a count of the number of unique image files invoked in this EBML file, including native "Image" lines, Subject Image, Product Image, and SECREFed "Image" lines
1184	pub fn get_unique_image_count(&self) -> usize {
1185		let (mut files, _captions): (Vec<_>, Vec<_>) = self.get_all_images().into_iter().map(|(a, b)| (a, b)).unzip();
1186		files.sort();
1187    	files.dedup();
1188    	return files.len();
1189	}
1190	/// Check to see if each image file needed for this process exists in the file system, and return a Vec of Strings of those that don't
1191	pub fn get_missing_images(&self) -> Vec<String> {
1192		let mut missing_images:Vec<String> = vec![];
1193		let (mut files, _captions): (Vec<_>, Vec<_>) = self.get_all_images().into_iter().map(|(a,b)| (a,b)).unzip();
1194		files.sort();
1195		files.dedup();
1196		let process_file = self.get_process_file();
1197		let process_file_parts:Vec<&str> = process_file.split('/').collect();
1198		let mut process_folder:String = String::from("");
1199		for (ii,process_file_part) in process_file_parts.clone().into_iter().enumerate() {
1200			if ii == 0 { process_folder.push_str(process_file_part); }
1201			else if ii+1 < process_file_parts.len() { process_folder.push_str(&("/".to_owned() + process_file_part)); }
1202		}
1203		for file in files { 
1204			let file_full = process_folder.to_owned() + "/" + &file.trim();
1205			//println!("Checking if [{}] exists...",&file);
1206			match std::fs::exists(&file_full) {
1207				Ok(true) => (),
1208		        Ok(false) => missing_images.push(file.trim().to_string()),
1209		        Err(e) => {
1210		        	missing_images.push(file.trim().to_string());
1211		        	eprintln!("Error checking file: {}",e);
1212		        },
1213	    	}
1214		}
1215		return missing_images;
1216	}
1217	/// Check to see if each image file needed for this process exists in the file system, and return a count of those that don't
1218	pub fn get_missing_image_count(&self) -> usize { self.get_missing_images().len() }
1219	/// Return a count of "Section Reference" or "SECREF" lines in this Process, prior to evaluating them into sections
1220	pub fn get_section_reference_count(&self) -> usize {
1221		let mut secref_cnt:usize = 0;
1222		let all_lines = self.get_full_source();
1223		for line in all_lines {
1224			let all_parts: Vec<&str> = line.split('|').collect();
1225			if all_parts.len() > 0 {
1226				match trim_whitespace_make_uppercase(all_parts[0]).as_str() {
1227					"SECTIONREFERENCE" | "SECREF" => secref_cnt+=1,
1228					_ => (),
1229				};
1230			}
1231		}
1232		return secref_cnt;
1233	} 
1234	/// Return the full source EBML of this process as a Vec of Strings, one String per line of EBML
1235	pub fn get_full_source(&self) -> &Vec<String> { &self.full_source }
1236
1237	/// Used in verbose mode to display contents of the struct to stdout
1238	pub fn display_process_to_stdout(&self) {
1239		println!("\n== =================================================================");
1240		println!("==  {}",&self.get_title());
1241		if self.get_all_objectives().len() > 0 {
1242			println!("==   ↪ Objectives:");
1243			for (jj,(obj,_snum)) in self.get_all_objectives().into_iter().enumerate() {
1244				println!("==    {}. {}",jj+1,obj);
1245			}
1246		}
1247		println!("==   ↪ Document Number:           {}",&self.get_number());
1248		println!("==   ↪ Process Type:              {}",&self.get_process_type());
1249		println!("==   ↪ Current Revision:          {}",&self.get_revision());
1250		println!("==     ↪ Number of Revs:          {}",&self.get_all_revisions().len());
1251		println!("==   ↪ Author of this Rev:        {}",&self.get_author());
1252		println!("==   ↪ Reviewer of this Rev:      {}",&self.get_reviewer());
1253		println!("==\n==  Purpose and Objectives");
1254		println!("==   ↪ Subject of process:        {}",&self.get_subject());
1255		println!("==   ↪ Image of Subject:          {}",&self.get_subject_image());
1256		println!("==   ↪ Product produced:          {}",&self.get_product());
1257		println!("==   ↪ Image of Product:          {}",&self.get_product_image());
1258		println!("==   ↪ Count of Objectives:       {}",&self.get_all_objectives().len());
1259		println!("==   ↪ Count of Out of Scopes:    {}",&self.get_all_out_of_scopes().len());
1260		println!("==\n==  Process Structure");
1261		println!("==   ↪ Count of Templates:        {}",&self.get_all_templates().len());
1262		println!("==   ↪ Count of Sections:         {}",&self.get_all_sections().len());
1263		println!("==     ↪ Section References:      {}",&self.get_section_reference_count());
1264		println!("==   ↪ Count of Steps (total):    {}",&self.get_step_count());
1265		println!("==   ↪ Count of Resources:        {}",&self.get_all_resources().len());
1266		println!("==     ↪ Calibrated Resources:    {}",&self.get_all_calibrated_resources().len());
1267		println!("==   ↪ Count of Verifications:    {}",&self.get_all_verifications().len());
1268		println!("==   ↪ Count of Actions:          {}",&self.get_tpv_count()+&self.get_non_tpv_count());
1269		println!("==     ↪ TPV Actions:             {}",&self.get_tpv_count());
1270		println!("==     ↪ Non-TPV Actions:         {}",&self.get_non_tpv_count());
1271		println!("==   ↪ Count of Commands:         {}",&self.get_all_command_lines().len());
1272		println!("==   ↪ Count of Context lines:    {}",&self.get_all_context_lines().len());
1273		println!("==   ↪ Images (lines, sub, prod): {}",&self.get_all_images().len());
1274		println!("==     ↪ Unique images called:    {}",&self.get_unique_image_count());
1275		println!("==     ↪ Missing image files:     {}",&self.get_missing_image_count());
1276		if self.get_missing_image_count() > 0 {
1277			for (ii,image) in self.get_missing_images().into_iter().enumerate() {
1278				println!("==       [{}] {}",ii+1,&image);
1279			}
1280		}
1281		println!("== =================================================================\n");
1282	}
1283
1284	/// Change the stored location of the EBML file for this process
1285	pub fn set_process_file(&mut self, file_name:&str) {
1286		self.process_file = String::from(file_name);
1287	}
1288	/// Change the Document Number
1289	pub fn set_number(&mut self, doc_num: &str) {
1290		self.number = String::from(doc_num.trim());
1291	}
1292	/// Add a Revision Tuple to the Vector "all_revisions"
1293	pub fn add_revision(&mut self, rev_line: &str) {
1294		// Delimit the line into its bar/piped chunks
1295		let chunks:Vec<&str> = rev_line.split('|').collect();
1296		// If the first chunk isn't an empty string, then use it (otherwise use default text)
1297		let rev_str = if !(chunks[0]=="") { chunks[0] } else { &"[No Rev]" };
1298		// Use the second chunk if it exists and isn't an empty string
1299		let chg_str = if chunks.len()<2 { &"[No description provided by Author]" } else if chunks[1]=="" { &"[No description provided by Author]" } else { chunks[1] };
1300		self.all_revisions.push((String::from(rev_str.trim()),String::from(chg_str.trim())));
1301	}
1302	/// Change the Document Title
1303	pub fn set_title(&mut self, title: &str) {
1304		self.title = String::from(title.trim());
1305	}
1306	/// Change the Process Type
1307	pub fn set_process_type(&mut self, process_type: &str) {
1308		self.process_type = String::from(process_type.trim());
1309	}
1310	/// Change the Document Author
1311	pub fn set_author(&mut self, author: &str) {
1312		self.author = String::from(author.trim());
1313	}
1314	/// Change the Document Reviewer
1315	pub fn set_reviewer(&mut self, reviewer: &str) {
1316		self.reviewer = String::from(reviewer.trim());
1317	}
1318	/// Change the Document Subject
1319	pub fn set_subject(&mut self, subject: &str) {
1320		self.subject = String::from(subject.trim());
1321	}
1322	/// Change the Image file name of the Document Subject
1323	pub fn set_subject_image(&mut self, subject_image: &str) {
1324		self.subject_image = String::from(subject_image.trim());
1325	}
1326	/// Change the Document Product
1327	pub fn set_product(&mut self, product: &str) {
1328		self.product = String::from(product.trim());
1329	}
1330	/// Change the Image file name of the Document Product
1331	pub fn set_product_image(&mut self, product_image: &str) {
1332		self.product_image = String::from(product_image.trim());
1333	}
1334	/// Add a Template file to the Vector of Template files (strings)
1335	pub fn add_template(&mut self, template: &str) {
1336		self.all_templates.push(String::from(template.trim()));
1337	}
1338	/// Add a Section to the Vector of Section structs
1339	pub fn add_section(&mut self, section: Section) {
1340		self.all_sections.push(section);
1341	}
1342	/// Add a Verification tuple to the Vector of Verification tuples
1343	pub fn add_verification(&mut self, requirement: Requirement, step:String) {
1344		self.all_verifications.push((requirement,step));
1345	}
1346	/// Add an Objective tuple to the Vector of Objective tuples
1347	pub fn add_objective(&mut self, objective:String, step:String) {
1348		self.all_objectives.push((objective,step));
1349	}
1350	/// Add an Out-of-Scope tuple to the Vector of Out-of-Scope tuples
1351	pub fn add_out_of_scope(&mut self, out_of_scope:String, step:String) {
1352		self.all_out_of_scopes.push((out_of_scope,step));
1353	}
1354	/// Add a Resource struct to the Vector of Resource structs
1355	pub fn add_resource(&mut self, resource: Resource, step:String) {
1356		self.all_resources.push((resource,step));
1357	}
1358	/// Add a Resource struct to the Vector of calibrated Resource structs
1359	pub fn add_calibrated_resource(&mut self, resource: Resource) {
1360		if *resource.get_calibration() { self.all_calibrated_resources.push(resource); }
1361	}
1362	/// Store the full text of the EBML file for this process as a Vec of Strings, one String per line of EBML
1363	pub fn set_full_source(&mut self, full_source: Vec<String>) {
1364		self.full_source = full_source;
1365	}
1366	
1367}
1368
1369/// Section needs "get" and "set/add" functions as well, to read and write private fields
1370impl Section {
1371
1372	/// Blank string for the title, and empty Vec initialized for the list of Steps
1373	fn new() -> Section {
1374		Section {
1375			title: "".to_string(),
1376			all_steps: vec![],
1377			relative_path: "".to_string(),
1378		}
1379	}
1380
1381	/// Return reference to **Section Title** as string
1382	pub fn get_title(&self) -> &String { &self.title }
1383	/// Return reference to **Vector of Step structs**
1384	pub fn get_all_steps(&self) -> &Vec<Step> { &self.all_steps }
1385	/// Return the relative path from the Process folder to the Section's Process folder, only non-empty for SECREF feature
1386	pub fn get_relative_path(&self) -> &String { &self.relative_path }
1387
1388	/// Change the **Section Title**
1389	fn set_title(&mut self, title: &str) {
1390		self.title = String::from(title);
1391	}
1392	/// Add a complete Step struct to the Vector of Step structs
1393	fn add_step(&mut self, step: Step) {
1394		self.all_steps.push(step);
1395	}
1396	/// Change the **Relative Path** from the Process folder to the Section's Process folder, especially if performing a SECREF fetch
1397	fn set_relative_path(&mut self, path:&str) {
1398		self.relative_path = String::from(path);
1399	}
1400}
1401
1402/// Step needs "get" and "set/add" functions as well, to read and write private fields
1403impl Step {
1404
1405	/// Empty string for the Step text, and empty vectors for 'resources' and 'all_sub_steps'
1406	pub fn new() -> Step {
1407		Step {
1408			text: "".to_string(),
1409			resources: vec![],
1410			all_sub_steps: vec![],
1411		}
1412	}
1413
1414	/// Return reference to **Step text** as string
1415	pub fn get_text(&self) -> &String { &self.text }
1416	/// Return reference to **Vector of Resource structs** for this step
1417	pub fn get_resources(&self) -> &Vec<Resource> { &self.resources }
1418	/// Return reference to **Vector of SubStep structs** for all SubSteps in this Step
1419	pub fn get_all_sub_steps(&self) -> &Vec<SubStep> { &self.all_sub_steps }
1420
1421	/// Change the text of this Step
1422	fn set_text(&mut self, text: &str) {
1423		self.text = String::from(text);
1424	}
1425	/// Add a complete SubStep to the ordered Vector of SubStep structs
1426	fn add_sub_step(&mut self, sub_step: SubStep) {
1427		self.all_sub_steps.push(sub_step);
1428	}
1429	/// Add a complete Resource struct to the ordered Vector of Resource structs
1430	fn add_resource(&mut self,r:Resource) {
1431		self.resources.push(r);
1432	}
1433
1434}
1435
1436/// Since Action structs are created directly in a function outside the struct at read-time, we just need "get" functions
1437impl Action {
1438	/// Return reference to the "Action to Perform" string
1439	pub fn get_perform(&self) -> &String { &self.perform }
1440	/// Return reference to the "Expected Outcome" string
1441	pub fn get_expect(&self) -> &String { &self.expect }
1442	/// Return reference to the "Two Party Verification" boolean
1443	pub fn get_tpv(&self) -> &bool { &self.tpv }
1444}
1445
1446/// Since Requirement structs are created directly in a function outside the struct at read-time, we just need "get" functions
1447impl Requirement { 
1448	/// Return reference to the **Requirement ID** string
1449	pub fn get_id(&self) -> &String { &self.id }
1450	/// Return reference to the **Requirement Text** string
1451	pub fn get_text(&self) -> &String { &self.text }
1452	/// Return reference to the **Verification Method** as a string, even though that's not how's it's stored
1453	pub fn get_method(&self) -> String {
1454		// The Verification Method is stored as a VerificationMethod enum, so we need to map that to String values
1455		match &self.method {
1456			VerificationMethod::Demonstration => "Demonstration".to_string(),
1457			VerificationMethod::Inspection => "Inspection".to_string(),
1458			VerificationMethod::Analysis => "Analysis".to_string(),
1459			VerificationMethod::Sampling => "Sampling".to_string(),
1460			VerificationMethod::Test => "Test".to_string(),
1461		}
1462	}
1463}
1464
1465/// Simple public "get" funciton for the one struct field
1466impl Resource {
1467	/// Return reference to the **Resource Name** string
1468	pub fn get_name(&self) -> &String { &self.name }
1469
1470	/// Return the boolean that indicates if the resource requires calibration
1471	pub fn get_calibration(&self) -> &bool { &self.calibration }
1472}
1473
1474/// Public "get" functions give access to the private fields. Private "set/add" functions are used within this module to construct the fields from a "new" Table struct.
1475impl Table {
1476
1477	/// Empty string for the default caption, and an empty array for the default table data
1478	pub fn new() -> Table {
1479		Table {
1480			caption: "".to_string(),
1481			array: vec![],
1482		}
1483	}
1484
1485	/// Return reference to the table caption
1486	pub fn get_caption(&self) -> &String { &self.caption }
1487	/// Change the table caption field
1488	fn set_caption(&mut self, caption: &str) {
1489		self.caption = String::from(caption.trim_start().trim_end());
1490	}
1491
1492	/// Return a tuple with the number of rows and the number of columns in the table data array.
1493	/// 
1494	/// Assumptions:
1495	/// 1. if there are zero rows, there are zero columns
1496	/// 1. the number of columns in the first row is the number of columns that **should** be in every row
1497	pub fn get_size(&self) -> (usize,usize) {
1498		
1499		let rows = self.array.len();
1500
1501		let columns:usize = match rows {
1502			0 => 0,
1503			_ => self.array[0].len(),
1504		};
1505		
1506		(rows,columns)
1507	}
1508
1509	/// Return a vector with all cell contents for an indexed row number.
1510	/// 
1511	/// Example: to return the contents of the first row,
1512	/// > my_table.get_row(0)
1513	/// 
1514	/// Example: to return the conents of the 10th row,
1515	/// > my_table.get_row(9)
1516	pub fn get_row(&self,row_num:usize) -> Vec<String> {
1517		let (rows,_columns) = self.get_size();
1518		match &row_num <= &(rows-1) {
1519			true 	=> self.array[row_num].clone(),
1520			false 	=> panic!("table reference out of bounds"),
1521		}
1522	}
1523
1524	/// Append the bottom of the table data array with another row of data.
1525	/// 
1526	/// Assumptions:
1527	/// 1. if this is the first row to be pushed, then it has the **correct** number of columns
1528	/// 1. if this is **not** the first row, then columns will be added from left-to-right until the **correct** number of columns is reached
1529	/// 1. if this new row has fewer than the **correct** number of columns, empty strings will fill in the righthand columns until the **correct** number is achieved
1530	fn add_row(&mut self,row:Vec<String>) {
1531		let (rows,columns) = self.get_size();
1532		match rows {
1533			0 => self.array.push(row),
1534			_ => {
1535				let mut new_row:Vec<String> = vec![];
1536				for ii in 0..columns {
1537					if ii < row.len() {
1538						new_row.push(row[ii].clone().trim_start().trim_end().to_string());
1539					} else {
1540						new_row.push("".to_string());
1541					}
1542				}
1543				self.array.push(new_row)
1544			},
1545		};
1546	}
1547
1548}
1549
1550
1551//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
1552// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
1553//  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
1554//      ▐░▌     ▐░▌          ▐░▌               ▐░▌     
1555//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
1556//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
1557//      ▐░▌     ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     
1558//      ▐░▌     ▐░▌                    ▐░▌     ▐░▌     
1559//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     
1560//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
1561//       ▀       ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀      
1562
1563#[cfg(test)]
1564mod tests {
1565    // Note this useful idiom: importing names from outer (for mod tests) scope.
1566	use super::*;
1567	use std::fs;
1568	use std::fs::OpenOptions;
1569	use std::io::Write;
1570
1571	// Test the struct impl functions
1572	#[test]
1573	fn test_process_new() {
1574		Process::new();
1575	}
1576
1577	#[test]
1578    fn test_process_get_functions() {
1579		
1580		let p:Process = Process::new();
1581
1582		assert_eq!(&p.number,p.get_number());
1583		println!("struct Process / Result of get_number() -> {:?}",p.get_number());
1584
1585		assert_eq!(&p.process_type,p.get_process_type());
1586		println!("struct Process / Result of get_process_type() -> {:?}",p.get_process_type());
1587		
1588		assert_eq!(&p.number,p.get_revision());
1589		println!("struct Process / Result of get_revision() -> {:?}",p.get_revision());
1590		
1591		assert!(p.all_revisions.len()==0);
1592		let _is_right_type:&Vec<(String,String)> = p.get_all_revisions();
1593		println!("struct Process / Result of get_all_revisions() -> {:?}",p.get_all_revisions());
1594
1595		assert_eq!(&p.title,p.get_title());
1596		println!("struct Process / Result of get_title() -> {:?}",p.get_title());
1597
1598		assert_eq!(&p.author,p.get_author());
1599		println!("struct Process / Result of get_author() -> {:?}",p.get_author());
1600
1601		assert_eq!(&p.reviewer,p.get_reviewer());
1602		println!("struct Process / Result of get_reviewer() -> {:?}",p.get_reviewer());
1603
1604		assert_eq!(&p.subject,p.get_subject());
1605		println!("struct Process / Result of get_subject() -> {:?}",p.get_subject());
1606
1607		assert_eq!(&p.subject_image,p.get_subject_image());
1608		println!("struct Process / Result of get_subject_image() -> {:?}",p.get_subject_image());
1609
1610		assert_eq!(&p.product,p.get_product());
1611		println!("struct Process / Result of get_product() -> {:?}",p.get_product());
1612
1613		assert_eq!(&p.product_image,p.get_product_image());
1614		println!("struct Process / Result of get_product_image() -> {:?}",p.get_product_image());
1615
1616		assert_eq!(p.get_tpv_count(),0);
1617		println!("struct Process / Result of get_tpv_count() -> {:?}",p.get_tpv_count());
1618
1619		assert_eq!(p.get_non_tpv_count(),0);
1620		println!("struct Process / Result of get_non_tpv_count() -> {:?}",p.get_non_tpv_count());
1621
1622		assert!(p.all_sections.len()==0);
1623		let _is_right_type:&Vec<Section> = p.get_all_sections();
1624		println!("struct Process / Result of get_all_sections() -> [it's empty]");
1625
1626		assert!(p.all_verifications.len()==0);
1627		let _is_right_type:&Vec<(Requirement,String)> = p.get_all_verifications();
1628		println!("struct Process / Result of get_all_sections() -> [it's empty]");
1629
1630		assert!(p.all_templates.len()==0);
1631		let _is_right_type:&Vec<String> = p.get_all_templates();
1632		println!("struct Process / Result of get_all_templates() -> [it's empty]");
1633
1634		assert!(p.all_resources.len()==0);
1635		let _is_right_type:&Vec<(Resource,String)> = p.get_all_resources();
1636		println!("struct Process / Result of get_all_resources() -> [it's an empty]");
1637
1638		assert!(p.all_calibrated_resources.len()==0);
1639		let _is_right_type:&Vec<Resource> = p.get_all_calibrated_resources();
1640		println!("struct Process / Result of get_all_calibrated_resources() -> [it's an empty]");
1641
1642		assert!(p.all_objectives.len()==0);
1643		let _is_right_type:&Vec<(String,String)> = p.get_all_objectives();
1644		println!("struct Process / Result of get_all_objectives() -> [it's an empty]");
1645
1646		assert!(p.all_out_of_scopes.len()==0);
1647		let _is_right_type:&Vec<(String,String)> = p.get_all_out_of_scopes();
1648		println!("struct Process / Result of get_all_out_of_scopes() -> [it's an empty]");
1649
1650	}
1651
1652	#[test]
1653	fn test_process_display_process_to_stdout() {
1654
1655		let p:Process = Process::new();
1656		p.display_process_to_stdout();
1657
1658	}
1659
1660	#[test]
1661	fn test_process_set_functions() {
1662
1663		let mut p:Process = Process::new();
1664
1665		p.set_number("SET_PROCESS_DOC_NUMBER");
1666		p.add_revision("SET_REV|SET_REV_CHANGE");
1667		p.set_title("SET_PROCESS_TITLE");
1668		p.set_author("SET_AUTHOR");
1669		p.set_reviewer("SET_REVIEWER");
1670		p.set_subject("SET_SUBJECT");
1671		p.set_subject_image("SET_SUBJECT_IMAGE");
1672		p.set_product("SET_PRODUCT");
1673		p.set_product_image("SET_PRODUCT_IMAGE");
1674		p.add_template("SET_TEMPLATE");
1675		p.add_section(Section{title:"SET_SECTION_TITLE".to_string(),all_steps:vec![],relative_path:"SET_SECTION_RELATIVE_PATH".to_string()});
1676		p.add_verification(Requirement{id:"SET_REQUIREMENT_ID".to_string(),text:"SET_REQUIREMENT_TEXT".to_string(),method:VerificationMethod::Sampling,},"SET_VERIFICATION_STEP".to_string());
1677		p.add_resource(Resource{name:"SET_RESOURCE_NAME".to_string(),calibration:false},"SET_RESOURCE_STEP".to_string());
1678		p.add_objective("SET_OBJECTIVE_TEXT".to_string(),"SET_OBJECTIVE_STEP".to_string());
1679		p.add_out_of_scope("SET_OBJECTIVE_TEXT".to_string(),"SET_OBJECTIVE_STEP".to_string());
1680
1681		p.display_process_to_stdout();
1682
1683	}
1684
1685	#[test]
1686	fn test_section_new() {
1687		Section::new();
1688	}
1689
1690	#[test]
1691	fn test_section_get_functions() {
1692
1693		let s:Section = Section::new();
1694
1695		assert_eq!(&s.title,s.get_title());
1696		println!("struct Section / Result of get_title() -> {:?}",s.get_title());
1697
1698		assert!(s.get_all_steps().len()==0);
1699		let _is_right_type:&Vec<Step> = s.get_all_steps();
1700		println!("struct Section / Result of get_all_steps -> [it's empty]");
1701
1702	}
1703
1704	#[test]
1705	fn test_section_set_functions() {
1706
1707		let mut s:Section = Section::new();
1708
1709		s.set_title("SET_SECTION_TITLE");
1710		s.add_step(Step{text:"SET_SECTION_STEP_TEXT".to_string(),resources:vec![],all_sub_steps:vec![],});
1711
1712	}
1713
1714	#[test]
1715	fn test_step_new() {
1716		Step::new();
1717	}
1718
1719	#[test]
1720	fn test_step_get_functions() {
1721
1722		let stp:Step = Step::new();
1723
1724		assert_eq!(&stp.text,stp.get_text());
1725		println!("struct Step / Result of get_text() -> {:?}",stp.get_text());
1726
1727		assert!(stp.get_resources().len()==0);
1728		let _is_right_type:&Vec<Resource> = stp.get_resources();
1729		println!("struct Step / Result of get_resources() -> [it's empty]");
1730
1731		assert!(stp.get_all_sub_steps().len()==0);
1732		let _is_right_type:&Vec<SubStep> = stp.get_all_sub_steps();
1733		println!("struct Step / Result of get_all_sub_steps() -> [it's empty]");
1734
1735	}
1736
1737	#[test]
1738	fn test_step_set_functions() {
1739
1740		let mut stp:Step = Step::new();
1741
1742		stp.set_text("SET_STEP_TEXT");
1743		stp.add_sub_step(SubStep::Warning("SET_STEP_SUB_STEP_WARNING".to_string()));
1744		stp.add_resource(Resource{name:"SET_STEP_SUB_STEP_RESOURCE".to_string(),calibration:false});
1745
1746	}
1747
1748	#[test]
1749	fn test_action_get_functions() {
1750
1751		let a:Action = parse_action_line("ACTION|EXPECTED|TPV_TEXT");
1752
1753		assert_eq!(&a.perform,a.get_perform());
1754		println!("struct Action / Result of get_perform() -> {:?}",a.get_perform());
1755
1756		assert_eq!(&a.expect,a.get_expect());
1757		println!("struct Action / Result of get_expect() -> {:?}",a.get_expect());
1758
1759		assert_eq!(&a.tpv,a.get_tpv());
1760		println!("struct Action / Result of get_tpv() -> {:?}",a.get_tpv());
1761
1762	}
1763
1764	#[test]
1765	fn test_requirement_get_functions() {
1766
1767		let r:Requirement = parse_verification_line("RID|REQ_TEXT|VER_METH");
1768
1769		assert_eq!(&r.id,r.get_id());
1770		println!("struct Requirement / Result of get_id() -> {:?}",r.get_id());
1771
1772		assert_eq!(&r.text,r.get_text());
1773		println!("struct Requirement / Result of get_text() -> {:?}",r.get_text());
1774
1775		assert_eq!("Sampling",r.get_method());
1776		println!("struct Requirement / Result of get_method() -> {:?}",r.get_method());
1777
1778	}
1779
1780	#[test]
1781	fn test_resource_get_functions() {
1782
1783		let res:Resource = Resource{name:"RESOURCE_NAME".to_string(),calibration:false};
1784
1785		assert_eq!(&res.name,res.get_name());
1786		println!("struct Resource / Result of get_name() -> {:?}",res.get_name());
1787
1788		assert_eq!(&res.calibration,res.get_calibration());
1789		println!("struct Resource / Result of get_calibration() -> {:?}",res.get_calibration());
1790
1791	}
1792
1793	#[test]
1794	fn test_read_file_blank() {
1795		let _is_right_type:Process = read_ebml(&"".to_string());
1796	}
1797
1798	// Some test-only functions for creating and destroying files to feed to read_ebml
1799
1800	fn create_test_file(filename:&str, lines:Vec<String>) {
1801		let mut new_file = OpenOptions::new()
1802		            .read(true)
1803		            .write(true)
1804		            .create(true)
1805		            .open(filename)
1806		            .expect("Could not open the file!");
1807        for line in lines {
1808        	new_file.write({line+"\n"}.as_bytes()).expect("Could not write line to test-only file!");
1809        }
1810	}
1811
1812	fn destroy_test_file(filename:&str) {
1813		let _ = fs::remove_file(filename);
1814	}
1815
1816	fn generate_ebml_with_diabolical_comments() -> Vec<String> {
1817		let mut new_vec_of_strings:Vec<String> = vec![];
1818		new_vec_of_strings.push("//".to_string());
1819		new_vec_of_strings.push("///".to_string());
1820		new_vec_of_strings.push("// /".to_string());
1821		new_vec_of_strings.push("/////////".to_string());
1822		new_vec_of_strings.push("//\\\\\\\\\\".to_string());
1823		new_vec_of_strings.push("\\\\Section|Section title".to_string());
1824		new_vec_of_strings.push("\n\n\n".to_string());
1825		new_vec_of_strings.push("//Section|This is a section! Maybe?".to_string());
1826		new_vec_of_strings.push("//Step|This is a step! Maybe?".to_string());
1827		new_vec_of_strings.push("\n\n\n".to_string());
1828		new_vec_of_strings.push("/ /".to_string());
1829		new_vec_of_strings.push("/Section/".to_string());
1830		new_vec_of_strings.push("/Section|Section title/".to_string());
1831		new_vec_of_strings.push("\n\n\n".to_string());
1832		new_vec_of_strings.push(" //".to_string());
1833		new_vec_of_strings.push(" / / / / / / / / ".to_string());
1834		return new_vec_of_strings;
1835	}
1836
1837	fn generate_ebml_with_1000_sections() -> Vec<String> {
1838		let mut new_vec_of_strings:Vec<String> = vec![];
1839		for ii in 0..1000 {
1840			new_vec_of_strings.push("  sEc tIo      N  |Section ".to_string()+&(ii+1).to_string());
1841		}
1842		return new_vec_of_strings;
1843	}
1844
1845	fn generate_ebml_with_1000_steps_in_one_section() -> Vec<String> {
1846		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1".to_string()];
1847		for ii in 0..1000 {
1848			new_vec_of_strings.push("  s T e P   |Step 1.".to_string()+&(ii+1).to_string());
1849		}
1850		return new_vec_of_strings;
1851	}
1852
1853	fn generate_ebml_with_1000_verifications_in_one_step() -> Vec<String> {
1854		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1855		for ii in 0..1000 {
1856			new_vec_of_strings.push("  vEr iFi cAt iOn   |R".to_string()+&(ii+1).to_string()+"|Requirement text|demo");
1857		}
1858		return new_vec_of_strings;
1859	}
1860
1861	fn generate_ebml_with_1000_resources_in_one_step() -> Vec<String> {
1862		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1863		for ii in 0..500 {
1864			new_vec_of_strings.push("  rEs oUr cE   |Really Important Tool #".to_string()+&(ii+1).to_string());
1865		}
1866		for ii in 500..1000 {
1867			new_vec_of_strings.push("  rEs oUr cE   |Really Important Tool #".to_string()+&(ii+1).to_string()+"|cal");
1868		}
1869		return new_vec_of_strings;
1870	}
1871
1872	fn generate_ebml_with_diabolical_calibrated_resources() -> Vec<String> {
1873		// "C"|"CAL"|"CALIB"|"CALIBRATE"|"CALIBRATED"|CALIBRATION"|"Y"|"YES"|"T"|"TRUE"
1874		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1875		new_vec_of_strings.push("Resource|Calibrated Resource|c    ".to_string());
1876		new_vec_of_strings.push("Resource|Calibrated Resource| c   ".to_string());
1877		new_vec_of_strings.push("Resource|Calibrated Resource|  c  ".to_string());
1878		new_vec_of_strings.push("Resource|Calibrated Resource|   c ".to_string());
1879		new_vec_of_strings.push("Resource|Calibrated Resource|    c".to_string());
1880		new_vec_of_strings.push("Resource|Calibrated Resource|C    ".to_string());
1881		new_vec_of_strings.push("Resource|Calibrated Resource| C   ".to_string());
1882		new_vec_of_strings.push("Resource|Calibrated Resource|  C  ".to_string());
1883		new_vec_of_strings.push("Resource|Calibrated Resource|   C ".to_string());
1884		new_vec_of_strings.push("Resource|Calibrated Resource|    C".to_string());
1885		new_vec_of_strings.push("Resource|Calibrated Resource| c    A    l    ".to_string());
1886		new_vec_of_strings.push("Resource|Calibrated Resource|CA    L".to_string());
1887		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b ".to_string());
1888		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b rate ".to_string());
1889		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b rate  d ".to_string());
1890		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b rat   ion ".to_string());
1891		new_vec_of_strings.push("Resource|Calibrated Resource|      y         ".to_string());
1892		new_vec_of_strings.push("Resource|Calibrated Resource|   y  e  s      ".to_string());
1893		new_vec_of_strings.push("Resource|Calibrated Resource|   t      ".to_string());
1894		new_vec_of_strings.push("Resource|Calibrated Resource|   t r u e   ".to_string());
1895		// 20 calibrated
1896
1897		// And... one that isn't. So... this process should have 20 calibrated (but 21 total resources...)
1898		new_vec_of_strings.push("Resource|Calibrated Resource| nopey dopey!!! ".to_string());
1899
1900		return new_vec_of_strings;
1901
1902	}
1903
1904	fn generate_ebml_with_1000_actions_in_one_step() -> Vec<String> {
1905		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1906		for _ii in 0..1000 {
1907			new_vec_of_strings.push(" a CT i o N      |Thing to do|Thing to Expect|TPV".to_string());
1908			//new_vec_of_strings.push("Action|Thing to do|Thing to Expect|TPV".to_string());
1909		}
1910		return new_vec_of_strings;
1911	}
1912
1913	fn generate_ebml_with_300_tpv_700_non_tpv_actions_in_one_step() -> Vec<String> {
1914		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1915		for _ii in 0..300 {
1916			new_vec_of_strings.push(" aC  tI  oN |Thing to do|Thing to Expect|TPV".to_string());
1917		}
1918		for _ii in 0..700 {
1919			new_vec_of_strings.push("actio      N|Thing to do|Thing to Expect".to_string());
1920		}
1921		return new_vec_of_strings;
1922	}
1923
1924	fn generate_ebml_with_1000_objectives_in_one_step() -> Vec<String> {
1925		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1926		for ii in 0..1000 {
1927			new_vec_of_strings.push("    o Bj   eCt  i   v   E    |This is the point of the procedure, number ".to_string()+&(ii+1).to_string());
1928		}
1929		return new_vec_of_strings;
1930	}
1931
1932	fn generate_ebml_with_1000_out_of_scopes_in_one_step() -> Vec<String> {
1933		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1934		for ii in 0..1000 {
1935			new_vec_of_strings.push("   oU To        FsCo P e  |This is yet another thing we DON'T do here, number ".to_string()+&(ii+1).to_string());
1936		}
1937		return new_vec_of_strings;
1938	}
1939
1940	fn generate_ebml_set_meta_twice() -> Vec<String> {
1941		let mut new_vec_of_strings:Vec<String> = vec![];
1942		new_vec_of_strings.push("Number|First Number".to_string());
1943		new_vec_of_strings.push("Number|Second Number".to_string());
1944		new_vec_of_strings.push("Title|First Title".to_string());
1945		new_vec_of_strings.push("Title|Second Title".to_string());
1946		new_vec_of_strings.push("Author|First Author".to_string());
1947		new_vec_of_strings.push("Author|Second Author".to_string());
1948		new_vec_of_strings.push("Reviewer|First Reviewer".to_string());
1949		new_vec_of_strings.push("Reviewer|Second Reviewer".to_string());
1950		new_vec_of_strings.push("Subject|First Subject".to_string());
1951		new_vec_of_strings.push("Subject|Second Subject".to_string());
1952		new_vec_of_strings.push("SubjectImage|First SubjectImage".to_string());
1953		new_vec_of_strings.push("SubjectImage|Second SubjectImage".to_string());
1954		new_vec_of_strings.push("Product|First Product".to_string());
1955		new_vec_of_strings.push("Product|Second Product".to_string());
1956		new_vec_of_strings.push("ProductImage|First ProductImage".to_string());
1957		new_vec_of_strings.push("ProductImage|Second ProductImage".to_string());
1958		new_vec_of_strings.push("ProcessType|First ProcessType".to_string());
1959		new_vec_of_strings.push("ProcessType|Second ProcessType".to_string());
1960		return new_vec_of_strings;
1961	}
1962
1963	fn generate_ebml_with_diabolical_whitespace_first_part() -> Vec<String> {
1964		let mut new_vec_of_strings:Vec<String> = vec![];
1965		new_vec_of_strings.push("Section|Section Title".to_string());
1966		new_vec_of_strings.push(" Section|Section Title".to_string());
1967		new_vec_of_strings.push("  Section|Section Title".to_string());
1968		new_vec_of_strings.push("Section |Section Title".to_string());
1969		new_vec_of_strings.push("Section  |Section Title".to_string());
1970		new_vec_of_strings.push(" Section |Section Title".to_string());
1971		new_vec_of_strings.push(" S e c t i o n |Section Title".to_string());
1972		new_vec_of_strings.push("Sec      ti        on       |Section Title".to_string());
1973		new_vec_of_strings.push(" S                    ection|Section Title".to_string());
1974		new_vec_of_strings.push("Section|Section Title".to_string());
1975		// 10 total
1976		return new_vec_of_strings;
1977	}
1978
1979	fn generate_ebml_with_diabolical_section_and_step_triggers() -> Vec<String> {
1980		let mut new_vec_of_strings:Vec<String> = vec![];
1981		// Three sections, each with three steps, let's get goofy AF
1982		new_vec_of_strings.push("Section |Section Title".to_string());
1983		new_vec_of_strings.push(" S t e p |Step Text".to_string());
1984		new_vec_of_strings.push(" A c t i o n |Do This|Expect This|TPV".to_string());
1985		new_vec_of_strings.push(" C o m m a n d |Command Text".to_string());
1986		new_vec_of_strings.push(" I m a g e |ImageFile.Ext|Image Caption".to_string());
1987		new_vec_of_strings.push(" W a r n i n g |Warning Text".to_string());
1988		new_vec_of_strings.push(" V e r i f i c a t i o n |RID|Req Text|T".to_string());
1989		new_vec_of_strings.push(" R e s o u r c e |Resource Text".to_string());
1990		new_vec_of_strings.push("St  ep   |Step Text".to_string());
1991		new_vec_of_strings.push("Com man d|Command Text".to_string());
1992		new_vec_of_strings.push("     Step|Step Text".to_string());
1993		new_vec_of_strings.push(" Command |Command Text".to_string());
1994		new_vec_of_strings.push(" S e c t i o n |Section Title".to_string());
1995		new_vec_of_strings.push("St ep|Step Text".to_string());
1996		new_vec_of_strings.push("C om ma n d|Command Text".to_string());
1997		new_vec_of_strings.push("St  ep|Step Text".to_string());
1998		new_vec_of_strings.push("Command          |Command Text".to_string());
1999		new_vec_of_strings.push("St   ep|Step Text".to_string());
2000		new_vec_of_strings.push(" command |Command Text".to_string());
2001		new_vec_of_strings.push("SECTION|Section Title".to_string());
2002		new_vec_of_strings.push("STEP|Step Text".to_string());
2003		new_vec_of_strings.push(" C O M M A N D|Command Text".to_string());
2004		new_vec_of_strings.push("S T E P |Step Text".to_string());
2005		new_vec_of_strings.push("   CO   MM   AND   |Command Text".to_string());
2006		new_vec_of_strings.push("ST   EP|Step Text".to_string());
2007		new_vec_of_strings.push(" c o MM a n D     |Command Text".to_string());
2008		return new_vec_of_strings;
2009	}
2010
2011	fn generate_ebml_with_diabolical_actions() -> Vec<String> {
2012		let mut new_vec_of_strings:Vec<String> = vec![];
2013		// One Section, three Steps, one ActionSequence apiece
2014		new_vec_of_strings.push("Section |The one and only section".to_string());
2015		new_vec_of_strings.push("Step|lots of bars".to_string());
2016		// ActionSequence should have length 6
2017		new_vec_of_strings.push("Action|Nominal|Nominal|TPV".to_string());
2018		new_vec_of_strings.push("Action|Nominal|Nominal|TPV|".to_string());
2019		new_vec_of_strings.push("Action|Nominal|Nominal|TPV||".to_string());
2020		new_vec_of_strings.push("Action|Nominal|Nominal|TPV|||".to_string());
2021		new_vec_of_strings.push("Action|Nominal|Nominal|TPV||||".to_string());
2022		new_vec_of_strings.push("Action|||TPV|||".to_string());
2023		new_vec_of_strings.push("Step|push the TPV limits - all should be true".to_string());
2024		// ActionSequence should have length 12
2025		new_vec_of_strings.push("Action|Nominal|Nominal|TPV".to_string());
2026		new_vec_of_strings.push("Action|Nominal|Nominal|tpv".to_string());
2027		new_vec_of_strings.push("Action|Nominal|Nominal|TRUE".to_string());
2028		new_vec_of_strings.push("Action|Nominal|Nominal|true".to_string());
2029		new_vec_of_strings.push("Action|Nominal|Nominal|T".to_string());
2030		new_vec_of_strings.push("Action|Nominal|Nominal|t".to_string());
2031		new_vec_of_strings.push("Action|Nominal|Nominal|Two Party Verification".to_string());
2032		new_vec_of_strings.push("Action|Nominal|Nominal|Y".to_string());
2033		new_vec_of_strings.push("Action|Nominal|Nominal|y".to_string());
2034		new_vec_of_strings.push("Action|Nominal|Nominal|Yes".to_string());
2035		new_vec_of_strings.push("Action|Nominal|Nominal|yes".to_string());
2036		new_vec_of_strings.push("Action|Nominal|Nominal|YES".to_string());
2037		new_vec_of_strings.push("Step|these TPVs should be false".to_string());
2038		// ActionSequence should have length 8
2039		new_vec_of_strings.push("Action|Nominal|TPV".to_string());
2040		new_vec_of_strings.push("Action|TPV".to_string());
2041		new_vec_of_strings.push("Action|Nominal|Nominal|naw|TPV".to_string());
2042		new_vec_of_strings.push("Action|Nominal|Nominal|naw|??|TPV".to_string());
2043		new_vec_of_strings.push("Action|Nominal|Nominal|naw|??|??|TPV".to_string());
2044		new_vec_of_strings.push("Action|Nominal|Nominal||??|??|TPV".to_string());
2045		new_vec_of_strings.push("Action|Nominal|Nominal|||??|TPV".to_string());
2046		new_vec_of_strings.push("Action|Nominal|Nominal||||TPV".to_string());
2047		return new_vec_of_strings;
2048	}
2049
2050	fn generate_ebml_with_diabolical_verification_methods() -> Vec<String> {
2051 		let mut new_vec_of_strings:Vec<String> = vec![];
2052		// One Section
2053		new_vec_of_strings.push("Section |The one and only section".to_string());
2054		new_vec_of_strings.push("Step|Analysis".to_string());
2055		// First step has 7 Verification SubSteps, all of them with VerificationMethod of Analysis
2056		new_vec_of_strings.push("Verification|A001|Analysis|Analysis".to_string());
2057		new_vec_of_strings.push("VERIFICATION|A002|Analysis|ANALYSIS".to_string());
2058		new_vec_of_strings.push("verification|A003|Analysis|analysis".to_string());
2059		new_vec_of_strings.push("v e r i f i c a t i o n |A004|Analysis| a n a l y s i s".to_string());
2060		new_vec_of_strings.push("v e r i f i c a t i o n |A005|Analysis| a n a l y s i s | test |demo|sampling|inspection".to_string());
2061		new_vec_of_strings.push("Verification|A006|Analysis|A".to_string());
2062		new_vec_of_strings.push("Verification|A007|Analysis|        a".to_string());
2063		new_vec_of_strings.push("Step|Inspection".to_string());
2064		// Second step has 7 Verification SubSteps, all of them with VerificationMethod of Inspection
2065		new_vec_of_strings.push("Verification|I001|Inspection|Inspection".to_string());
2066		new_vec_of_strings.push("VERIFICATION|I002|Inspection|INSPECTION".to_string());
2067		new_vec_of_strings.push("verification|I003|Inspection|inspection".to_string());
2068		new_vec_of_strings.push("v e r i f i c a t i o n |I004|Inspection| i n s p e c t i o n".to_string());
2069		new_vec_of_strings.push("v e r i f i c a t i o n |I005|Inspection| i n s p e c t i o n | test |demo|sampling|analysis".to_string());
2070		new_vec_of_strings.push("Verification|I006|Inspection|I".to_string());
2071		new_vec_of_strings.push("Verification|I007|Inspection|        i".to_string());
2072		new_vec_of_strings.push("Step|Test".to_string());
2073		// Third step has 7 Verification SubSteps, all of them with VerificationMethod of Test
2074		new_vec_of_strings.push("Verification|T001|Test|Test".to_string());
2075		new_vec_of_strings.push("VERIFICATION|T002|Test|TEST".to_string());
2076		new_vec_of_strings.push("verification|T003|Test|test".to_string());
2077		new_vec_of_strings.push("v e r i f i c a t i o n |T004|Test| t e s t".to_string());
2078		new_vec_of_strings.push("v e r i f i c a t i o n |T005|Test| t e s t | analysis |demo|sampling|inspection".to_string());
2079		new_vec_of_strings.push("Verification|T006|Test|T".to_string());
2080		new_vec_of_strings.push("Verification|T007|Test|        t".to_string());
2081		new_vec_of_strings.push("Step|Sampling".to_string());
2082		// Third step has 9 Verification SubSteps, all of them with VerificationMethod of Sampling
2083		new_vec_of_strings.push("Verification|S001|Sampling|Sampling".to_string());
2084		new_vec_of_strings.push("VERIFICATION|S002|Sampling|SAMPLING".to_string());
2085		new_vec_of_strings.push("verification|S003|Sampling|sampling".to_string());
2086		new_vec_of_strings.push("v e r i f i c a t i o n |S004|Sampling| s a m p l i n g".to_string());
2087		new_vec_of_strings.push("v e r i f i c a t i o n |S005|Sampling| s a m p l i n g | analysis |demo|TEst|inspection".to_string());
2088		new_vec_of_strings.push("Verification|S006|Sampling|S".to_string());
2089		new_vec_of_strings.push("Verification|S007|Sampling|        s".to_string());
2090		new_vec_of_strings.push("Verification|S008|Sampling| SAMPLE".to_string());
2091		new_vec_of_strings.push("Verification|S009|Sampling|        s a m PLE   ".to_string());
2092		new_vec_of_strings.push("Step|Demonstration".to_string());
2093		// Third step has 9 Verification SubSteps, all of them with VerificationMethod of Demonstration
2094		new_vec_of_strings.push("Verification|D001|Demonstration|Demonstration".to_string());
2095		new_vec_of_strings.push("VERIFICATION|D002|Demonstration|DEMONSTRATION".to_string());
2096		new_vec_of_strings.push("verification|D003|Demonstration|demonstration".to_string());
2097		new_vec_of_strings.push("v e r i f i c a t i o n |D004|Demonstration| d e m o n s t r a t i o n".to_string());
2098		new_vec_of_strings.push("v e r i f i c a t i o n |D005|Demonstration| d e m o n s t r a t i o n | analysis |test|sampling|inspection".to_string());
2099		new_vec_of_strings.push("Verification|D006|Demonstration|D".to_string());
2100		new_vec_of_strings.push("Verification|D007|Demonstration|        d".to_string());
2101		new_vec_of_strings.push("Verification|D007|Demonstration| DEMO".to_string());
2102		new_vec_of_strings.push("Verification|D007|Demonstration|        d EM o   ".to_string());
2103		/*
2104		"DEMONSTRATION"|"DEMO"|"D"	=> VerificationMethod::Demonstration,
2105		"INSPECTION"|"I"			=> VerificationMethod::Inspection,
2106		"ANALYSIS"|"A"				=> VerificationMethod::Analysis,
2107		"SAMPLING"|"SAMPLE"|"S"		=> VerificationMethod::Sampling,
2108		"TEST"|"T"					=> VerificationMethod::Test,
2109		*/
2110		return new_vec_of_strings;
2111	}
2112
2113	fn generate_ebml_with_diabolical_image_lines() -> Vec<String> {
2114		let mut new_vec_of_strings:Vec<String> = vec![];
2115		// One Section
2116		new_vec_of_strings.push("Section |The one and only section".to_string());
2117		new_vec_of_strings.push("Step|Strange three-part image lines".to_string());
2118		// First step has 5 Images
2119		new_vec_of_strings.push("Image|filename.ext|Caption".to_string());
2120		new_vec_of_strings.push("IMAGE|filename.ext|Caption".to_string());
2121		new_vec_of_strings.push("image|filename.ext|Caption".to_string());
2122		new_vec_of_strings.push("  iM  Ag     E   |filename.ext|Caption".to_string());
2123		new_vec_of_strings.push("           IMage |filename.ext|Caption".to_string());
2124
2125		new_vec_of_strings.push("Step|lots of bars, empty parts".to_string());
2126		// Second step has 5 Images, all of which should have default text for filename and Caption
2127		new_vec_of_strings.push("image |".to_string());
2128		new_vec_of_strings.push("image ||".to_string());
2129		new_vec_of_strings.push("image |||".to_string());
2130		new_vec_of_strings.push("image ||||".to_string());
2131		new_vec_of_strings.push("image |||||||||||||||".to_string());
2132
2133		new_vec_of_strings.push("Step|lots of bars, empty parts".to_string());
2134		// Third step has 5 Images, all of which should have default text for filename and Caption
2135		new_vec_of_strings.push("image |||filename.ext|Caption".to_string());
2136		new_vec_of_strings.push("image ||||filename.ext|Caption".to_string());
2137		new_vec_of_strings.push("image |||||filename.ext|Caption".to_string());
2138		new_vec_of_strings.push("image ||||||filename.ext|Caption".to_string());
2139		new_vec_of_strings.push("image |||||||filename.ext|Caption".to_string());
2140		return new_vec_of_strings;
2141	}
2142
2143	fn generate_ebml_with_diabolical_resources() -> Vec<String> {
2144		let mut new_vec_of_strings:Vec<String> = vec![];
2145		// One Section
2146		new_vec_of_strings.push("Section |The one and only section".to_string());
2147		new_vec_of_strings.push("Step|Strange resource lines".to_string());
2148		// First step has 5 Resources
2149		new_vec_of_strings.push("Resource|Nominal".to_string());
2150		new_vec_of_strings.push("RESOURCE|Nominal".to_string());
2151		new_vec_of_strings.push("resource|Nominal".to_string());
2152		new_vec_of_strings.push(" r e s o u r c e |Nominal".to_string());
2153		new_vec_of_strings.push(" rEs oUr cE      |Nominal".to_string());
2154		new_vec_of_strings.push("Step|Strange resource lines".to_string());
2155		// First step has 5 Resources
2156		new_vec_of_strings.push("Resource|".to_string());
2157		new_vec_of_strings.push("RESOURCE||".to_string());
2158		new_vec_of_strings.push("resource|||".to_string());
2159		new_vec_of_strings.push(" r e s o u r c e ||||".to_string());
2160		new_vec_of_strings.push(" rEs oUr cE      |||||".to_string());
2161		return new_vec_of_strings;
2162	}
2163
2164	fn generate_ebml_with_csv_table_embedded_1000_rows() -> Vec<String> {
2165		let mut new_vec_of_strings:Vec<String> = vec![];
2166		// One Section
2167		new_vec_of_strings.push("Section |The one and only section".to_string());
2168		new_vec_of_strings.push("Step|Strange resource lines".to_string());
2169		// First step has one embedded CSV table
2170		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2171		for _ in 0..1000 {
2172			new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2173		}
2174		new_vec_of_strings.push("CSV End |".to_string());
2175		return new_vec_of_strings;
2176	}
2177
2178	fn generate_ebml_with_csv_table_embedded_rows_wrong_lengths() -> Vec<String> {
2179		let mut new_vec_of_strings:Vec<String> = vec![];
2180		// One Section
2181		new_vec_of_strings.push("Section |The one and only section".to_string());
2182		new_vec_of_strings.push("Step|One and only step".to_string());
2183		// First step has one embedded CSV table
2184		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2185		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2186		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine".to_string());
2187		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight".to_string());
2188		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven".to_string());
2189		new_vec_of_strings.push("One,Two,Three,Four,Five,Six".to_string());
2190		new_vec_of_strings.push("One,Two,Three,Four,Five".to_string());
2191		new_vec_of_strings.push("One,Two,Three,Four".to_string());
2192		new_vec_of_strings.push("One,Two,Three".to_string());
2193		new_vec_of_strings.push("One,Two".to_string());
2194		new_vec_of_strings.push("One".to_string());
2195		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven".to_string());
2196		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve".to_string());
2197		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen".to_string());
2198		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen,Fourteen".to_string());
2199		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Twelve,Thirteen,Fourteen,Fifteen".to_string());
2200		new_vec_of_strings.push("CSV End |".to_string());
2201		return new_vec_of_strings;
2202	}
2203
2204	fn generate_ebml_with_csv_table_embedded_edge_cases() -> Vec<String> {
2205		let mut new_vec_of_strings:Vec<String> = vec![];
2206		// One Section
2207		new_vec_of_strings.push("Section |The one and only section".to_string());
2208		new_vec_of_strings.push("Step|One and only step".to_string());
2209		// First step has one embedded CSV table
2210		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2211		new_vec_of_strings.push("CSV Start | This is one effed-up CSV line, tell you what. It's meant to look like EBML, but it isn't!!!".to_string());
2212		new_vec_of_strings.push("Wait, so you're saying the line above is CSV and not EBML?".to_string());
2213		new_vec_of_strings.push("Yes, that's exactly what I'm saying. YOU are even a CSV line, my friend.".to_string());
2214		new_vec_of_strings.push("Me? You're saying that THIS is also a CSV line? If so, how many columns are in this line?".to_string());
2215		new_vec_of_strings.push("Two. You see, When you said, 'If so,' you used a comma. In fact, in this line alone I've used four.".to_string());
2216		new_vec_of_strings.push("CSV End |".to_string());
2217		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Second
2218		new_vec_of_strings.push("CSV End |".to_string());
2219		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Third
2220		new_vec_of_strings.push("CSV End |".to_string());
2221		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Fourth
2222		new_vec_of_strings.push("CSV End |".to_string());
2223		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Fifth
2224		new_vec_of_strings.push("CSV End |".to_string());
2225		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Sixth
2226		new_vec_of_strings.push("CSV End |".to_string());
2227		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Seventh
2228		new_vec_of_strings.push("".to_string());
2229		new_vec_of_strings.push("".to_string());
2230		new_vec_of_strings.push("".to_string());
2231		new_vec_of_strings.push("".to_string());
2232		new_vec_of_strings.push("".to_string());
2233		new_vec_of_strings.push("".to_string());
2234		new_vec_of_strings.push("".to_string());
2235		new_vec_of_strings.push("".to_string());
2236		new_vec_of_strings.push("CSV End |".to_string());
2237		new_vec_of_strings.push("Command | rm -rf lol".to_string());	 // Eigth SUBSTEP (first command)
2238		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Ninth SUBSTEP (eighth table...)
2239		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2240		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2241		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2242		new_vec_of_strings.push(",,,,CUCU,,,,,".to_string()); // That's the fifth column in the fourth row, of the ninth substep...
2243		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2244		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2245		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2246		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2247		new_vec_of_strings.push("CSV End |".to_string());
2248		return new_vec_of_strings;
2249	}
2250
2251	fn generate_ebml_with_csv_table_embedded_no_end_line() -> Vec<String> {
2252		let mut new_vec_of_strings:Vec<String> = vec![];
2253		// One Section
2254		new_vec_of_strings.push("Section |The one and only section".to_string());
2255		new_vec_of_strings.push("Step|One and only step".to_string());
2256		// First step has one embedded CSV table, but the "Start" line is missing
2257		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2258		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2259		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2260		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2261		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2262		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());	
2263		// new_vec_of_strings.push("CSV End |".to_string());
2264		new_vec_of_strings.push("WARNING | This SHOULD be picked up as a SubStep, even though the CSV lines below will be picked up in the SubStep above...".to_string());
2265		new_vec_of_strings.push("WARNING | This SHOULD be picked up as a SubStep, even though the CSV lines below will be picked up in the SubStep above...".to_string());
2266		new_vec_of_strings.push("WARNING | This SHOULD be picked up as a SubStep, even though the CSV lines below will be picked up in the SubStep above...".to_string());
2267		new_vec_of_strings.push("CSV Start | This SHOULD be read as a CSV line for the ONE table SubStep...".to_string());
2268		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2269		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2270		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2271		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2272		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2273		//new_vec_of_strings.push("CSV End |".to_string());
2274		return new_vec_of_strings;
2275	}
2276
2277	fn generate_ebml_with_csv_table_embedded_no_start_line() -> Vec<String> {
2278		let mut new_vec_of_strings:Vec<String> = vec![];
2279		// One Section
2280		new_vec_of_strings.push("Section |The one and only section".to_string());
2281		new_vec_of_strings.push("Step|One and only step".to_string());
2282		// First step has one embedded CSV table, but the "Start" line is missing
2283		//new_vec_of_strings.push("CSV Start | Caption text".to_string());
2284		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2285		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine".to_string());
2286		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight".to_string());
2287		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven".to_string());
2288		new_vec_of_strings.push("One,Two,Three,Four,Five,Six".to_string());
2289		new_vec_of_strings.push("One,Two,Three,Four,Five".to_string());
2290		new_vec_of_strings.push("One,Two,Three,Four".to_string());
2291		new_vec_of_strings.push("One,Two,Three".to_string());
2292		new_vec_of_strings.push("One,Two".to_string());
2293		new_vec_of_strings.push("One".to_string());
2294		new_vec_of_strings.push("Context | Since the CSV Start line is missing, this should be the first SubStep that registers... a Context line.".to_string());
2295		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven".to_string());
2296		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve".to_string());
2297		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen".to_string());
2298		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen,Fourteen".to_string());
2299		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Twelve,Thirteen,Fourteen,Fifteen".to_string());
2300		new_vec_of_strings.push("CSV End |".to_string());
2301		new_vec_of_strings.push("WARNING | This is the second SubStep that should be found...".to_string());
2302		return new_vec_of_strings;
2303	}
2304
2305	fn generate_ebml_with_csv_table_external() -> Vec<String> {
2306		let mut new_vec_of_strings:Vec<String> = vec![];
2307		// One Section
2308		new_vec_of_strings.push("Section |The one and only section".to_string());
2309		new_vec_of_strings.push("Step|One and only step".to_string());
2310		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
2311		new_vec_of_strings.push("WARNING | This is the second SubStep that should be found...".to_string());
2312		new_vec_of_strings.push("WARNING | This is the third SubStep that should be found...".to_string());
2313		new_vec_of_strings.push("WARNING | This is the fourth SubStep that should be found...".to_string());
2314		return new_vec_of_strings;
2315	}
2316
2317	fn generate_csv_table_external() -> Vec<String> {
2318		let mut new_vec_of_strings:Vec<String> = vec![];
2319		new_vec_of_strings.push("H1,H2,H3".to_string());
2320		new_vec_of_strings.push("D1,D2,D3".to_string());
2321		new_vec_of_strings.push("D4,D5,D6".to_string());
2322		new_vec_of_strings.push("D7,D8,D9".to_string());
2323		return new_vec_of_strings;
2324	}
2325
2326	fn generate_ebml_with_csv_table_external_stressing() -> Vec<String> {
2327		let mut new_vec_of_strings:Vec<String> = vec![];
2328		// One Section
2329		new_vec_of_strings.push("Section |The one and only section".to_string());
2330		new_vec_of_strings.push("Step|One and only step".to_string());
2331
2332		// First five valid SubStep lines: nominal... call an external file
2333		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
2334		new_vec_of_strings.push("      C S V  F i l e        |        test.csv            |           Caption text          ".to_string());
2335		new_vec_of_strings.push("csvfile|test.csv|Caption text".to_string());
2336		new_vec_of_strings.push("  cSvFiLe  |    test.csv| Caption text".to_string());
2337		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
2338
2339		// False SubSteps that won't be counted: typos
2340		new_vec_of_strings.push("CSVee File | test.csv | Caption text".to_string());
2341		new_vec_of_strings.push("CSV Flie | test.csv | Caption text".to_string());
2342		new_vec_of_strings.push("CSV Fille | test.csv | Caption text".to_string());
2343		new_vec_of_strings.push("CVS File | test.csv | Caption text".to_string());
2344		new_vec_of_strings.push("Cee Ess Vee File | test.csv | Caption text".to_string());
2345
2346		// Valid "embedded" CSV file call, with an "external" CSV call within it, which will be TWO SubSteps... but not more...
2347		// Furthermore, the "embedded" one will count the "external" CSV EBML line as one of its CSV lines... so it will have 3 rows...
2348		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2349		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2350		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
2351		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2352		new_vec_of_strings.push("CSV End |".to_string());
2353
2354		// Running totals:
2355		// One Section
2356		// One Step
2357		// Seven SubSteps, all tables
2358		// > 0-4 : (4,3)
2359		// > 5   : (3,10)
2360		// > 6   : (4,3)
2361		return new_vec_of_strings;
2362	}
2363	
2364	// end test-only functions	
2365	
2366	#[test]
2367	fn test_read_file_all_comments() {
2368		let file_name = "test_comments_only.ebml".to_string();
2369		create_test_file(&file_name,generate_ebml_with_diabolical_comments());
2370		let process = read_ebml(&file_name);
2371		assert_eq!(process.get_all_sections().len(),0);
2372		assert_eq!(process.get_all_resources().len(),0);
2373		assert_eq!(process.get_all_verifications().len(),0);
2374		assert_eq!(process.get_all_templates().len(),0);
2375		destroy_test_file(&file_name);
2376	}
2377
2378	#[test]
2379	fn test_set_process_meta_twice() {
2380		let file_name = "test_process_meta_set_twice.ebml".to_string();
2381		create_test_file(&file_name,generate_ebml_set_meta_twice());
2382		let process = read_ebml(&file_name);
2383		assert_eq!(process.get_number(),"Second Number");
2384		assert_eq!(process.get_title(),"Second Title");
2385		assert_eq!(process.get_author(),"Second Author");
2386		assert_eq!(process.get_reviewer(),"Second Reviewer");
2387		assert_eq!(process.get_subject(),"Second Subject");
2388		assert_eq!(process.get_subject_image(),"Second SubjectImage");
2389		assert_eq!(process.get_product(),"Second Product");
2390		assert_eq!(process.get_product_image(),"Second ProductImage");
2391		destroy_test_file(&file_name);
2392	}
2393
2394	#[test]
2395	fn test_read_file_1000_sections() {
2396		let file_name = "test_1000_sections.ebml".to_string();
2397		create_test_file(&file_name,generate_ebml_with_1000_sections());
2398		let process = read_ebml(&file_name);
2399		assert_eq!(process.get_all_sections().len(),1000);
2400		destroy_test_file(&file_name);
2401	}
2402
2403	#[test]
2404	fn test_read_file_1000_steps() {
2405		let file_name = "test_1000_steps.ebml".to_string();
2406		create_test_file(&file_name,generate_ebml_with_1000_steps_in_one_section());
2407		let process = read_ebml(&file_name);
2408		assert_eq!(process.get_all_sections().len(),1);
2409		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1000);
2410		destroy_test_file(&file_name);
2411	}
2412
2413	#[test]
2414	fn test_read_file_1000_verifications() {
2415		let file_name = "test_1000_verifications.ebml".to_string();
2416		create_test_file(&file_name,generate_ebml_with_1000_verifications_in_one_step());
2417		let process = read_ebml(&file_name);
2418		assert_eq!(process.get_all_sections().len(),1);
2419		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2420		assert_eq!(process.get_all_verifications().len(),1000);
2421		assert_eq!(process.get_all_verifications()[500].1,"Step 1.1");
2422		destroy_test_file(&file_name);
2423	}
2424
2425	#[test]
2426	fn test_read_file_1000_resources() {
2427		let file_name = "test_1000_resources.ebml".to_string();
2428		create_test_file(&file_name,generate_ebml_with_1000_resources_in_one_step());
2429		let process = read_ebml(&file_name);
2430		assert_eq!(process.get_all_sections().len(),1);
2431		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2432		assert_eq!(process.get_all_resources().len(),1000);
2433		assert_eq!(process.get_all_calibrated_resources().len(),500);
2434		assert_eq!(process.get_all_resources()[500].1,"Step 1.1");
2435		destroy_test_file(&file_name);
2436	}
2437
2438	#[test]
2439	fn test_read_file_1000_actions() {
2440		let file_name = "test_1000_actions.ebml".to_string();
2441		create_test_file(&file_name,generate_ebml_with_1000_actions_in_one_step());
2442		let process = read_ebml(&file_name);
2443		assert_eq!(process.get_all_sections().len(),1);
2444		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2445		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
2446		match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2447			SubStep::ActionSequence(list) => assert_eq!(list.len(),1000),
2448			_ => assert!(1==0),
2449		};		
2450		destroy_test_file(&file_name);
2451	}
2452
2453	#[test]
2454	fn test_read_file_300_tpv_700_non_tpv() {
2455		let file_name = "test_300_tpv_700_non_tpv.ebml".to_string();
2456		create_test_file(&file_name,generate_ebml_with_300_tpv_700_non_tpv_actions_in_one_step());
2457		let process = read_ebml(&file_name);
2458		assert_eq!(process.get_tpv_count(),300);
2459		assert_eq!(process.get_non_tpv_count(),700);
2460		destroy_test_file(&file_name);
2461	}
2462
2463	#[test]
2464	fn test_read_file_1000_objectives(){
2465		let file_name = "test_1000_objectives.ebml".to_string();
2466		create_test_file(&file_name,generate_ebml_with_1000_objectives_in_one_step());
2467		let process = read_ebml(&file_name);
2468		assert_eq!(process.get_all_sections().len(),1);
2469		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2470		assert_eq!(process.get_all_objectives().len(),1000);
2471		destroy_test_file(&file_name);
2472	}
2473
2474	#[test]
2475	fn test_read_file_1000_out_of_scopes(){
2476		let file_name = "test_1000_out_of_scopes.ebml".to_string();
2477		create_test_file(&file_name,generate_ebml_with_1000_out_of_scopes_in_one_step());
2478		let process = read_ebml(&file_name);
2479		assert_eq!(process.get_all_sections().len(),1);
2480		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2481		assert_eq!(process.get_all_out_of_scopes().len(),1000);
2482		destroy_test_file(&file_name);
2483	}
2484
2485	#[test]
2486	fn test_read_file_whitespace_first_part() {
2487		let file_name = "test_whitespace_first_part.ebml".to_string();
2488		create_test_file(&file_name,generate_ebml_with_diabolical_whitespace_first_part());
2489		let process = read_ebml(&file_name);
2490		assert_eq!(process.get_all_sections().len(),10);
2491		destroy_test_file(&file_name);
2492	}
2493
2494	#[test]
2495	fn test_extract_section_and_step_triggers() {
2496		let file_name = "test_extract_section_triggers.ebml".to_string();
2497		create_test_file(&file_name,generate_ebml_with_diabolical_section_and_step_triggers());
2498		let process = read_ebml(&file_name);
2499		// SHOULD BE: Three Sections, each with three Steps, but formatted hella goofy
2500		assert_eq!(process.get_all_sections().len(),3);
2501		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),3);
2502		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),6);
2503		assert_eq!(process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps().len(),1);
2504		assert_eq!(process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps().len(),1);
2505		assert_eq!(process.get_all_sections()[1].get_all_steps().len(),3);
2506		assert_eq!(process.get_all_sections()[1].get_all_steps()[0].get_all_sub_steps().len(),1);
2507		assert_eq!(process.get_all_sections()[1].get_all_steps()[1].get_all_sub_steps().len(),1);
2508		assert_eq!(process.get_all_sections()[1].get_all_steps()[2].get_all_sub_steps().len(),1);
2509		assert_eq!(process.get_all_sections()[2].get_all_steps().len(),3);
2510		assert_eq!(process.get_all_sections()[2].get_all_steps()[0].get_all_sub_steps().len(),1);
2511		assert_eq!(process.get_all_sections()[2].get_all_steps()[1].get_all_sub_steps().len(),1);
2512		assert_eq!(process.get_all_sections()[2].get_all_steps()[2].get_all_sub_steps().len(),1);
2513		destroy_test_file(&file_name);
2514	}
2515
2516	#[test]
2517	fn test_action_lines() {
2518		let file_name = "test_action_lines.ebml".to_string();
2519		create_test_file(&file_name,generate_ebml_with_diabolical_actions());
2520		let process = read_ebml(&file_name);
2521		assert_eq!(process.get_all_sections().len(),1);
2522		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),3);
2523		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
2524			match substep {
2525				SubStep::ActionSequence(list) => {
2526					assert_eq!(list.len(),6);
2527					for a in list { assert_eq!(a.get_tpv(),&true); }
2528				},
2529				_ => (),
2530			}
2531		}
2532		for substep in process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps() {
2533			match substep {
2534				SubStep::ActionSequence(list) => {
2535					assert_eq!(list.len(),12);
2536					for a in list { assert_eq!(a.get_tpv(),&true); }
2537				},
2538				_ => (),
2539			}
2540		}
2541		for substep in process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps() {
2542			match substep {
2543				SubStep::ActionSequence(list) => {
2544					assert_eq!(list.len(),8);
2545					for a in list { assert_eq!(a.get_tpv(),&false); }
2546				},
2547				_ => (),
2548			}
2549		}
2550		destroy_test_file(&file_name);
2551	}
2552
2553	#[test]
2554	fn test_verification_methods() {
2555		let file_name = "test_verification_methods.ebml".to_string();
2556		create_test_file(&file_name,generate_ebml_with_diabolical_verification_methods());
2557		let process = read_ebml(&file_name);
2558		assert_eq!(process.get_all_sections().len(),1);
2559		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),5);
2560		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() { match substep { SubStep::Verification(req) => match req.get_method().as_str() { "Analysis" => (), _ => assert!(1==0), }, _ => assert!(1==0),};}
2561		for substep in process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps() { match substep { SubStep::Verification(req) => match req.get_method().as_str() { "Inspection" => (), _ => assert!(1==0), }, _ => assert!(1==0),};}
2562		for substep in process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps() { match substep { SubStep::Verification(req) => match req.get_method().as_str() { "Test" => (), _ => assert!(1==0), }, _ => assert!(1==0),};}
2563		for substep in process.get_all_sections()[0].get_all_steps()[3].get_all_sub_steps() { match substep { SubStep::Verification(req) => match req.get_method().as_str() { "Sampling" => (), _ => assert!(1==0), }, _ => assert!(1==0),};}
2564		for substep in process.get_all_sections()[0].get_all_steps()[4].get_all_sub_steps() { match substep { SubStep::Verification(req) => match req.get_method().as_str() { "Demonstration" => (), _ => assert!(1==0), }, _ => assert!(1==0),};}
2565		destroy_test_file(&file_name);
2566	}
2567
2568	#[test]
2569	fn test_image_lines() {
2570		let file_name = "test_image_lines.ebml".to_string();
2571		create_test_file(&file_name,generate_ebml_with_diabolical_image_lines());
2572		let process = read_ebml(&file_name);
2573		// SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string())
2574		assert_eq!(process.get_all_sections().len(),1);
2575		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),3);
2576		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),5);
2577		assert_eq!(process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps().len(),5);
2578		assert_eq!(process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps().len(),5);
2579		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() { 
2580			match substep {
2581				SubStep::Image(f,c) => {
2582					match f.as_str() {"filename.ext" => (), _ => assert!(1==0),};
2583					match c.as_str() {"Caption" => (), _ => assert!(1==0),};
2584				},
2585				_ => assert!(1==0),
2586			};
2587		};
2588		for substep in process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps() { 
2589			match substep {
2590				SubStep::Image(f,c) => {
2591					match f.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2592					match c.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2593				},
2594				_ => assert!(1==0),
2595			};
2596		};
2597		for substep in process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps() { 
2598			match substep {
2599				SubStep::Image(f,c) => {
2600					match f.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2601					match c.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2602				},
2603				_ => assert!(1==0),
2604			};
2605		};
2606		
2607		destroy_test_file(&file_name);
2608	}
2609
2610	#[test]
2611	fn test_resource_lines() {
2612		let file_name = "test_resource_lines.ebml".to_string();
2613		create_test_file(&file_name,generate_ebml_with_diabolical_resources());
2614		let process = read_ebml(&file_name);
2615		assert_eq!(process.get_all_sections().len(),1);
2616		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),2);
2617		let mut count_one_point_one = 0;
2618		let mut count_one_point_two = 0;
2619		for (resource,stepno) in process.get_all_resources() {
2620			match stepno.as_str() {
2621				"Step 1.1"	=> {
2622					count_one_point_one +=1;
2623					assert_eq!(resource.get_name().as_str(),"Nominal");
2624				},
2625				"Step 1.2"	=> {
2626					count_one_point_two +=1;
2627					assert_eq!(resource.get_name().as_str(),"ERROR: NO RESOURCE IDENTIFIED");
2628				},
2629				_ 			=> assert!(1==0),
2630			};
2631		}
2632		assert_eq!(count_one_point_one,5);
2633		assert_eq!(count_one_point_two,5);
2634
2635		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_resources().len(),5);
2636		for resource in process.get_all_sections()[0].get_all_steps()[0].get_resources() {
2637			assert_eq!(resource.get_name().as_str(),"Nominal");
2638		}
2639		assert_eq!(process.get_all_sections()[0].get_all_steps()[1].get_resources().len(),5);
2640		for resource in process.get_all_sections()[0].get_all_steps()[1].get_resources() {
2641			assert_eq!(resource.get_name().as_str(),"ERROR: NO RESOURCE IDENTIFIED");
2642		}
2643		destroy_test_file(&file_name);
2644	}
2645
2646	#[test]
2647	fn test_calibrated_resource_lines() {
2648		let file_name = "test_calibrated_resource_lines.ebml".to_string();
2649		create_test_file(&file_name,generate_ebml_with_diabolical_calibrated_resources());
2650		let process = read_ebml(&file_name);
2651		assert_eq!(process.get_all_sections().len(),1);
2652		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2653		assert_eq!(process.get_all_resources().len(),21);
2654		assert_eq!(process.get_all_calibrated_resources().len(),20);
2655		destroy_test_file(&file_name);
2656	}
2657
2658	#[test]
2659	fn test_get_all_commands_count() {
2660		let file_name = "test_get_all_commands_count.ebml".to_string();
2661		create_test_file(&file_name,generate_ebml_with_diabolical_section_and_step_triggers());
2662		let process = read_ebml(&file_name);
2663		// SHOULD BE: Three Sections, each with three Steps, and a total of 9 command lines
2664		assert_eq!(process.get_all_command_lines().len(),9);
2665		destroy_test_file(&file_name);
2666	}
2667
2668	#[test]
2669	fn test_csv_table_embedded_1000_rows() {
2670		let file_name = "test_csv_table_embedded_1000_rows.ebml".to_string();
2671		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_1000_rows());
2672		let process = read_ebml(&file_name);
2673		assert_eq!(process.get_all_sections().len(),1);
2674		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2675		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
2676		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2677			SubStep::Table(table) => table,
2678			_ => &Table::new(),
2679		};
2680		let (rows,cols) = table.get_size();
2681		assert_eq!(rows,1000);
2682		assert_eq!(cols,10);
2683		destroy_test_file(&file_name);
2684	}
2685
2686	#[test]
2687	fn test_csv_table_embedded_data_rows_wrong_lengths() {
2688		let file_name = "test_csv_table_embedded_rows_wrong_lengths.ebml".to_string();
2689		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_rows_wrong_lengths());
2690		let process = read_ebml(&file_name);
2691		assert_eq!(process.get_all_sections().len(),1);
2692		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2693		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
2694		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2695			SubStep::Table(table) => table,
2696			_ => &Table::new(),
2697		};
2698		let (rows,cols) = table.get_size();
2699		assert_eq!(rows,15);
2700		assert_eq!(cols,10);
2701		assert_eq!(table.get_row(1)[9],"".to_string());
2702		assert_eq!(table.get_row(9)[1],"".to_string());
2703		assert_eq!(table.get_row(14)[9],"Ten".to_string());
2704		destroy_test_file(&file_name);
2705	}
2706
2707	#[test]
2708	fn test_csv_table_embedded_edge_cases() {
2709		let file_name = "test_csv_table_embedded_edge_cases.ebml".to_string();
2710		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_edge_cases());
2711		let process = read_ebml(&file_name);
2712		assert_eq!(process.get_all_sections().len(),1);
2713		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2714		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),9);
2715		
2716		// First table: 
2717		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2718			SubStep::Table(table) => table,
2719			_ => &Table::new(),
2720		};
2721		let (rows,cols) = table.get_size();
2722		assert_eq!(rows,5);
2723		assert_eq!(cols,3);
2724		
2725		// Second table: 
2726		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] {
2727			SubStep::Table(table) => table,
2728			_ => &Table::new(),
2729		};
2730		let (rows,cols) = table.get_size();
2731		assert_eq!(rows,0);
2732		assert_eq!(cols,0);
2733
2734		// Seventh table: 
2735		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[6] {
2736			SubStep::Table(table) => table,
2737			_ => &Table::new(),
2738		};
2739		let (rows,cols) = table.get_size();
2740		assert_eq!(rows,8);
2741		assert_eq!(cols,1);
2742		assert_eq!(table.get_caption(),"Caption text");
2743
2744		// Ninth SubStep is the Eighth table...: 
2745		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[8] {
2746			SubStep::Table(table) => table,
2747			_ => &Table::new(),
2748		};
2749		let (rows,cols) = table.get_size();
2750		assert_eq!(rows,8);
2751		assert_eq!(cols,14);
2752		assert_eq!(table.get_caption(),"Caption text");
2753		assert_eq!(table.get_row(3)[4],"CUCU".to_string());
2754		assert_eq!(table.get_row(0)[0],"".to_string());
2755
2756		destroy_test_file(&file_name);
2757	}
2758
2759	#[test]
2760	fn test_csv_table_embedded_no_end_line() {
2761		let file_name = "test_csv_table_embedded_no_end_line.ebml".to_string();
2762		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_no_end_line());
2763		let process = read_ebml(&file_name);
2764		assert_eq!(process.get_all_sections().len(),1);
2765		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2766		// The number of SubSteps is tricky here... if working as expected, we should have:
2767		// > ONE Table as the first SubStep
2768		// > Three Warning SubSteps
2769		// So four SubSteps total...
2770		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),4);
2771		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2772			SubStep::Table(table) => table,
2773			_ => &Table::new(),
2774		};
2775		let (rows,cols) = table.get_size();
2776		// The number of rows in this ONE table is tricky... because the user looks like they wanted two tables, with three warnings in between.
2777		// If working as expected, we should have:
2778		// > 5 intended CSV rows
2779		// > 3 Warning rows that are unintentionally read in as CSV rows because the CSV End is missing
2780		// > 1 CSV Start line following the warnings, read in as CSV unintentionally
2781		// > 5 intended CSV rows, but intended for a second table
2782		// So 14 rows, even though 5 are intended. The table should be read even though there is NO ending line in the file at all...
2783		assert_eq!(rows,14);
2784		assert_eq!(cols,10);
2785		destroy_test_file(&file_name);		
2786	}
2787
2788	#[test]
2789	fn test_csv_table_embedded_no_start_line() {
2790		let file_name = "test_csv_table_embedded_no_start_line.ebml".to_string();
2791		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_no_start_line());
2792		let process = read_ebml(&file_name);
2793		assert_eq!(process.get_all_sections().len(),1);
2794		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2795		// The number of SubSteps is tricky here... if working as expected, we should have:
2796		// > ZERO tables...
2797		// > ONE Context
2798		// > ONE Warning
2799		// So two SubSteps, but neither of them should be Table
2800		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),2);
2801		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] { SubStep::Context(_) => true, _ => false, });
2802		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] { SubStep::Warning(_) => true, _ => false, });
2803		destroy_test_file(&file_name);
2804	}
2805
2806	#[test]
2807	fn test_csv_table_external() {
2808		let file_name_ebml = "././test_csv_table_external.ebml".to_string();
2809		create_test_file(&file_name_ebml,generate_ebml_with_csv_table_external());
2810		
2811		let file_name_csv = "././test.csv".to_string();
2812		create_test_file(&file_name_csv,generate_csv_table_external());		
2813
2814		let process = read_ebml(&file_name_ebml);
2815		
2816		assert_eq!(process.get_all_sections().len(),1);
2817		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2818		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),4);
2819		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] { SubStep::Table(_) => true, _ => false, });
2820		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2821			SubStep::Table(table) => table,
2822			_ => &Table::new(),
2823		};
2824		let (rows,cols) = table.get_size();
2825		assert_eq!(rows,4);
2826		assert_eq!(cols,3);
2827		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] { SubStep::Warning(_) => true, _ => false, });
2828		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[2] { SubStep::Warning(_) => true, _ => false, });
2829		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[3] { SubStep::Warning(_) => true, _ => false, });
2830		
2831		destroy_test_file(&file_name_ebml);
2832		destroy_test_file(&file_name_csv);
2833	}
2834
2835	#[test]
2836	fn test_csv_table_external_stressing() {
2837		let file_name_ebml = "././test_csv_table_external_stressing.ebml".to_string();
2838		create_test_file(&file_name_ebml,generate_ebml_with_csv_table_external_stressing());
2839		
2840		let file_name_csv = "././test.csv".to_string();
2841		create_test_file(&file_name_csv,generate_csv_table_external());		
2842
2843		let process = read_ebml(&file_name_ebml);
2844
2845		// Running totals:
2846		// One Section
2847		// One Step
2848		// Seven SubSteps, all tables
2849		// > 0-4 : (4,3)
2850		// > 5   : (3,10)
2851		// > 6   : (4,3)
2852		assert_eq!(process.get_all_sections().len(),1);
2853		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2854		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),7);
2855
2856		// 0
2857		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] { SubStep::Table(_) => true, _ => false, });
2858		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2859			SubStep::Table(table) => table,
2860			_ => &Table::new(),
2861		};
2862		let (rows,cols) = table.get_size();
2863		assert_eq!(rows,4);
2864		assert_eq!(cols,3);
2865
2866		// 1
2867		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] { SubStep::Table(_) => true, _ => false, });
2868		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] {
2869			SubStep::Table(table) => table,
2870			_ => &Table::new(),
2871		};
2872		let (rows,cols) = table.get_size();
2873		assert_eq!(rows,4);
2874		assert_eq!(cols,3);
2875
2876		// 2
2877		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[2] { SubStep::Table(_) => true, _ => false, });
2878		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[2] {
2879			SubStep::Table(table) => table,
2880			_ => &Table::new(),
2881		};
2882		let (rows,cols) = table.get_size();
2883		assert_eq!(rows,4);
2884		assert_eq!(cols,3);
2885
2886		// 3
2887		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[3] { SubStep::Table(_) => true, _ => false, });
2888		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[3] {
2889			SubStep::Table(table) => table,
2890			_ => &Table::new(),
2891		};
2892		let (rows,cols) = table.get_size();
2893		assert_eq!(rows,4);
2894		assert_eq!(cols,3);
2895
2896		// 4
2897		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[4] { SubStep::Table(_) => true, _ => false, });
2898		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[4] {
2899			SubStep::Table(table) => table,
2900			_ => &Table::new(),
2901		};
2902		let (rows,cols) = table.get_size();
2903		assert_eq!(rows,4);
2904		assert_eq!(cols,3);
2905
2906		// 5
2907		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[5] { SubStep::Table(_) => true, _ => false, });
2908		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[5] {
2909			SubStep::Table(table) => table,
2910			_ => &Table::new(),
2911		};
2912		let (rows,cols) = table.get_size();
2913		assert_eq!(rows,3);
2914		assert_eq!(cols,10);
2915
2916		// 6
2917		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[6] { SubStep::Table(_) => true, _ => false, });
2918		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[6] {
2919			SubStep::Table(table) => table,
2920			_ => &Table::new(),
2921		};
2922		let (rows,cols) = table.get_size();
2923		assert_eq!(rows,4);
2924		assert_eq!(cols,3);
2925
2926		destroy_test_file(&file_name_ebml);
2927		destroy_test_file(&file_name_csv);
2928	}
2929
2930	// helper functions to generate ebml for new aliases added >0.80.0
2931
2932	fn generate_ebml_with_section_alias() -> Vec<String> {
2933		let mut new_vec_of_strings:Vec<String> = vec![];
2934		new_vec_of_strings.push("Section|Old-style Section".to_string());
2935		new_vec_of_strings.push("SEC|Alias Section".to_string());
2936		new_vec_of_strings.push(" s e c |Alias Section".to_string());
2937		new_vec_of_strings.push(" SE   c|Alias Section".to_string());
2938		return new_vec_of_strings;
2939		// 4 sections (1 old-style, 3 new alias)
2940	}
2941
2942	fn generate_ebml_with_section_reference_alias() -> Vec<String> {
2943		let mut new_vec_of_strings:Vec<String> = vec![];
2944		new_vec_of_strings.push("Section Reference|Old-style Section Reference|Section Title".to_string());
2945		new_vec_of_strings.push("SECREF|Alias Section|Section Title".to_string());
2946		new_vec_of_strings.push(" s e c r e f |Alias Section|Section Title".to_string());
2947		new_vec_of_strings.push(" SE   c   RE   f   |      Alias Section     | Section Title".to_string());
2948		return new_vec_of_strings;
2949		// 4 sections references (1 old-style, 3 new alias)
2950	}
2951
2952	fn generate_ebml_with_step_alias() -> Vec<String> {
2953		let mut new_vec_of_strings:Vec<String> = vec![];
2954		new_vec_of_strings.push("Section|One section".to_string());
2955		new_vec_of_strings.push("Step|Old-style Step".to_string());
2956		new_vec_of_strings.push("STP|Alias Step".to_string());
2957		new_vec_of_strings.push(" s t p |Alias Step".to_string());
2958		new_vec_of_strings.push(" ST    p|Alias Step".to_string());
2959		return new_vec_of_strings;
2960		// 1 section, 4 steps (1 old-style, 3 new alias)
2961	}
2962
2963	fn generate_ebml_with_context_alias() -> Vec<String> {
2964		let mut new_vec_of_strings:Vec<String> = vec![];
2965		new_vec_of_strings.push("Section|One section".to_string());
2966		new_vec_of_strings.push("Step|One step".to_string());
2967		new_vec_of_strings.push("Context|Old-style Context".to_string());
2968		new_vec_of_strings.push("Comment|Alias Context".to_string());
2969		new_vec_of_strings.push(" c o m m e n t |Alias Context".to_string());
2970		new_vec_of_strings.push("TXT|Alias Context".to_string());
2971		new_vec_of_strings.push(" t x t |Alias Context".to_string());
2972		new_vec_of_strings.push("CMT|Alias Context".to_string());
2973		new_vec_of_strings.push(" c m t |Alias Context".to_string());
2974		return new_vec_of_strings;
2975		// 1 section, 1 step, 7 substeps that are all Context (1 old-style, 6 new alias)
2976	}
2977
2978	fn generate_ebml_with_command_alias() -> Vec<String> {
2979		let mut new_vec_of_strings:Vec<String> = vec![];
2980		new_vec_of_strings.push("Section|One section".to_string());
2981		new_vec_of_strings.push("Step|One step".to_string());
2982		new_vec_of_strings.push("Command|Old-style Command".to_string());
2983		new_vec_of_strings.push("CMD|Alias Command".to_string());
2984		new_vec_of_strings.push(" c m d |Alias Command".to_string());
2985		new_vec_of_strings.push(" > |Alias Command".to_string());
2986		new_vec_of_strings.push("    %     |Alias Command".to_string());
2987		new_vec_of_strings.push("  $    |Alias Command".to_string());
2988		new_vec_of_strings.push("#    |Alias Command".to_string());
2989		return new_vec_of_strings;
2990		// 1 section, 1 step, 7 substeps that are all Command (1 old-style, 6 new alias)
2991	}
2992
2993	fn generate_ebml_with_image_alias() -> Vec<String> {
2994		let mut new_vec_of_strings:Vec<String> = vec![];
2995		new_vec_of_strings.push("Section|One section".to_string());
2996		new_vec_of_strings.push("Step|One step".to_string());
2997		new_vec_of_strings.push("Image|Old-style Image filename|Old-style Image caption".to_string());
2998		new_vec_of_strings.push("IMG|Alias Image filename|Alias Image caption".to_string());
2999		new_vec_of_strings.push(" i m g |Alias Image filename|Alias Image caption".to_string());
3000		new_vec_of_strings.push("PICTURE|Alias Image filename|Alias Image caption".to_string());
3001		new_vec_of_strings.push(" p i c t u r e |Alias Image filename|Alias Image caption".to_string());
3002		new_vec_of_strings.push("PIC|Alias Image filename|Alias Image caption".to_string());
3003		new_vec_of_strings.push(" p i c |Alias Image filename|Alias Image caption".to_string());
3004		new_vec_of_strings.push("FIGURE|Alias Image filename|Alias Image caption".to_string());
3005		new_vec_of_strings.push(" f i g u r e |Alias Image filename|Alias Image caption".to_string());
3006		new_vec_of_strings.push("FIG|Alias Image filename|Alias Image caption".to_string());
3007		new_vec_of_strings.push(" f i g |Alias Image filename|Alias Image caption".to_string());
3008		return new_vec_of_strings;
3009		// 1 section, 1 step, 11 substeps that are all Image (1 old-style, 10 new alias)
3010	}
3011
3012	fn generate_ebml_with_action_alias() -> Vec<String> {
3013		let mut new_vec_of_strings:Vec<String> = vec![];
3014		new_vec_of_strings.push("Section|One section".to_string());
3015		new_vec_of_strings.push("Step|One step".to_string());
3016		new_vec_of_strings.push("Action|Old-style Action|Old-style Expectation|Old-style TPV".to_string());
3017		new_vec_of_strings.push("DO|Alias Action|Alias Expectation|Alias TPV".to_string());
3018		new_vec_of_strings.push(" d o |Alias Action|Alias Expectation|Alias TPV".to_string());
3019		new_vec_of_strings.push("  D    o |Alias Action|Alias Expectation|Alias TPV".to_string());
3020		new_vec_of_strings.push(" dO |Alias Action|Alias Expectation|Alias TPV".to_string());
3021		return new_vec_of_strings;
3022		// 1 section, 1 step, 1 substep that is an ActionSequence of length 5 (1 old-style, 4 new alias)
3023	}
3024
3025	fn generate_ebml_with_warning_alias() -> Vec<String> {
3026		let mut new_vec_of_strings:Vec<String> = vec![];
3027		new_vec_of_strings.push("Section|One section".to_string());
3028		new_vec_of_strings.push("Step|One step".to_string());
3029		new_vec_of_strings.push("Warning|Old-style Warning".to_string());
3030		new_vec_of_strings.push("WARN|Alias Warning".to_string());
3031		new_vec_of_strings.push(" w a r n |Alias Warning".to_string());
3032		new_vec_of_strings.push("WRN|Alias Warning".to_string());
3033		new_vec_of_strings.push(" w r n |Alias Warning".to_string());
3034		new_vec_of_strings.push("WAR|Alias Warning".to_string());
3035		new_vec_of_strings.push(" w a r |Alias Warning".to_string());
3036		new_vec_of_strings.push("ALERT|Alias Warning".to_string());
3037		new_vec_of_strings.push(" a l e r t |Alias Warning".to_string());
3038		new_vec_of_strings.push("  !  |Alias Warning".to_string());
3039		return new_vec_of_strings;
3040		// 1 section, 1 step, 10 substeps that are all Warnings (1 old-style, 9 new alias)
3041	}
3042
3043	fn generate_ebml_with_verification_alias() -> Vec<String> {
3044		let mut new_vec_of_strings:Vec<String> = vec![];
3045		new_vec_of_strings.push("Section|One section".to_string());
3046		new_vec_of_strings.push("Step|One step".to_string());
3047		new_vec_of_strings.push("Verification|Old-style Verification ReqID|Old-style Verification Text|Method".to_string());
3048		new_vec_of_strings.push("VER|Alias Verification ReqID|Alias Verification Text|Method".to_string());
3049		new_vec_of_strings.push(" v e r |Alias Verification ReqID|Alias Verification Text|Method".to_string());
3050		new_vec_of_strings.push("REQUIREMENT|Alias Verification ReqID|Alias Verification Text|Method".to_string());
3051		new_vec_of_strings.push(" r e q u i r e m e n t |Alias Verification ReqID|Alias Verification Text|Method".to_string());
3052		new_vec_of_strings.push("REQ|Alias Verification ReqID|Alias Verification Text|Method".to_string());
3053		new_vec_of_strings.push(" r e q |Alias Verification ReqID|Alias Verification Text|Method".to_string());
3054		return new_vec_of_strings;
3055		// 1 section, 1 step, 7 substeps that are all Verifications (1 old-style, 6 new alias)
3056	}
3057
3058	fn generate_ebml_with_resource_alias() -> Vec<String> {
3059		let mut new_vec_of_strings:Vec<String> = vec![];
3060		new_vec_of_strings.push("Section|One section".to_string());
3061		new_vec_of_strings.push("Step|One step".to_string());
3062		new_vec_of_strings.push("Resource|Old-style Resource|Old-style Calibration".to_string());
3063		new_vec_of_strings.push("RES|Alias Resource|Alias Calibration".to_string());
3064		new_vec_of_strings.push(" r e s |Alias Resource|Alias Calibration".to_string());
3065		return new_vec_of_strings;
3066		// 1 section, 1 step, 3 substeps that are all Resources (1 old-style, 2 new alias)
3067	}
3068
3069	fn generate_ebml_with_objective_alias() -> Vec<String> {
3070		let mut new_vec_of_strings:Vec<String> = vec![];
3071		new_vec_of_strings.push("Section|One section".to_string());
3072		new_vec_of_strings.push("Step|One step".to_string());
3073		new_vec_of_strings.push("Objective|Old-style Objective".to_string());
3074		new_vec_of_strings.push("OBJ|Alias Objective".to_string());
3075		new_vec_of_strings.push(" o b j |Alias Objective".to_string());
3076		return new_vec_of_strings;
3077		// 1 section, 1 step, 3 substeps that are all Objectives (1 old-style, 2 new alias)
3078	}
3079
3080	fn generate_ebml_with_out_of_scope_alias() -> Vec<String> {
3081		let mut new_vec_of_strings:Vec<String> = vec![];
3082		new_vec_of_strings.push("Section|One section".to_string());
3083		new_vec_of_strings.push("Step|One step".to_string());
3084		new_vec_of_strings.push("Out of Scope|Old-style Out of Scope".to_string());
3085		new_vec_of_strings.push("OOS|Alias Objective".to_string());
3086		new_vec_of_strings.push(" o o s |Alias Out of Scope".to_string());
3087		return new_vec_of_strings;
3088		// 1 section, 1 step, 3 substeps that are all Out of Scopes (1 old-style, 2 new alias)
3089	}
3090
3091	fn generate_ebml_with_revision_alias() -> Vec<String> {
3092		let mut new_vec_of_strings:Vec<String> = vec![];
3093		new_vec_of_strings.push(" Revision |      Successful Revision      |    Successful Description       ".to_string());
3094		new_vec_of_strings.push(" REV |      Successful Revision      |    Successful Description       ".to_string());
3095		new_vec_of_strings.push(" r e v |      Successful Revision      |    Successful Description       ".to_string());
3096		new_vec_of_strings.push("Section|One section".to_string());
3097		new_vec_of_strings.push("Step|One step".to_string());
3098		return new_vec_of_strings;
3099		// 3 Revisions, each of which should be Rev "Successful Revision" with reason of "Successful Description"
3100	}
3101
3102	fn generate_ebml_with_template_alias() -> Vec<String> {
3103		let mut new_vec_of_strings:Vec<String> = vec![];
3104		new_vec_of_strings.push("Template | Nominal.css".to_string());
3105		new_vec_of_strings.push("CSS|Alias.css".to_string());
3106		new_vec_of_strings.push(" c s s |Alias.css".to_string());
3107		return new_vec_of_strings;
3108		// 3 Templates, one old-style and 2 alias
3109	}
3110
3111	// use the helper functions, and confirm that EBML intent is read correctly
3112
3113	#[test]
3114	fn test_section_alias() {
3115		let file_name = "test_section_alias.ebml".to_string();
3116		create_test_file(&file_name,generate_ebml_with_section_alias());
3117		let process = read_ebml(&file_name);
3118		assert_eq!(process.get_all_sections().len(),4);
3119		destroy_test_file(&file_name);
3120	}
3121
3122	#[test]
3123	fn test_step_alias() {
3124		let file_name = "test_step_alias.ebml".to_string();
3125		create_test_file(&file_name,generate_ebml_with_step_alias());
3126		let process = read_ebml(&file_name);
3127		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),4);
3128		destroy_test_file(&file_name);
3129	}
3130
3131	#[test]
3132	fn test_context_alias() {
3133		let file_name = "test_context_alias.ebml".to_string();
3134		create_test_file(&file_name,generate_ebml_with_context_alias());
3135		let process = read_ebml(&file_name);
3136		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),7);
3137		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3138			match substep { SubStep::Context(..) => println!(">>Context line found, as expected!"), _ => panic!(">>Should be a 'Context'")};
3139		}
3140		destroy_test_file(&file_name);
3141	}
3142
3143	#[test]
3144	fn test_command_alias() {
3145		let file_name = "test_command_alias.ebml".to_string();
3146		create_test_file(&file_name,generate_ebml_with_command_alias());
3147		let process = read_ebml(&file_name);
3148		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),7);
3149		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3150			match substep { SubStep::Command(..) => println!(">>Command line found, as expected!"), _ => panic!(">>Should be a 'Command'")};
3151		}
3152		destroy_test_file(&file_name);
3153	}
3154
3155	#[test]
3156	fn test_image_alias() {
3157		let file_name = "test_image_alias.ebml".to_string();
3158		create_test_file(&file_name,generate_ebml_with_image_alias());
3159		let process = read_ebml(&file_name);
3160		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),11);
3161		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3162			match substep { SubStep::Image(..) => println!(">>Image line found, as expected!"), _ => panic!(">>Should be a 'Image'")};
3163		}
3164		destroy_test_file(&file_name);
3165	}
3166
3167	#[test]
3168	fn test_action_alias() {
3169		let file_name = "test_action_alias.ebml".to_string();
3170		create_test_file(&file_name,generate_ebml_with_action_alias());
3171		let process = read_ebml(&file_name);
3172		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
3173		match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
3174			SubStep::ActionSequence(list) => assert_eq!(list.len(),5),
3175			_ => panic!(">>Should be a 'ActionSequence'"),
3176		};
3177		destroy_test_file(&file_name);
3178	}
3179
3180	#[test]
3181	fn test_warning_alias() {
3182		let file_name = "test_warning_alias.ebml".to_string();
3183		create_test_file(&file_name,generate_ebml_with_warning_alias());
3184		let process = read_ebml(&file_name);
3185		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),10);
3186		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3187			match substep { SubStep::Warning(..) => println!(">>Warning line found, as expected!"), _ => panic!(">>Should be a 'Warning'")};
3188		}
3189		destroy_test_file(&file_name);
3190	}
3191
3192	#[test]
3193	fn test_verification_alias() {
3194		let file_name = "test_verification_alias.ebml".to_string();
3195		create_test_file(&file_name,generate_ebml_with_verification_alias());
3196		let process = read_ebml(&file_name);
3197		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),7);
3198		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3199			match substep { SubStep::Verification(..) => println!(">>Verification line found, as expected!"), _ => panic!(">>Should be a 'Verification'")};
3200		}
3201		destroy_test_file(&file_name);
3202	}
3203
3204	#[test]
3205	fn test_resource_alias() {
3206		let file_name = "test_resource_alias.ebml".to_string();
3207		create_test_file(&file_name,generate_ebml_with_resource_alias());
3208		let process = read_ebml(&file_name);
3209		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),3);
3210		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3211			match substep { SubStep::Resource(..) => println!(">>Resource line found, as expected!"), _ => panic!(">>Should be a 'Resource'")};
3212		}
3213		destroy_test_file(&file_name);
3214	}
3215
3216	#[test]
3217	fn test_objective_alias() {
3218		let file_name = "test_objective_alias.ebml".to_string();
3219		create_test_file(&file_name,generate_ebml_with_objective_alias());
3220		let process = read_ebml(&file_name);
3221		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),3);
3222		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3223			match substep { SubStep::Objective(..) => println!(">>Objective line found, as expected!"), _ => panic!(">>Should be a 'Objective'")};
3224		}
3225		destroy_test_file(&file_name);
3226	}
3227
3228	#[test]
3229	fn test_out_of_scope_alias() {
3230		let file_name = "test_out_of_scope_alias.ebml".to_string();
3231		create_test_file(&file_name,generate_ebml_with_out_of_scope_alias());
3232		let process = read_ebml(&file_name);
3233		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),3);
3234		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3235			match substep { SubStep::OutOfScope(..) => println!(">>Out of Scope line found, as expected!"), _ => panic!(">>Should be a 'Out of Scope'")};
3236		}
3237		destroy_test_file(&file_name);
3238	}
3239
3240	#[test]
3241	fn test_revision_alias() {
3242		let file_name = "test_revision_alias.ebml".to_string();
3243		create_test_file(&file_name,generate_ebml_with_revision_alias());
3244		let process = read_ebml(&file_name);
3245		assert_eq!(process.get_all_revisions().len(),3);
3246		for (rev,desc) in process.get_all_revisions() {
3247			assert_eq!(rev,"Successful Revision");
3248			assert_eq!(desc,"Successful Description");
3249		}
3250		destroy_test_file(&file_name);
3251	}
3252
3253	#[test]
3254	fn test_template_alias() {
3255		let file_name = "test_template_alias.ebml".to_string();
3256		create_test_file(&file_name,generate_ebml_with_template_alias());
3257		let process = read_ebml(&file_name);
3258		assert_eq!(process.get_all_templates().len(),3);
3259		destroy_test_file(&file_name);
3260	}
3261
3262	// for stanhope features added between 0.95.0 and 0.103.0
3263
3264	#[test]
3265	fn test_process_get_step_count() {
3266		let file_name = "test_process_get_step_count.ebml".to_string();
3267		create_test_file(&file_name,generate_ebml_with_1000_steps_in_one_section());
3268		let process = read_ebml(&file_name);
3269		assert_eq!(process.get_step_count(),1000);
3270		destroy_test_file(&file_name);
3271	}
3272
3273	#[test]
3274	fn test_process_get_all_context_lines() {
3275		let file_name = "test_process_get_all_context_lines.ebml".to_string();
3276		create_test_file(&file_name,generate_ebml_with_context_alias());
3277		let process = read_ebml(&file_name);
3278		assert_eq!(process.get_all_context_lines().len(),7);
3279		destroy_test_file(&file_name);
3280	}
3281
3282	#[test]
3283	fn test_process_get_all_images() {
3284		let file_name = "test_process_get_all_images.ebml".to_string();
3285		create_test_file(&file_name,generate_ebml_with_diabolical_image_lines());
3286		let process = read_ebml(&file_name);
3287		assert_eq!(process.get_all_images().len(),15);
3288		destroy_test_file(&file_name);
3289	}
3290
3291	#[test]
3292	fn test_process_get_unique_image_count() {
3293		let file_name = "test_process_get_unique_image_count.ebml".to_string();
3294		create_test_file(&file_name,generate_ebml_with_diabolical_image_lines());
3295		let process = read_ebml(&file_name);
3296		assert_eq!(process.get_unique_image_count(),2);
3297		destroy_test_file(&file_name);
3298	}
3299
3300	#[test]
3301	fn test_process_get_missing_image_count() {
3302		let file_name = "test_process_get_missing_image_count.ebml".to_string();
3303		create_test_file(&file_name,generate_ebml_with_diabolical_image_lines());
3304		let process = read_ebml(&file_name);
3305		assert_eq!(process.get_missing_image_count(),2);
3306		assert_eq!(process.get_missing_images().len(),2);
3307		destroy_test_file(&file_name);
3308	}
3309
3310	#[test]
3311	fn test_process_get_section_reference_count() {
3312		let file_name = "test_process_get_section_reference_count.ebml".to_string();
3313		create_test_file(&file_name,generate_ebml_with_section_reference_alias());
3314		let process = read_ebml(&file_name);
3315		assert_eq!(process.get_section_reference_count(),4);
3316		destroy_test_file(&file_name);
3317	}
3318
3319	#[test]
3320	fn test_process_set_and_get_process_file() {
3321		let test_process_file = "Not a real EBML file";
3322		let mut new_proc = Process::new();
3323		assert_ne!(new_proc.get_process_file().to_string(),test_process_file.to_string());
3324		new_proc.set_process_file(test_process_file);
3325		assert_eq!(new_proc.get_process_file().to_string(),test_process_file.to_string());
3326	}
3327
3328	#[test]
3329	fn test_process_set_and_get_full_source() {
3330		let test_full_source = vec!["First Line of fake EBML".to_string(),"Second Line of fake EBML".to_string()];
3331		let mut new_proc = Process::new();
3332		assert_ne!(new_proc.get_full_source(),&test_full_source);
3333		new_proc.set_full_source(test_full_source.clone());
3334		assert_eq!(new_proc.get_full_source(),&test_full_source);
3335	}
3336
3337	#[test]
3338	fn test_section_set_get_relative_path() {
3339		let test_relative_path = "Not a real Process folder path";
3340		let mut new_sec = Section::new();
3341		assert_ne!(new_sec.get_relative_path().to_string(),test_relative_path.to_string());
3342		new_sec.set_relative_path(test_relative_path);
3343		assert_eq!(new_sec.get_relative_path().to_string(),test_relative_path.to_string());
3344	}
3345
3346}