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		// Use the function to pull all the lines out of the markup file
838		let lines = lines_from_file(file_name).expect("Could not load lines");
839
840		let mut data_array:Vec<Vec<String>> = vec![];
841		for line in lines {
842			let mut new_csv_data_line:Vec<String> = vec![];
843			let all_parts: Vec<&str> = line.split(',').collect();
844			for part in all_parts {
845				new_csv_data_line.push(part.trim_start().trim_end().to_string());
846			}
847			data_array.push(new_csv_data_line);
848		}
849		data_array
850	}
851
852	// Initialize the output
853	let mut new_table = Table::new();
854	let all_parts: Vec<&str> = line.split('|').collect();
855
856	// TODO: robustify the lines below, because it's built on an unstable house of cards
857	// println!("'ebml_file' --> [{}]",&ebml_file);
858	let path_parts = ebml_file.split('/').collect::<Vec<_>>();
859	// println!("'path_parts' --> [{:?}]",&path_parts);
860
861	let csv_file_to_open = path_parts[0].to_owned() + "/" + path_parts[1] + "/" + all_parts[0].trim_start().trim_end();
862	// println!("'csv_file_to_open' --> [{}]",csv_file_to_open);
863	
864	// One 'part' in the remainder means there's only a file reference in the CSV File line, e.g. "CSV File | file.csv"
865	// Two 'parts' in the remainder means there's also a caption, e.g. "CSV File | file.csv | Caption text"
866	// 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.
867	match all_parts.len() {
868		1 => {
869			for row in open_csv_file_and_return_data_array(&csv_file_to_open) { new_table.add_row(row) };
870		},
871		2 => {
872			for row in open_csv_file_and_return_data_array(&csv_file_to_open) { new_table.add_row(row) };
873			new_table.set_caption(all_parts[1]);
874		},
875		_ => {
876			for row in open_csv_file_and_return_data_array(&csv_file_to_open) { new_table.add_row(row) };
877			new_table.set_caption(all_parts[1]);
878		},
879	}
880	new_table // return the completed Table struct, complete with "array" and "caption" information
881}
882
883/// For a single Action line, extract the information and return an Action struct
884fn parse_action_line(line:&str) -> Action {
885	// Default value of "Two Party Verification" field is FALSE
886	// delimit the line by the bar/pipe character
887	let a: Vec<&str> = line.split('|').collect();
888	match a.len() {
889		2 => Action { perform:a[0].to_string(), expect:a[1].to_string(), tpv:false },
890		1 => Action { perform:a[0].to_string(), expect:"ERROR: NO EXPECTED VALUE PROVIDED".to_string(), tpv:false },
891		0 => Action { perform:"ERROR: NO ACTION PROVIDED".to_string(), expect:"ERROR: NO EXPECTED VALUE PROVIDED".to_string(), tpv:false },
892		// 3 is expected, and the wildcard case covers 3 or more
893		_ => Action { perform:a[0].to_string(), expect:a[1].to_string(), tpv: 
894			match trim_whitespace_make_uppercase(a[2]).as_str() {
895				"TPV"|"T"|"TRUE"|"Y"|"YES"|"TWOPARTYVERIFICATION"|"TWO-PARTYVERIFICATION" => true,
896				_ => false,
897			}
898		},
899	}
900}
901
902
903/// For an "Image" line, parse the two output pieces of information and return as an Image type SubStep
904fn parse_image_line(line:&str) -> SubStep {
905
906	// Delimit the input string by bar/pipe characters and respond based on the number of substrings
907	let all_parts: Vec<&str> = line.split('|').collect();
908	match all_parts.len() {
909		
910		// If only one string, then the image file and caption are the same
911		// If only one string but it's empty, then include a default path and file name for a placeholder image
912		1 => {
913			if all_parts[0]=="" {
914				return SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string())
915			} else {
916				return SubStep::Image(all_parts[0].to_string().trim().to_string(),all_parts[0].to_string().trim().to_string())
917			}
918		},
919		// If for some reason the length of the segment vector is zero (??), then placeholder
920		0 => return SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string()),
921		// Two is expected here: the first is the image file name, and the second is the caption
922		// Wildcard means 3 or more string segments, so use first two as in nominal path
923		_ => {
924			if all_parts[0]=="" {
925				if all_parts[1]=="" {
926					return SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string());
927				} else {
928					return SubStep::Image("../assets/placeholderImage-small.png".to_string(),all_parts[1].to_string().trim().to_string());
929				}
930			} else {
931				if all_parts[1]=="" {
932					return SubStep::Image(all_parts[0].to_string().trim().to_string(),all_parts[0].to_string().trim().to_string())
933				} else {
934					return SubStep::Image(all_parts[0].to_string().trim().to_string(),all_parts[1].to_string().trim().to_string())
935				}				
936			}
937		},
938	}
939
940}
941
942/// Verification lines have three expected pieces of information; this parsing function needs to be robust to all cases
943fn parse_verification_line(line:&str) -> Requirement {
944
945	// Delimit the input string by bar/pipe characters and respond based on the number of substrings
946	let all_parts: Vec<&str> = line.split('|').collect();
947	match all_parts.len() {
948		// Two means we will pass an unknown string to the method, leaving that handling to the other function
949		2 => return Requirement{id:all_parts[0].trim().to_string(),text:all_parts[1].trim().to_string(),method:which_method("???"),},
950		// One string part means we don't have requirement text or method, so unknown values are filled
951		1 => return Requirement{id:all_parts[0].trim().to_string(),text:"???".to_string(),method:which_method("???"),},
952		0 => return Requirement{id:"???".to_string(),text:"???".to_string(),method:which_method("???"),},
953		// Three is expected: ID field is first, then Requirement text, then the method of verificaiton (derived in another function)
954		_ => return Requirement{id:all_parts[0].trim().to_string(),text:all_parts[1].trim().to_string(),method:which_method(all_parts[2].trim()),},
955	}
956
957}
958
959/// Interpret a user string into an enum variant
960fn which_method(m:&str) -> VerificationMethod {
961	// Be robust and case insensitive
962	// Commonly used terms for these verification methods are single letter e.g. D, I, A, S, T
963	match trim_whitespace_make_uppercase(m).as_str() {
964		"DEMONSTRATION"|"DEMO"|"D"	=> VerificationMethod::Demonstration,
965		"INSPECTION"|"I"			=> VerificationMethod::Inspection,
966		"ANALYSIS"|"A"				=> VerificationMethod::Analysis,
967		"SAMPLING"|"SAMPLE"|"S"		=> VerificationMethod::Sampling,
968		"TEST"|"T"					=> VerificationMethod::Test,
969		// Sampling is the least expected of these for Stanhope processes
970		// Making Sampling the default is a pseudo-flag that something is wrong
971		// TODO: add an enum variant that covers the ??? case
972		_ => VerificationMethod::Sampling,
973	}
974}
975
976/// Pass the first part as the descriptive string for the resource
977/// The second part is a flag to determine if this resource needs to be calibrated to be used in this process
978fn parse_resource_line(line:&str) -> Resource {
979	let all_parts: Vec<&str> = line.split('|').collect();
980
981	let cal:bool = match all_parts.len() {
982		0|1 => false,
983		_ => match trim_whitespace_make_uppercase(all_parts[1]).as_str() {
984				"C"|"CAL"|"CALIB"|"CALIBRATE"|"CALIBRATED"|"CALIBRATION"|"Y"|"YES"|"T"|"TRUE" => true,
985				_ => false,
986			}
987	};
988
989	match all_parts[0] {
990		""	=> Resource { name: "ERROR: NO RESOURCE IDENTIFIED".to_string(), calibration:cal, },
991		_ 	=> Resource { name: all_parts[0].to_string(), calibration:cal, },
992	}
993}
994
995
996//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄       ▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄           
997// ▐░░░░░░░░░░░▌▐░░▌     ▐░░▌▐░░░░░░░░░░░▌▐░▌          
998//  ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌   ▐░▐░▌▐░█▀▀▀▀▀▀▀█░▌▐░▌          
999//      ▐░▌     ▐░▌▐░▌ ▐░▌▐░▌▐░▌       ▐░▌▐░▌          
1000//      ▐░▌     ▐░▌ ▐░▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌          
1001//      ▐░▌     ▐░▌  ▐░▌  ▐░▌▐░░░░░░░░░░░▌▐░▌          
1002//      ▐░▌     ▐░▌   ▀   ▐░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌          
1003//      ▐░▌     ▐░▌       ▐░▌▐░▌          ▐░▌          
1004//  ▄▄▄▄█░█▄▄▄▄ ▐░▌       ▐░▌▐░▌          ▐░█▄▄▄▄▄▄▄▄▄ 
1005// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌          ▐░░░░░░░░░░░▌
1006//  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀            ▀▀▀▀▀▀▀▀▀▀▀ 
1007
1008/// Functions necessary to create ::new() structs, as well as "get" every private field publicly, and to change or append private fields publicly
1009impl Process {
1010
1011	/// Return Process struct with placeholder text in String fields, and empty vectors for Vec fields
1012	pub fn new() -> Process {
1013		Process {
1014			process_file: "".to_string(),
1015			number: "NO NUMBER IN EBML FILE - THIS IS PLACEHOLDER TEXT".to_string(),
1016			all_revisions: vec![],
1017			title: "NO TITLE IN EBML FILE".to_string(),
1018			process_type: "NO PROCESS TYPE IDENTIFIED IN EBML FILE".to_string(),
1019			author: "NO AUTHOR IN EBML FILE".to_string(),
1020			reviewer: "NO REVIEWER IN EBML FILE".to_string(),
1021			subject: "NO SUBJECT IN EBML FILE".to_string(),
1022			subject_image: "DEFAULT-PLACEHOLDER-IMAGE.png".to_string(),
1023			product: "N/A".to_string(),
1024			product_image: "N/A".to_string(),
1025			all_objectives: vec![],
1026			all_out_of_scopes: vec![],
1027			all_sections: vec![],
1028			all_resources: vec![],
1029			all_calibrated_resources: vec![],
1030			all_verifications: vec![],
1031			all_templates: vec![],
1032			full_source: vec![],
1033		}
1034	}
1035
1036	/// Return reference to the **Process File** string
1037	pub fn get_process_file(&self) -> &String { &self.process_file }
1038	/// Return reference to the **Document Number** string
1039	pub fn get_number(&self) -> &String { &self.number }
1040	/// Return reference to the LATEST **Revision** (first listed in the markup file)
1041	pub fn get_revision(&self) -> &String { if self.all_revisions.len()==0 { &self.number } else { &self.all_revisions[0].0 } }
1042	/// Return reference to the vector of touples describing each Revision and what changed
1043	pub fn get_all_revisions(&self) -> &Vec<(String,String)> { &self.all_revisions }
1044	/// Return reference to the **Title** string
1045	pub fn get_title(&self) -> &String { &self.title }
1046	/// Return reference to the **Process Type** string
1047	pub fn get_process_type(&self) -> &String { &self.process_type }
1048	/// Return reference to the **Author** string
1049	pub fn get_author(&self) -> &String { &self.author }
1050	/// Return reference to the **Reviewer** string
1051	pub fn get_reviewer(&self) -> &String { &self.reviewer }
1052	/// Return reference to the **Subject** string
1053	pub fn get_subject(&self) -> &String { &self.subject }
1054	/// Return reference to the file name of an **Image of the Subject** as string
1055	pub fn get_subject_image(&self) -> &String { &self.subject_image }
1056	/// Return reference to the **Product** string
1057	pub fn get_product(&self) -> &String { &self.product }
1058	/// Return reference to the file name of an **Image of the Product** as string
1059	pub fn get_product_image(&self) -> &String { &self.product_image }
1060	/// Return reference to a **Vector of Sections**, in the order they appear in the markup
1061	pub fn get_all_sections(&self) -> &Vec<Section> { &self.all_sections }
1062	/// Return the total number of Steps throughout the process, including all Sections and Section References
1063	pub fn get_step_count(&self) -> usize {
1064		let mut stp_cnt:usize = 0;
1065		for sec in &self.all_sections {
1066			stp_cnt += sec.get_all_steps().len();
1067		}
1068		return stp_cnt;
1069	}
1070	/// Return reference to a **Vector of Requirement Tuples**, each with a Requirement struct and a string containing the Step number
1071	pub fn get_all_verifications(&self) -> &Vec<(Requirement,String)> { &self.all_verifications }
1072	/// Return reference to a **Vector of Tuples** describing each objective and at what step it is achieved
1073	pub fn get_all_objectives(&self) -> &Vec<(String,String)> { &self.all_objectives }
1074	/// Return reference to a **Vector of Tuples** describing each out-of-scope declaration and at from which step
1075	pub fn get_all_out_of_scopes(&self) -> &Vec<(String,String)> { &self.all_out_of_scopes }
1076	/// Return reference to a **Vector of Templates** as strings, one for each CSS file needed for the process
1077	pub fn get_all_templates(&self) -> &Vec<String> { &self.all_templates }
1078	/// Return reference to a **Vector of Resource Tuples**, each with a Resource struct and a string containing the Step number
1079	pub fn get_all_resources(&self) -> &Vec<(Resource,String)> { &self.all_resources }
1080	/// Return reference to a **Vector of Resources** (a unique set of only those that need calibration)
1081	pub fn get_all_calibrated_resources(&self) -> &Vec<Resource> { &self.all_calibrated_resources }
1082	/// Return the total number of Action lines that require Two-Party Verification (TPV)
1083	pub fn get_tpv_count(&self) -> usize {
1084		let mut counter:usize = 0;
1085		for sec in self.get_all_sections() {
1086			for stp in sec.get_all_steps() {
1087				for sub in stp.get_all_sub_steps() {
1088					match sub {
1089						SubStep::ActionSequence(acts) => {
1090							for act in acts {
1091								if act.get_tpv().clone() { counter += 1; }
1092							}
1093						},
1094						_ => (),
1095					}
1096				}
1097			}
1098		}
1099		counter
1100	}
1101	/// Return the total number of Action lines that don't require Two-Party Verification (no TPV)
1102	pub fn get_non_tpv_count(&self) -> usize {
1103		let mut counter:usize = 0;
1104		for sec in self.get_all_sections() {
1105			for stp in sec.get_all_steps() {
1106				for sub in stp.get_all_sub_steps() {
1107					match sub {
1108						SubStep::ActionSequence(acts) => {
1109							for act in acts {
1110								if !act.get_tpv().clone() { counter += 1; }
1111							}
1112						},
1113						_ => (),
1114					}
1115				}
1116			}
1117		}
1118		counter
1119	}
1120	/// 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
1121	pub fn get_all_command_lines(&self) -> Vec<(String,String)> {
1122    	let mut all_command_lines = vec![];
1123    	let mut sec_count:u16 = 0;
1124		for sec in self.get_all_sections() {
1125			sec_count += 1;
1126			let mut step_count:u16 = 0;
1127			for stp in sec.get_all_steps() {
1128				step_count += 1;
1129				for sub in stp.get_all_sub_steps() {
1130					match sub {
1131						SubStep::Command(txt) => {
1132							all_command_lines.push((txt.to_string(),("Step ".to_owned()+&sec_count.to_string()+"."+&step_count.to_string()).to_string()));
1133						},
1134						_ => (),
1135					}
1136				}
1137			}
1138		}
1139		all_command_lines
1140	}
1141	/// 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
1142	pub fn get_all_context_lines(&self) -> Vec<(String,String)> {
1143    	let mut all_context_lines = vec![];
1144    	let mut sec_count:u16 = 0;
1145		for sec in self.get_all_sections() {
1146			sec_count += 1;
1147			let mut step_count:u16 = 0;
1148			for stp in sec.get_all_steps() {
1149				step_count += 1;
1150				for sub in stp.get_all_sub_steps() {
1151					match sub {
1152						SubStep::Context(txt) => {
1153							all_context_lines.push((txt.to_string(),("Step ".to_owned()+&sec_count.to_string()+"."+&step_count.to_string()).to_string()));
1154						},
1155						_ => (),
1156					}
1157				}
1158			}
1159		}
1160		all_context_lines
1161	}
1162	/// Return extracted information from all SubStep "Image" variants as Vec of String tuples: (image file, caption text)
1163	pub fn get_all_images(&self) -> Vec<(String,String)> {
1164		let mut all_images = vec![];
1165		for sec in self.get_all_sections() { for stp in sec.get_all_steps() { for sub in stp.get_all_sub_steps() {
1166			match sub { SubStep::Image(file,caption) => all_images.push(((sec.get_relative_path().to_owned() + &file.to_string()).to_string(),caption.to_string())), _ => (), }
1167		}}}
1168		match self.get_subject_image().as_str() {
1169			"DEFAULT-PLACEHOLDER-IMAGE.png" | "N/A" | "" => (),
1170			_ => all_images.push((self.get_subject_image().to_string(),"Subject Image".to_string())),
1171		};
1172		match self.get_product_image().as_str() {
1173			"DEFAULT-PLACEHOLDER-IMAGE.png" | "N/A" | "" => (),
1174			_ => all_images.push((self.get_product_image().to_string(),"Product Image".to_string())),
1175		};
1176		return all_images;
1177	}
1178	/// 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
1179	pub fn get_unique_image_count(&self) -> usize {
1180		let (mut files, _captions): (Vec<_>, Vec<_>) = self.get_all_images().into_iter().map(|(a, b)| (a, b)).unzip();
1181		files.sort();
1182    	files.dedup();
1183    	return files.len();
1184	}
1185	/// 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
1186	pub fn get_missing_images(&self) -> Vec<String> {
1187		let mut missing_images:Vec<String> = vec![];
1188		let (mut files, _captions): (Vec<_>, Vec<_>) = self.get_all_images().into_iter().map(|(a,b)| (a,b)).unzip();
1189		files.sort();
1190		files.dedup();
1191		let process_file = self.get_process_file();
1192		let process_file_parts:Vec<&str> = process_file.split('/').collect();
1193		let mut process_folder:String = String::from("");
1194		for (ii,process_file_part) in process_file_parts.clone().into_iter().enumerate() {
1195			if ii == 0 { process_folder.push_str(process_file_part); }
1196			else if ii+1 < process_file_parts.len() { process_folder.push_str(&("/".to_owned() + process_file_part)); }
1197		}
1198		for file in files { 
1199			let file_full = process_folder.to_owned() + "/" + &file.trim();
1200			//println!("Checking if [{}] exists...",&file);
1201			match std::fs::exists(&file_full) {
1202				Ok(true) => (),
1203		        Ok(false) => missing_images.push(file.trim().to_string()),
1204		        Err(e) => {
1205		        	missing_images.push(file.trim().to_string());
1206		        	eprintln!("Error checking file: {}",e);
1207		        },
1208	    	}
1209		}
1210		return missing_images;
1211	}
1212	/// 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
1213	pub fn get_missing_image_count(&self) -> usize { self.get_missing_images().len() }
1214	/// Return a count of "Section Reference" or "SECREF" lines in this Process, prior to evaluating them into sections
1215	pub fn get_section_reference_count(&self) -> usize {
1216		let mut secref_cnt:usize = 0;
1217		let all_lines = self.get_full_source();
1218		for line in all_lines {
1219			let all_parts: Vec<&str> = line.split('|').collect();
1220			if all_parts.len() > 0 {
1221				match trim_whitespace_make_uppercase(all_parts[0]).as_str() {
1222					"SECTIONREFERENCE" | "SECREF" => secref_cnt+=1,
1223					_ => (),
1224				};
1225			}
1226		}
1227		return secref_cnt;
1228	} 
1229	/// Return the full source EBML of this process as a Vec of Strings, one String per line of EBML
1230	pub fn get_full_source(&self) -> &Vec<String> { &self.full_source }
1231
1232	/// Used in verbose mode to display contents of the struct to stdout
1233	pub fn display_process_to_stdout(&self) {
1234		println!("\n== =================================================================");
1235		println!("==  {}",&self.get_title());
1236		if self.get_all_objectives().len() > 0 {
1237			println!("==   ↪ Objectives:");
1238			for (jj,(obj,_snum)) in self.get_all_objectives().into_iter().enumerate() {
1239				println!("==    {}. {}",jj+1,obj);
1240			}
1241		}
1242		println!("==   ↪ Document Number:           {}",&self.get_number());
1243		println!("==   ↪ Process Type:              {}",&self.get_process_type());
1244		println!("==   ↪ Current Revision:          {}",&self.get_revision());
1245		println!("==     ↪ Number of Revs:          {}",&self.get_all_revisions().len());
1246		println!("==   ↪ Author of this Rev:        {}",&self.get_author());
1247		println!("==   ↪ Reviewer of this Rev:      {}",&self.get_reviewer());
1248		println!("==\n==  Purpose and Objectives");
1249		println!("==   ↪ Subject of process:        {}",&self.get_subject());
1250		println!("==   ↪ Image of Subject:          {}",&self.get_subject_image());
1251		println!("==   ↪ Product produced:          {}",&self.get_product());
1252		println!("==   ↪ Image of Product:          {}",&self.get_product_image());
1253		println!("==   ↪ Count of Objectives:       {}",&self.get_all_objectives().len());
1254		println!("==   ↪ Count of Out of Scopes:    {}",&self.get_all_out_of_scopes().len());
1255		println!("==\n==  Process Structure");
1256		println!("==   ↪ Count of Templates:        {}",&self.get_all_templates().len());
1257		println!("==   ↪ Count of Sections:         {}",&self.get_all_sections().len());
1258		println!("==     ↪ Section References:      {}",&self.get_section_reference_count());
1259		println!("==   ↪ Count of Steps (total):    {}",&self.get_step_count());
1260		println!("==   ↪ Count of Resources:        {}",&self.get_all_resources().len());
1261		println!("==     ↪ Calibrated Resources:    {}",&self.get_all_calibrated_resources().len());
1262		println!("==   ↪ Count of Verifications:    {}",&self.get_all_verifications().len());
1263		println!("==   ↪ Count of Actions:          {}",&self.get_tpv_count()+&self.get_non_tpv_count());
1264		println!("==     ↪ TPV Actions:             {}",&self.get_tpv_count());
1265		println!("==     ↪ Non-TPV Actions:         {}",&self.get_non_tpv_count());
1266		println!("==   ↪ Count of Commands:         {}",&self.get_all_command_lines().len());
1267		println!("==   ↪ Count of Context lines:    {}",&self.get_all_context_lines().len());
1268		println!("==   ↪ Images (lines, sub, prod): {}",&self.get_all_images().len());
1269		println!("==     ↪ Unique images called:    {}",&self.get_unique_image_count());
1270		println!("==     ↪ Missing image files:     {}",&self.get_missing_image_count());
1271		if self.get_missing_image_count() > 0 {
1272			for (ii,image) in self.get_missing_images().into_iter().enumerate() {
1273				println!("==       [{}] {}",ii+1,&image);
1274			}
1275		}
1276		println!("== =================================================================\n");
1277	}
1278
1279	/// Change the stored location of the EBML file for this process
1280	pub fn set_process_file(&mut self, file_name:&str) {
1281		self.process_file = String::from(file_name);
1282	}
1283	/// Change the Document Number
1284	pub fn set_number(&mut self, doc_num: &str) {
1285		self.number = String::from(doc_num.trim());
1286	}
1287	/// Add a Revision Tuple to the Vector "all_revisions"
1288	pub fn add_revision(&mut self, rev_line: &str) {
1289		// Delimit the line into its bar/piped chunks
1290		let chunks:Vec<&str> = rev_line.split('|').collect();
1291		// If the first chunk isn't an empty string, then use it (otherwise use default text)
1292		let rev_str = if !(chunks[0]=="") { chunks[0] } else { &"[No Rev]" };
1293		// Use the second chunk if it exists and isn't an empty string
1294		let chg_str = if chunks.len()<2 { &"[No description provided by Author]" } else if chunks[1]=="" { &"[No description provided by Author]" } else { chunks[1] };
1295		self.all_revisions.push((String::from(rev_str.trim()),String::from(chg_str.trim())));
1296	}
1297	/// Change the Document Title
1298	pub fn set_title(&mut self, title: &str) {
1299		self.title = String::from(title.trim());
1300	}
1301	/// Change the Process Type
1302	pub fn set_process_type(&mut self, process_type: &str) {
1303		self.process_type = String::from(process_type.trim());
1304	}
1305	/// Change the Document Author
1306	pub fn set_author(&mut self, author: &str) {
1307		self.author = String::from(author.trim());
1308	}
1309	/// Change the Document Reviewer
1310	pub fn set_reviewer(&mut self, reviewer: &str) {
1311		self.reviewer = String::from(reviewer.trim());
1312	}
1313	/// Change the Document Subject
1314	pub fn set_subject(&mut self, subject: &str) {
1315		self.subject = String::from(subject.trim());
1316	}
1317	/// Change the Image file name of the Document Subject
1318	pub fn set_subject_image(&mut self, subject_image: &str) {
1319		self.subject_image = String::from(subject_image.trim());
1320	}
1321	/// Change the Document Product
1322	pub fn set_product(&mut self, product: &str) {
1323		self.product = String::from(product.trim());
1324	}
1325	/// Change the Image file name of the Document Product
1326	pub fn set_product_image(&mut self, product_image: &str) {
1327		self.product_image = String::from(product_image.trim());
1328	}
1329	/// Add a Template file to the Vector of Template files (strings)
1330	pub fn add_template(&mut self, template: &str) {
1331		self.all_templates.push(String::from(template.trim()));
1332	}
1333	/// Add a Section to the Vector of Section structs
1334	pub fn add_section(&mut self, section: Section) {
1335		self.all_sections.push(section);
1336	}
1337	/// Add a Verification tuple to the Vector of Verification tuples
1338	pub fn add_verification(&mut self, requirement: Requirement, step:String) {
1339		self.all_verifications.push((requirement,step));
1340	}
1341	/// Add an Objective tuple to the Vector of Objective tuples
1342	pub fn add_objective(&mut self, objective:String, step:String) {
1343		self.all_objectives.push((objective,step));
1344	}
1345	/// Add an Out-of-Scope tuple to the Vector of Out-of-Scope tuples
1346	pub fn add_out_of_scope(&mut self, out_of_scope:String, step:String) {
1347		self.all_out_of_scopes.push((out_of_scope,step));
1348	}
1349	/// Add a Resource struct to the Vector of Resource structs
1350	pub fn add_resource(&mut self, resource: Resource, step:String) {
1351		self.all_resources.push((resource,step));
1352	}
1353	/// Add a Resource struct to the Vector of calibrated Resource structs
1354	pub fn add_calibrated_resource(&mut self, resource: Resource) {
1355		if *resource.get_calibration() { self.all_calibrated_resources.push(resource); }
1356	}
1357	/// Store the full text of the EBML file for this process as a Vec of Strings, one String per line of EBML
1358	pub fn set_full_source(&mut self, full_source: Vec<String>) {
1359		self.full_source = full_source;
1360	}
1361	
1362}
1363
1364/// Section needs "get" and "set/add" functions as well, to read and write private fields
1365impl Section {
1366
1367	/// Blank string for the title, and empty Vec initialized for the list of Steps
1368	fn new() -> Section {
1369		Section {
1370			title: "".to_string(),
1371			all_steps: vec![],
1372			relative_path: "".to_string(),
1373		}
1374	}
1375
1376	/// Return reference to **Section Title** as string
1377	pub fn get_title(&self) -> &String { &self.title }
1378	/// Return reference to **Vector of Step structs**
1379	pub fn get_all_steps(&self) -> &Vec<Step> { &self.all_steps }
1380	/// Return the relative path from the Process folder to the Section's Process folder, only non-empty for SECREF feature
1381	pub fn get_relative_path(&self) -> &String { &self.relative_path }
1382
1383	/// Change the **Section Title**
1384	fn set_title(&mut self, title: &str) {
1385		self.title = String::from(title);
1386	}
1387	/// Add a complete Step struct to the Vector of Step structs
1388	fn add_step(&mut self, step: Step) {
1389		self.all_steps.push(step);
1390	}
1391	/// Change the **Relative Path** from the Process folder to the Section's Process folder, especially if performing a SECREF fetch
1392	fn set_relative_path(&mut self, path:&str) {
1393		self.relative_path = String::from(path);
1394	}
1395}
1396
1397/// Step needs "get" and "set/add" functions as well, to read and write private fields
1398impl Step {
1399
1400	/// Empty string for the Step text, and empty vectors for 'resources' and 'all_sub_steps'
1401	pub fn new() -> Step {
1402		Step {
1403			text: "".to_string(),
1404			resources: vec![],
1405			all_sub_steps: vec![],
1406		}
1407	}
1408
1409	/// Return reference to **Step text** as string
1410	pub fn get_text(&self) -> &String { &self.text }
1411	/// Return reference to **Vector of Resource structs** for this step
1412	pub fn get_resources(&self) -> &Vec<Resource> { &self.resources }
1413	/// Return reference to **Vector of SubStep structs** for all SubSteps in this Step
1414	pub fn get_all_sub_steps(&self) -> &Vec<SubStep> { &self.all_sub_steps }
1415
1416	/// Change the text of this Step
1417	fn set_text(&mut self, text: &str) {
1418		self.text = String::from(text);
1419	}
1420	/// Add a complete SubStep to the ordered Vector of SubStep structs
1421	fn add_sub_step(&mut self, sub_step: SubStep) {
1422		self.all_sub_steps.push(sub_step);
1423	}
1424	/// Add a complete Resource struct to the ordered Vector of Resource structs
1425	fn add_resource(&mut self,r:Resource) {
1426		self.resources.push(r);
1427	}
1428
1429}
1430
1431/// Since Action structs are created directly in a function outside the struct at read-time, we just need "get" functions
1432impl Action {
1433	/// Return reference to the "Action to Perform" string
1434	pub fn get_perform(&self) -> &String { &self.perform }
1435	/// Return reference to the "Expected Outcome" string
1436	pub fn get_expect(&self) -> &String { &self.expect }
1437	/// Return reference to the "Two Party Verification" boolean
1438	pub fn get_tpv(&self) -> &bool { &self.tpv }
1439}
1440
1441/// Since Requirement structs are created directly in a function outside the struct at read-time, we just need "get" functions
1442impl Requirement { 
1443	/// Return reference to the **Requirement ID** string
1444	pub fn get_id(&self) -> &String { &self.id }
1445	/// Return reference to the **Requirement Text** string
1446	pub fn get_text(&self) -> &String { &self.text }
1447	/// Return reference to the **Verification Method** as a string, even though that's not how's it's stored
1448	pub fn get_method(&self) -> String {
1449		// The Verification Method is stored as a VerificationMethod enum, so we need to map that to String values
1450		match &self.method {
1451			VerificationMethod::Demonstration => "Demonstration".to_string(),
1452			VerificationMethod::Inspection => "Inspection".to_string(),
1453			VerificationMethod::Analysis => "Analysis".to_string(),
1454			VerificationMethod::Sampling => "Sampling".to_string(),
1455			VerificationMethod::Test => "Test".to_string(),
1456		}
1457	}
1458}
1459
1460/// Simple public "get" funciton for the one struct field
1461impl Resource {
1462	/// Return reference to the **Resource Name** string
1463	pub fn get_name(&self) -> &String { &self.name }
1464
1465	/// Return the boolean that indicates if the resource requires calibration
1466	pub fn get_calibration(&self) -> &bool { &self.calibration }
1467}
1468
1469/// 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.
1470impl Table {
1471
1472	/// Empty string for the default caption, and an empty array for the default table data
1473	pub fn new() -> Table {
1474		Table {
1475			caption: "".to_string(),
1476			array: vec![],
1477		}
1478	}
1479
1480	/// Return reference to the table caption
1481	pub fn get_caption(&self) -> &String { &self.caption }
1482	/// Change the table caption field
1483	fn set_caption(&mut self, caption: &str) {
1484		self.caption = String::from(caption.trim_start().trim_end());
1485	}
1486
1487	/// Return a tuple with the number of rows and the number of columns in the table data array.
1488	/// 
1489	/// Assumptions:
1490	/// 1. if there are zero rows, there are zero columns
1491	/// 1. the number of columns in the first row is the number of columns that **should** be in every row
1492	pub fn get_size(&self) -> (usize,usize) {
1493		
1494		let rows = self.array.len();
1495
1496		let columns:usize = match rows {
1497			0 => 0,
1498			_ => self.array[0].len(),
1499		};
1500		
1501		(rows,columns)
1502	}
1503
1504	/// Return a vector with all cell contents for an indexed row number.
1505	/// 
1506	/// Example: to return the contents of the first row,
1507	/// > my_table.get_row(0)
1508	/// 
1509	/// Example: to return the conents of the 10th row,
1510	/// > my_table.get_row(9)
1511	pub fn get_row(&self,row_num:usize) -> Vec<String> {
1512		let (rows,_columns) = self.get_size();
1513		match &row_num <= &(rows-1) {
1514			true 	=> self.array[row_num].clone(),
1515			false 	=> panic!("table reference out of bounds"),
1516		}
1517	}
1518
1519	/// Append the bottom of the table data array with another row of data.
1520	/// 
1521	/// Assumptions:
1522	/// 1. if this is the first row to be pushed, then it has the **correct** number of columns
1523	/// 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
1524	/// 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
1525	fn add_row(&mut self,row:Vec<String>) {
1526		let (rows,columns) = self.get_size();
1527		match rows {
1528			0 => self.array.push(row),
1529			_ => {
1530				let mut new_row:Vec<String> = vec![];
1531				for ii in 0..columns {
1532					if ii < row.len() {
1533						new_row.push(row[ii].clone().trim_start().trim_end().to_string());
1534					} else {
1535						new_row.push("".to_string());
1536					}
1537				}
1538				self.array.push(new_row)
1539			},
1540		};
1541	}
1542
1543}
1544
1545
1546//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
1547// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
1548//  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
1549//      ▐░▌     ▐░▌          ▐░▌               ▐░▌     
1550//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
1551//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
1552//      ▐░▌     ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     
1553//      ▐░▌     ▐░▌                    ▐░▌     ▐░▌     
1554//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     
1555//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
1556//       ▀       ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀      
1557
1558#[cfg(test)]
1559mod tests {
1560    // Note this useful idiom: importing names from outer (for mod tests) scope.
1561	use super::*;
1562	use std::fs;
1563	use std::fs::OpenOptions;
1564	use std::io::Write;
1565
1566	// Test the struct impl functions
1567	#[test]
1568	fn test_process_new() {
1569		Process::new();
1570	}
1571
1572	#[test]
1573    fn test_process_get_functions() {
1574		
1575		let p:Process = Process::new();
1576
1577		assert_eq!(&p.number,p.get_number());
1578		println!("struct Process / Result of get_number() -> {:?}",p.get_number());
1579
1580		assert_eq!(&p.process_type,p.get_process_type());
1581		println!("struct Process / Result of get_process_type() -> {:?}",p.get_process_type());
1582		
1583		assert_eq!(&p.number,p.get_revision());
1584		println!("struct Process / Result of get_revision() -> {:?}",p.get_revision());
1585		
1586		assert!(p.all_revisions.len()==0);
1587		let _is_right_type:&Vec<(String,String)> = p.get_all_revisions();
1588		println!("struct Process / Result of get_all_revisions() -> {:?}",p.get_all_revisions());
1589
1590		assert_eq!(&p.title,p.get_title());
1591		println!("struct Process / Result of get_title() -> {:?}",p.get_title());
1592
1593		assert_eq!(&p.author,p.get_author());
1594		println!("struct Process / Result of get_author() -> {:?}",p.get_author());
1595
1596		assert_eq!(&p.reviewer,p.get_reviewer());
1597		println!("struct Process / Result of get_reviewer() -> {:?}",p.get_reviewer());
1598
1599		assert_eq!(&p.subject,p.get_subject());
1600		println!("struct Process / Result of get_subject() -> {:?}",p.get_subject());
1601
1602		assert_eq!(&p.subject_image,p.get_subject_image());
1603		println!("struct Process / Result of get_subject_image() -> {:?}",p.get_subject_image());
1604
1605		assert_eq!(&p.product,p.get_product());
1606		println!("struct Process / Result of get_product() -> {:?}",p.get_product());
1607
1608		assert_eq!(&p.product_image,p.get_product_image());
1609		println!("struct Process / Result of get_product_image() -> {:?}",p.get_product_image());
1610
1611		assert_eq!(p.get_tpv_count(),0);
1612		println!("struct Process / Result of get_tpv_count() -> {:?}",p.get_tpv_count());
1613
1614		assert_eq!(p.get_non_tpv_count(),0);
1615		println!("struct Process / Result of get_non_tpv_count() -> {:?}",p.get_non_tpv_count());
1616
1617		assert!(p.all_sections.len()==0);
1618		let _is_right_type:&Vec<Section> = p.get_all_sections();
1619		println!("struct Process / Result of get_all_sections() -> [it's empty]");
1620
1621		assert!(p.all_verifications.len()==0);
1622		let _is_right_type:&Vec<(Requirement,String)> = p.get_all_verifications();
1623		println!("struct Process / Result of get_all_sections() -> [it's empty]");
1624
1625		assert!(p.all_templates.len()==0);
1626		let _is_right_type:&Vec<String> = p.get_all_templates();
1627		println!("struct Process / Result of get_all_templates() -> [it's empty]");
1628
1629		assert!(p.all_resources.len()==0);
1630		let _is_right_type:&Vec<(Resource,String)> = p.get_all_resources();
1631		println!("struct Process / Result of get_all_resources() -> [it's an empty]");
1632
1633		assert!(p.all_calibrated_resources.len()==0);
1634		let _is_right_type:&Vec<Resource> = p.get_all_calibrated_resources();
1635		println!("struct Process / Result of get_all_calibrated_resources() -> [it's an empty]");
1636
1637		assert!(p.all_objectives.len()==0);
1638		let _is_right_type:&Vec<(String,String)> = p.get_all_objectives();
1639		println!("struct Process / Result of get_all_objectives() -> [it's an empty]");
1640
1641		assert!(p.all_out_of_scopes.len()==0);
1642		let _is_right_type:&Vec<(String,String)> = p.get_all_out_of_scopes();
1643		println!("struct Process / Result of get_all_out_of_scopes() -> [it's an empty]");
1644
1645	}
1646
1647	#[test]
1648	fn test_process_display_process_to_stdout() {
1649
1650		let p:Process = Process::new();
1651		p.display_process_to_stdout();
1652
1653	}
1654
1655	#[test]
1656	fn test_process_set_functions() {
1657
1658		let mut p:Process = Process::new();
1659
1660		p.set_number("SET_PROCESS_DOC_NUMBER");
1661		p.add_revision("SET_REV|SET_REV_CHANGE");
1662		p.set_title("SET_PROCESS_TITLE");
1663		p.set_author("SET_AUTHOR");
1664		p.set_reviewer("SET_REVIEWER");
1665		p.set_subject("SET_SUBJECT");
1666		p.set_subject_image("SET_SUBJECT_IMAGE");
1667		p.set_product("SET_PRODUCT");
1668		p.set_product_image("SET_PRODUCT_IMAGE");
1669		p.add_template("SET_TEMPLATE");
1670		p.add_section(Section{title:"SET_SECTION_TITLE".to_string(),all_steps:vec![],relative_path:"SET_SECTION_RELATIVE_PATH".to_string()});
1671		p.add_verification(Requirement{id:"SET_REQUIREMENT_ID".to_string(),text:"SET_REQUIREMENT_TEXT".to_string(),method:VerificationMethod::Sampling,},"SET_VERIFICATION_STEP".to_string());
1672		p.add_resource(Resource{name:"SET_RESOURCE_NAME".to_string(),calibration:false},"SET_RESOURCE_STEP".to_string());
1673		p.add_objective("SET_OBJECTIVE_TEXT".to_string(),"SET_OBJECTIVE_STEP".to_string());
1674		p.add_out_of_scope("SET_OBJECTIVE_TEXT".to_string(),"SET_OBJECTIVE_STEP".to_string());
1675
1676		p.display_process_to_stdout();
1677
1678	}
1679
1680	#[test]
1681	fn test_section_new() {
1682		Section::new();
1683	}
1684
1685	#[test]
1686	fn test_section_get_functions() {
1687
1688		let s:Section = Section::new();
1689
1690		assert_eq!(&s.title,s.get_title());
1691		println!("struct Section / Result of get_title() -> {:?}",s.get_title());
1692
1693		assert!(s.get_all_steps().len()==0);
1694		let _is_right_type:&Vec<Step> = s.get_all_steps();
1695		println!("struct Section / Result of get_all_steps -> [it's empty]");
1696
1697	}
1698
1699	#[test]
1700	fn test_section_set_functions() {
1701
1702		let mut s:Section = Section::new();
1703
1704		s.set_title("SET_SECTION_TITLE");
1705		s.add_step(Step{text:"SET_SECTION_STEP_TEXT".to_string(),resources:vec![],all_sub_steps:vec![],});
1706
1707	}
1708
1709	#[test]
1710	fn test_step_new() {
1711		Step::new();
1712	}
1713
1714	#[test]
1715	fn test_step_get_functions() {
1716
1717		let stp:Step = Step::new();
1718
1719		assert_eq!(&stp.text,stp.get_text());
1720		println!("struct Step / Result of get_text() -> {:?}",stp.get_text());
1721
1722		assert!(stp.get_resources().len()==0);
1723		let _is_right_type:&Vec<Resource> = stp.get_resources();
1724		println!("struct Step / Result of get_resources() -> [it's empty]");
1725
1726		assert!(stp.get_all_sub_steps().len()==0);
1727		let _is_right_type:&Vec<SubStep> = stp.get_all_sub_steps();
1728		println!("struct Step / Result of get_all_sub_steps() -> [it's empty]");
1729
1730	}
1731
1732	#[test]
1733	fn test_step_set_functions() {
1734
1735		let mut stp:Step = Step::new();
1736
1737		stp.set_text("SET_STEP_TEXT");
1738		stp.add_sub_step(SubStep::Warning("SET_STEP_SUB_STEP_WARNING".to_string()));
1739		stp.add_resource(Resource{name:"SET_STEP_SUB_STEP_RESOURCE".to_string(),calibration:false});
1740
1741	}
1742
1743	#[test]
1744	fn test_action_get_functions() {
1745
1746		let a:Action = parse_action_line("ACTION|EXPECTED|TPV_TEXT");
1747
1748		assert_eq!(&a.perform,a.get_perform());
1749		println!("struct Action / Result of get_perform() -> {:?}",a.get_perform());
1750
1751		assert_eq!(&a.expect,a.get_expect());
1752		println!("struct Action / Result of get_expect() -> {:?}",a.get_expect());
1753
1754		assert_eq!(&a.tpv,a.get_tpv());
1755		println!("struct Action / Result of get_tpv() -> {:?}",a.get_tpv());
1756
1757	}
1758
1759	#[test]
1760	fn test_requirement_get_functions() {
1761
1762		let r:Requirement = parse_verification_line("RID|REQ_TEXT|VER_METH");
1763
1764		assert_eq!(&r.id,r.get_id());
1765		println!("struct Requirement / Result of get_id() -> {:?}",r.get_id());
1766
1767		assert_eq!(&r.text,r.get_text());
1768		println!("struct Requirement / Result of get_text() -> {:?}",r.get_text());
1769
1770		assert_eq!("Sampling",r.get_method());
1771		println!("struct Requirement / Result of get_method() -> {:?}",r.get_method());
1772
1773	}
1774
1775	#[test]
1776	fn test_resource_get_functions() {
1777
1778		let res:Resource = Resource{name:"RESOURCE_NAME".to_string(),calibration:false};
1779
1780		assert_eq!(&res.name,res.get_name());
1781		println!("struct Resource / Result of get_name() -> {:?}",res.get_name());
1782
1783		assert_eq!(&res.calibration,res.get_calibration());
1784		println!("struct Resource / Result of get_calibration() -> {:?}",res.get_calibration());
1785
1786	}
1787
1788	#[test]
1789	fn test_read_file_blank() {
1790		let _is_right_type:Process = read_ebml(&"".to_string());
1791	}
1792
1793	// Some test-only functions for creating and destroying files to feed to read_ebml
1794
1795	fn create_test_file(filename:&str, lines:Vec<String>) {
1796		let mut new_file = OpenOptions::new()
1797		            .read(true)
1798		            .write(true)
1799		            .create(true)
1800		            .open(filename)
1801		            .expect("Could not open the file!");
1802        for line in lines {
1803        	new_file.write({line+"\n"}.as_bytes()).expect("Could not write line to test-only file!");
1804        }
1805	}
1806
1807	fn destroy_test_file(filename:&str) {
1808		let _ = fs::remove_file(filename);
1809	}
1810
1811	fn generate_ebml_with_diabolical_comments() -> Vec<String> {
1812		let mut new_vec_of_strings:Vec<String> = vec![];
1813		new_vec_of_strings.push("//".to_string());
1814		new_vec_of_strings.push("///".to_string());
1815		new_vec_of_strings.push("// /".to_string());
1816		new_vec_of_strings.push("/////////".to_string());
1817		new_vec_of_strings.push("//\\\\\\\\\\".to_string());
1818		new_vec_of_strings.push("\\\\Section|Section title".to_string());
1819		new_vec_of_strings.push("\n\n\n".to_string());
1820		new_vec_of_strings.push("//Section|This is a section! Maybe?".to_string());
1821		new_vec_of_strings.push("//Step|This is a step! Maybe?".to_string());
1822		new_vec_of_strings.push("\n\n\n".to_string());
1823		new_vec_of_strings.push("/ /".to_string());
1824		new_vec_of_strings.push("/Section/".to_string());
1825		new_vec_of_strings.push("/Section|Section title/".to_string());
1826		new_vec_of_strings.push("\n\n\n".to_string());
1827		new_vec_of_strings.push(" //".to_string());
1828		new_vec_of_strings.push(" / / / / / / / / ".to_string());
1829		return new_vec_of_strings;
1830	}
1831
1832	fn generate_ebml_with_1000_sections() -> Vec<String> {
1833		let mut new_vec_of_strings:Vec<String> = vec![];
1834		for ii in 0..1000 {
1835			new_vec_of_strings.push("  sEc tIo      N  |Section ".to_string()+&(ii+1).to_string());
1836		}
1837		return new_vec_of_strings;
1838	}
1839
1840	fn generate_ebml_with_1000_steps_in_one_section() -> Vec<String> {
1841		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1".to_string()];
1842		for ii in 0..1000 {
1843			new_vec_of_strings.push("  s T e P   |Step 1.".to_string()+&(ii+1).to_string());
1844		}
1845		return new_vec_of_strings;
1846	}
1847
1848	fn generate_ebml_with_1000_verifications_in_one_step() -> Vec<String> {
1849		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1850		for ii in 0..1000 {
1851			new_vec_of_strings.push("  vEr iFi cAt iOn   |R".to_string()+&(ii+1).to_string()+"|Requirement text|demo");
1852		}
1853		return new_vec_of_strings;
1854	}
1855
1856	fn generate_ebml_with_1000_resources_in_one_step() -> Vec<String> {
1857		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1858		for ii in 0..500 {
1859			new_vec_of_strings.push("  rEs oUr cE   |Really Important Tool #".to_string()+&(ii+1).to_string());
1860		}
1861		for ii in 500..1000 {
1862			new_vec_of_strings.push("  rEs oUr cE   |Really Important Tool #".to_string()+&(ii+1).to_string()+"|cal");
1863		}
1864		return new_vec_of_strings;
1865	}
1866
1867	fn generate_ebml_with_diabolical_calibrated_resources() -> Vec<String> {
1868		// "C"|"CAL"|"CALIB"|"CALIBRATE"|"CALIBRATED"|CALIBRATION"|"Y"|"YES"|"T"|"TRUE"
1869		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1870		new_vec_of_strings.push("Resource|Calibrated Resource|c    ".to_string());
1871		new_vec_of_strings.push("Resource|Calibrated Resource| c   ".to_string());
1872		new_vec_of_strings.push("Resource|Calibrated Resource|  c  ".to_string());
1873		new_vec_of_strings.push("Resource|Calibrated Resource|   c ".to_string());
1874		new_vec_of_strings.push("Resource|Calibrated Resource|    c".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    A    l    ".to_string());
1881		new_vec_of_strings.push("Resource|Calibrated Resource|CA    L".to_string());
1882		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b ".to_string());
1883		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b rate ".to_string());
1884		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b rate  d ".to_string());
1885		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b rat   ion ".to_string());
1886		new_vec_of_strings.push("Resource|Calibrated Resource|      y         ".to_string());
1887		new_vec_of_strings.push("Resource|Calibrated Resource|   y  e  s      ".to_string());
1888		new_vec_of_strings.push("Resource|Calibrated Resource|   t      ".to_string());
1889		new_vec_of_strings.push("Resource|Calibrated Resource|   t r u e   ".to_string());
1890		// 20 calibrated
1891
1892		// And... one that isn't. So... this process should have 20 calibrated (but 21 total resources...)
1893		new_vec_of_strings.push("Resource|Calibrated Resource| nopey dopey!!! ".to_string());
1894
1895		return new_vec_of_strings;
1896
1897	}
1898
1899	fn generate_ebml_with_1000_actions_in_one_step() -> Vec<String> {
1900		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1901		for _ii in 0..1000 {
1902			new_vec_of_strings.push(" a CT i o N      |Thing to do|Thing to Expect|TPV".to_string());
1903			//new_vec_of_strings.push("Action|Thing to do|Thing to Expect|TPV".to_string());
1904		}
1905		return new_vec_of_strings;
1906	}
1907
1908	fn generate_ebml_with_300_tpv_700_non_tpv_actions_in_one_step() -> Vec<String> {
1909		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1910		for _ii in 0..300 {
1911			new_vec_of_strings.push(" aC  tI  oN |Thing to do|Thing to Expect|TPV".to_string());
1912		}
1913		for _ii in 0..700 {
1914			new_vec_of_strings.push("actio      N|Thing to do|Thing to Expect".to_string());
1915		}
1916		return new_vec_of_strings;
1917	}
1918
1919	fn generate_ebml_with_1000_objectives_in_one_step() -> Vec<String> {
1920		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1921		for ii in 0..1000 {
1922			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());
1923		}
1924		return new_vec_of_strings;
1925	}
1926
1927	fn generate_ebml_with_1000_out_of_scopes_in_one_step() -> Vec<String> {
1928		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1929		for ii in 0..1000 {
1930			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());
1931		}
1932		return new_vec_of_strings;
1933	}
1934
1935	fn generate_ebml_set_meta_twice() -> Vec<String> {
1936		let mut new_vec_of_strings:Vec<String> = vec![];
1937		new_vec_of_strings.push("Number|First Number".to_string());
1938		new_vec_of_strings.push("Number|Second Number".to_string());
1939		new_vec_of_strings.push("Title|First Title".to_string());
1940		new_vec_of_strings.push("Title|Second Title".to_string());
1941		new_vec_of_strings.push("Author|First Author".to_string());
1942		new_vec_of_strings.push("Author|Second Author".to_string());
1943		new_vec_of_strings.push("Reviewer|First Reviewer".to_string());
1944		new_vec_of_strings.push("Reviewer|Second Reviewer".to_string());
1945		new_vec_of_strings.push("Subject|First Subject".to_string());
1946		new_vec_of_strings.push("Subject|Second Subject".to_string());
1947		new_vec_of_strings.push("SubjectImage|First SubjectImage".to_string());
1948		new_vec_of_strings.push("SubjectImage|Second SubjectImage".to_string());
1949		new_vec_of_strings.push("Product|First Product".to_string());
1950		new_vec_of_strings.push("Product|Second Product".to_string());
1951		new_vec_of_strings.push("ProductImage|First ProductImage".to_string());
1952		new_vec_of_strings.push("ProductImage|Second ProductImage".to_string());
1953		new_vec_of_strings.push("ProcessType|First ProcessType".to_string());
1954		new_vec_of_strings.push("ProcessType|Second ProcessType".to_string());
1955		return new_vec_of_strings;
1956	}
1957
1958	fn generate_ebml_with_diabolical_whitespace_first_part() -> Vec<String> {
1959		let mut new_vec_of_strings:Vec<String> = vec![];
1960		new_vec_of_strings.push("Section|Section Title".to_string());
1961		new_vec_of_strings.push(" Section|Section Title".to_string());
1962		new_vec_of_strings.push("  Section|Section Title".to_string());
1963		new_vec_of_strings.push("Section |Section Title".to_string());
1964		new_vec_of_strings.push("Section  |Section Title".to_string());
1965		new_vec_of_strings.push(" Section |Section Title".to_string());
1966		new_vec_of_strings.push(" S e c t i o n |Section Title".to_string());
1967		new_vec_of_strings.push("Sec      ti        on       |Section Title".to_string());
1968		new_vec_of_strings.push(" S                    ection|Section Title".to_string());
1969		new_vec_of_strings.push("Section|Section Title".to_string());
1970		// 10 total
1971		return new_vec_of_strings;
1972	}
1973
1974	fn generate_ebml_with_diabolical_section_and_step_triggers() -> Vec<String> {
1975		let mut new_vec_of_strings:Vec<String> = vec![];
1976		// Three sections, each with three steps, let's get goofy AF
1977		new_vec_of_strings.push("Section |Section Title".to_string());
1978		new_vec_of_strings.push(" S t e p |Step Text".to_string());
1979		new_vec_of_strings.push(" A c t i o n |Do This|Expect This|TPV".to_string());
1980		new_vec_of_strings.push(" C o m m a n d |Command Text".to_string());
1981		new_vec_of_strings.push(" I m a g e |ImageFile.Ext|Image Caption".to_string());
1982		new_vec_of_strings.push(" W a r n i n g |Warning Text".to_string());
1983		new_vec_of_strings.push(" V e r i f i c a t i o n |RID|Req Text|T".to_string());
1984		new_vec_of_strings.push(" R e s o u r c e |Resource Text".to_string());
1985		new_vec_of_strings.push("St  ep   |Step Text".to_string());
1986		new_vec_of_strings.push("Com man d|Command Text".to_string());
1987		new_vec_of_strings.push("     Step|Step Text".to_string());
1988		new_vec_of_strings.push(" Command |Command Text".to_string());
1989		new_vec_of_strings.push(" S e c t i o n |Section Title".to_string());
1990		new_vec_of_strings.push("St ep|Step Text".to_string());
1991		new_vec_of_strings.push("C om ma n d|Command Text".to_string());
1992		new_vec_of_strings.push("St  ep|Step Text".to_string());
1993		new_vec_of_strings.push("Command          |Command Text".to_string());
1994		new_vec_of_strings.push("St   ep|Step Text".to_string());
1995		new_vec_of_strings.push(" command |Command Text".to_string());
1996		new_vec_of_strings.push("SECTION|Section Title".to_string());
1997		new_vec_of_strings.push("STEP|Step Text".to_string());
1998		new_vec_of_strings.push(" C O M M A N D|Command Text".to_string());
1999		new_vec_of_strings.push("S T E P |Step Text".to_string());
2000		new_vec_of_strings.push("   CO   MM   AND   |Command Text".to_string());
2001		new_vec_of_strings.push("ST   EP|Step Text".to_string());
2002		new_vec_of_strings.push(" c o MM a n D     |Command Text".to_string());
2003		return new_vec_of_strings;
2004	}
2005
2006	fn generate_ebml_with_diabolical_actions() -> Vec<String> {
2007		let mut new_vec_of_strings:Vec<String> = vec![];
2008		// One Section, three Steps, one ActionSequence apiece
2009		new_vec_of_strings.push("Section |The one and only section".to_string());
2010		new_vec_of_strings.push("Step|lots of bars".to_string());
2011		// ActionSequence should have length 6
2012		new_vec_of_strings.push("Action|Nominal|Nominal|TPV".to_string());
2013		new_vec_of_strings.push("Action|Nominal|Nominal|TPV|".to_string());
2014		new_vec_of_strings.push("Action|Nominal|Nominal|TPV||".to_string());
2015		new_vec_of_strings.push("Action|Nominal|Nominal|TPV|||".to_string());
2016		new_vec_of_strings.push("Action|Nominal|Nominal|TPV||||".to_string());
2017		new_vec_of_strings.push("Action|||TPV|||".to_string());
2018		new_vec_of_strings.push("Step|push the TPV limits - all should be true".to_string());
2019		// ActionSequence should have length 12
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|Nominal|Nominal|TRUE".to_string());
2023		new_vec_of_strings.push("Action|Nominal|Nominal|true".to_string());
2024		new_vec_of_strings.push("Action|Nominal|Nominal|T".to_string());
2025		new_vec_of_strings.push("Action|Nominal|Nominal|t".to_string());
2026		new_vec_of_strings.push("Action|Nominal|Nominal|Two Party Verification".to_string());
2027		new_vec_of_strings.push("Action|Nominal|Nominal|Y".to_string());
2028		new_vec_of_strings.push("Action|Nominal|Nominal|y".to_string());
2029		new_vec_of_strings.push("Action|Nominal|Nominal|Yes".to_string());
2030		new_vec_of_strings.push("Action|Nominal|Nominal|yes".to_string());
2031		new_vec_of_strings.push("Action|Nominal|Nominal|YES".to_string());
2032		new_vec_of_strings.push("Step|these TPVs should be false".to_string());
2033		// ActionSequence should have length 8
2034		new_vec_of_strings.push("Action|Nominal|TPV".to_string());
2035		new_vec_of_strings.push("Action|TPV".to_string());
2036		new_vec_of_strings.push("Action|Nominal|Nominal|naw|TPV".to_string());
2037		new_vec_of_strings.push("Action|Nominal|Nominal|naw|??|TPV".to_string());
2038		new_vec_of_strings.push("Action|Nominal|Nominal|naw|??|??|TPV".to_string());
2039		new_vec_of_strings.push("Action|Nominal|Nominal||??|??|TPV".to_string());
2040		new_vec_of_strings.push("Action|Nominal|Nominal|||??|TPV".to_string());
2041		new_vec_of_strings.push("Action|Nominal|Nominal||||TPV".to_string());
2042		return new_vec_of_strings;
2043	}
2044
2045	fn generate_ebml_with_diabolical_verification_methods() -> Vec<String> {
2046 		let mut new_vec_of_strings:Vec<String> = vec![];
2047		// One Section
2048		new_vec_of_strings.push("Section |The one and only section".to_string());
2049		new_vec_of_strings.push("Step|Analysis".to_string());
2050		// First step has 7 Verification SubSteps, all of them with VerificationMethod of Analysis
2051		new_vec_of_strings.push("Verification|A001|Analysis|Analysis".to_string());
2052		new_vec_of_strings.push("VERIFICATION|A002|Analysis|ANALYSIS".to_string());
2053		new_vec_of_strings.push("verification|A003|Analysis|analysis".to_string());
2054		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());
2055		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());
2056		new_vec_of_strings.push("Verification|A006|Analysis|A".to_string());
2057		new_vec_of_strings.push("Verification|A007|Analysis|        a".to_string());
2058		new_vec_of_strings.push("Step|Inspection".to_string());
2059		// Second step has 7 Verification SubSteps, all of them with VerificationMethod of Inspection
2060		new_vec_of_strings.push("Verification|I001|Inspection|Inspection".to_string());
2061		new_vec_of_strings.push("VERIFICATION|I002|Inspection|INSPECTION".to_string());
2062		new_vec_of_strings.push("verification|I003|Inspection|inspection".to_string());
2063		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());
2064		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());
2065		new_vec_of_strings.push("Verification|I006|Inspection|I".to_string());
2066		new_vec_of_strings.push("Verification|I007|Inspection|        i".to_string());
2067		new_vec_of_strings.push("Step|Test".to_string());
2068		// Third step has 7 Verification SubSteps, all of them with VerificationMethod of Test
2069		new_vec_of_strings.push("Verification|T001|Test|Test".to_string());
2070		new_vec_of_strings.push("VERIFICATION|T002|Test|TEST".to_string());
2071		new_vec_of_strings.push("verification|T003|Test|test".to_string());
2072		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());
2073		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());
2074		new_vec_of_strings.push("Verification|T006|Test|T".to_string());
2075		new_vec_of_strings.push("Verification|T007|Test|        t".to_string());
2076		new_vec_of_strings.push("Step|Sampling".to_string());
2077		// Third step has 9 Verification SubSteps, all of them with VerificationMethod of Sampling
2078		new_vec_of_strings.push("Verification|S001|Sampling|Sampling".to_string());
2079		new_vec_of_strings.push("VERIFICATION|S002|Sampling|SAMPLING".to_string());
2080		new_vec_of_strings.push("verification|S003|Sampling|sampling".to_string());
2081		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());
2082		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());
2083		new_vec_of_strings.push("Verification|S006|Sampling|S".to_string());
2084		new_vec_of_strings.push("Verification|S007|Sampling|        s".to_string());
2085		new_vec_of_strings.push("Verification|S008|Sampling| SAMPLE".to_string());
2086		new_vec_of_strings.push("Verification|S009|Sampling|        s a m PLE   ".to_string());
2087		new_vec_of_strings.push("Step|Demonstration".to_string());
2088		// Third step has 9 Verification SubSteps, all of them with VerificationMethod of Demonstration
2089		new_vec_of_strings.push("Verification|D001|Demonstration|Demonstration".to_string());
2090		new_vec_of_strings.push("VERIFICATION|D002|Demonstration|DEMONSTRATION".to_string());
2091		new_vec_of_strings.push("verification|D003|Demonstration|demonstration".to_string());
2092		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());
2093		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());
2094		new_vec_of_strings.push("Verification|D006|Demonstration|D".to_string());
2095		new_vec_of_strings.push("Verification|D007|Demonstration|        d".to_string());
2096		new_vec_of_strings.push("Verification|D007|Demonstration| DEMO".to_string());
2097		new_vec_of_strings.push("Verification|D007|Demonstration|        d EM o   ".to_string());
2098		/*
2099		"DEMONSTRATION"|"DEMO"|"D"	=> VerificationMethod::Demonstration,
2100		"INSPECTION"|"I"			=> VerificationMethod::Inspection,
2101		"ANALYSIS"|"A"				=> VerificationMethod::Analysis,
2102		"SAMPLING"|"SAMPLE"|"S"		=> VerificationMethod::Sampling,
2103		"TEST"|"T"					=> VerificationMethod::Test,
2104		*/
2105		return new_vec_of_strings;
2106	}
2107
2108	fn generate_ebml_with_diabolical_image_lines() -> Vec<String> {
2109		let mut new_vec_of_strings:Vec<String> = vec![];
2110		// One Section
2111		new_vec_of_strings.push("Section |The one and only section".to_string());
2112		new_vec_of_strings.push("Step|Strange three-part image lines".to_string());
2113		// First step has 5 Images
2114		new_vec_of_strings.push("Image|filename.ext|Caption".to_string());
2115		new_vec_of_strings.push("IMAGE|filename.ext|Caption".to_string());
2116		new_vec_of_strings.push("image|filename.ext|Caption".to_string());
2117		new_vec_of_strings.push("  iM  Ag     E   |filename.ext|Caption".to_string());
2118		new_vec_of_strings.push("           IMage |filename.ext|Caption".to_string());
2119
2120		new_vec_of_strings.push("Step|lots of bars, empty parts".to_string());
2121		// Second step has 5 Images, all of which should have default text for filename and Caption
2122		new_vec_of_strings.push("image |".to_string());
2123		new_vec_of_strings.push("image ||".to_string());
2124		new_vec_of_strings.push("image |||".to_string());
2125		new_vec_of_strings.push("image ||||".to_string());
2126		new_vec_of_strings.push("image |||||||||||||||".to_string());
2127
2128		new_vec_of_strings.push("Step|lots of bars, empty parts".to_string());
2129		// Third step has 5 Images, all of which should have default text for filename and Caption
2130		new_vec_of_strings.push("image |||filename.ext|Caption".to_string());
2131		new_vec_of_strings.push("image ||||filename.ext|Caption".to_string());
2132		new_vec_of_strings.push("image |||||filename.ext|Caption".to_string());
2133		new_vec_of_strings.push("image ||||||filename.ext|Caption".to_string());
2134		new_vec_of_strings.push("image |||||||filename.ext|Caption".to_string());
2135		return new_vec_of_strings;
2136	}
2137
2138	fn generate_ebml_with_diabolical_resources() -> Vec<String> {
2139		let mut new_vec_of_strings:Vec<String> = vec![];
2140		// One Section
2141		new_vec_of_strings.push("Section |The one and only section".to_string());
2142		new_vec_of_strings.push("Step|Strange resource lines".to_string());
2143		// First step has 5 Resources
2144		new_vec_of_strings.push("Resource|Nominal".to_string());
2145		new_vec_of_strings.push("RESOURCE|Nominal".to_string());
2146		new_vec_of_strings.push("resource|Nominal".to_string());
2147		new_vec_of_strings.push(" r e s o u r c e |Nominal".to_string());
2148		new_vec_of_strings.push(" rEs oUr cE      |Nominal".to_string());
2149		new_vec_of_strings.push("Step|Strange resource lines".to_string());
2150		// First step has 5 Resources
2151		new_vec_of_strings.push("Resource|".to_string());
2152		new_vec_of_strings.push("RESOURCE||".to_string());
2153		new_vec_of_strings.push("resource|||".to_string());
2154		new_vec_of_strings.push(" r e s o u r c e ||||".to_string());
2155		new_vec_of_strings.push(" rEs oUr cE      |||||".to_string());
2156		return new_vec_of_strings;
2157	}
2158
2159	fn generate_ebml_with_csv_table_embedded_1000_rows() -> Vec<String> {
2160		let mut new_vec_of_strings:Vec<String> = vec![];
2161		// One Section
2162		new_vec_of_strings.push("Section |The one and only section".to_string());
2163		new_vec_of_strings.push("Step|Strange resource lines".to_string());
2164		// First step has one embedded CSV table
2165		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2166		for _ in 0..1000 {
2167			new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2168		}
2169		new_vec_of_strings.push("CSV End |".to_string());
2170		return new_vec_of_strings;
2171	}
2172
2173	fn generate_ebml_with_csv_table_embedded_rows_wrong_lengths() -> Vec<String> {
2174		let mut new_vec_of_strings:Vec<String> = vec![];
2175		// One Section
2176		new_vec_of_strings.push("Section |The one and only section".to_string());
2177		new_vec_of_strings.push("Step|One and only step".to_string());
2178		// First step has one embedded CSV table
2179		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2180		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2181		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine".to_string());
2182		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight".to_string());
2183		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven".to_string());
2184		new_vec_of_strings.push("One,Two,Three,Four,Five,Six".to_string());
2185		new_vec_of_strings.push("One,Two,Three,Four,Five".to_string());
2186		new_vec_of_strings.push("One,Two,Three,Four".to_string());
2187		new_vec_of_strings.push("One,Two,Three".to_string());
2188		new_vec_of_strings.push("One,Two".to_string());
2189		new_vec_of_strings.push("One".to_string());
2190		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven".to_string());
2191		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve".to_string());
2192		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen".to_string());
2193		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen,Fourteen".to_string());
2194		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Twelve,Thirteen,Fourteen,Fifteen".to_string());
2195		new_vec_of_strings.push("CSV End |".to_string());
2196		return new_vec_of_strings;
2197	}
2198
2199	fn generate_ebml_with_csv_table_embedded_edge_cases() -> Vec<String> {
2200		let mut new_vec_of_strings:Vec<String> = vec![];
2201		// One Section
2202		new_vec_of_strings.push("Section |The one and only section".to_string());
2203		new_vec_of_strings.push("Step|One and only step".to_string());
2204		// First step has one embedded CSV table
2205		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2206		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());
2207		new_vec_of_strings.push("Wait, so you're saying the line above is CSV and not EBML?".to_string());
2208		new_vec_of_strings.push("Yes, that's exactly what I'm saying. YOU are even a CSV line, my friend.".to_string());
2209		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());
2210		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());
2211		new_vec_of_strings.push("CSV End |".to_string());
2212		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Second
2213		new_vec_of_strings.push("CSV End |".to_string());
2214		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Third
2215		new_vec_of_strings.push("CSV End |".to_string());
2216		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Fourth
2217		new_vec_of_strings.push("CSV End |".to_string());
2218		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Fifth
2219		new_vec_of_strings.push("CSV End |".to_string());
2220		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Sixth
2221		new_vec_of_strings.push("CSV End |".to_string());
2222		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Seventh
2223		new_vec_of_strings.push("".to_string());
2224		new_vec_of_strings.push("".to_string());
2225		new_vec_of_strings.push("".to_string());
2226		new_vec_of_strings.push("".to_string());
2227		new_vec_of_strings.push("".to_string());
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("CSV End |".to_string());
2232		new_vec_of_strings.push("Command | rm -rf lol".to_string());	 // Eigth SUBSTEP (first command)
2233		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Ninth SUBSTEP (eighth table...)
2234		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2235		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2236		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
2237		new_vec_of_strings.push(",,,,CUCU,,,,,".to_string()); // That's the fifth column in the fourth row, of the ninth substep...
2238		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
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("CSV End |".to_string());
2243		return new_vec_of_strings;
2244	}
2245
2246	fn generate_ebml_with_csv_table_embedded_no_end_line() -> Vec<String> {
2247		let mut new_vec_of_strings:Vec<String> = vec![];
2248		// One Section
2249		new_vec_of_strings.push("Section |The one and only section".to_string());
2250		new_vec_of_strings.push("Step|One and only step".to_string());
2251		// First step has one embedded CSV table, but the "Start" line is missing
2252		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2253		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2254		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2255		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2256		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2257		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());	
2258		// new_vec_of_strings.push("CSV End |".to_string());
2259		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());
2260		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());
2261		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());
2262		new_vec_of_strings.push("CSV Start | This SHOULD be read as a CSV line for the ONE table SubStep...".to_string());
2263		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2264		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2265		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2266		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2267		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2268		//new_vec_of_strings.push("CSV End |".to_string());
2269		return new_vec_of_strings;
2270	}
2271
2272	fn generate_ebml_with_csv_table_embedded_no_start_line() -> Vec<String> {
2273		let mut new_vec_of_strings:Vec<String> = vec![];
2274		// One Section
2275		new_vec_of_strings.push("Section |The one and only section".to_string());
2276		new_vec_of_strings.push("Step|One and only step".to_string());
2277		// First step has one embedded CSV table, but the "Start" line is missing
2278		//new_vec_of_strings.push("CSV Start | Caption text".to_string());
2279		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2280		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine".to_string());
2281		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight".to_string());
2282		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven".to_string());
2283		new_vec_of_strings.push("One,Two,Three,Four,Five,Six".to_string());
2284		new_vec_of_strings.push("One,Two,Three,Four,Five".to_string());
2285		new_vec_of_strings.push("One,Two,Three,Four".to_string());
2286		new_vec_of_strings.push("One,Two,Three".to_string());
2287		new_vec_of_strings.push("One,Two".to_string());
2288		new_vec_of_strings.push("One".to_string());
2289		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());
2290		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven".to_string());
2291		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve".to_string());
2292		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen".to_string());
2293		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen,Fourteen".to_string());
2294		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Twelve,Thirteen,Fourteen,Fifteen".to_string());
2295		new_vec_of_strings.push("CSV End |".to_string());
2296		new_vec_of_strings.push("WARNING | This is the second SubStep that should be found...".to_string());
2297		return new_vec_of_strings;
2298	}
2299
2300	fn generate_ebml_with_csv_table_external() -> Vec<String> {
2301		let mut new_vec_of_strings:Vec<String> = vec![];
2302		// One Section
2303		new_vec_of_strings.push("Section |The one and only section".to_string());
2304		new_vec_of_strings.push("Step|One and only step".to_string());
2305		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
2306		new_vec_of_strings.push("WARNING | This is the second SubStep that should be found...".to_string());
2307		new_vec_of_strings.push("WARNING | This is the third SubStep that should be found...".to_string());
2308		new_vec_of_strings.push("WARNING | This is the fourth SubStep that should be found...".to_string());
2309		return new_vec_of_strings;
2310	}
2311
2312	fn generate_csv_table_external() -> Vec<String> {
2313		let mut new_vec_of_strings:Vec<String> = vec![];
2314		new_vec_of_strings.push("H1,H2,H3".to_string());
2315		new_vec_of_strings.push("D1,D2,D3".to_string());
2316		new_vec_of_strings.push("D4,D5,D6".to_string());
2317		new_vec_of_strings.push("D7,D8,D9".to_string());
2318		return new_vec_of_strings;
2319	}
2320
2321	fn generate_ebml_with_csv_table_external_stressing() -> Vec<String> {
2322		let mut new_vec_of_strings:Vec<String> = vec![];
2323		// One Section
2324		new_vec_of_strings.push("Section |The one and only section".to_string());
2325		new_vec_of_strings.push("Step|One and only step".to_string());
2326
2327		// First five valid SubStep lines: nominal... call an external file
2328		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
2329		new_vec_of_strings.push("      C S V  F i l e        |        test.csv            |           Caption text          ".to_string());
2330		new_vec_of_strings.push("csvfile|test.csv|Caption text".to_string());
2331		new_vec_of_strings.push("  cSvFiLe  |    test.csv| Caption text".to_string());
2332		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
2333
2334		// False SubSteps that won't be counted: typos
2335		new_vec_of_strings.push("CSVee File | test.csv | Caption text".to_string());
2336		new_vec_of_strings.push("CSV Flie | test.csv | Caption text".to_string());
2337		new_vec_of_strings.push("CSV Fille | test.csv | Caption text".to_string());
2338		new_vec_of_strings.push("CVS File | test.csv | Caption text".to_string());
2339		new_vec_of_strings.push("Cee Ess Vee File | test.csv | Caption text".to_string());
2340
2341		// Valid "embedded" CSV file call, with an "external" CSV call within it, which will be TWO SubSteps... but not more...
2342		// Furthermore, the "embedded" one will count the "external" CSV EBML line as one of its CSV lines... so it will have 3 rows...
2343		new_vec_of_strings.push("CSV Start | Caption text".to_string());
2344		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2345		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
2346		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2347		new_vec_of_strings.push("CSV End |".to_string());
2348
2349		// Running totals:
2350		// One Section
2351		// One Step
2352		// Seven SubSteps, all tables
2353		// > 0-4 : (4,3)
2354		// > 5   : (3,10)
2355		// > 6   : (4,3)
2356		return new_vec_of_strings;
2357	}
2358	
2359	// end test-only functions	
2360	
2361	#[test]
2362	fn test_read_file_all_comments() {
2363		let file_name = "test_comments_only.ebml".to_string();
2364		create_test_file(&file_name,generate_ebml_with_diabolical_comments());
2365		let process = read_ebml(&file_name);
2366		assert_eq!(process.get_all_sections().len(),0);
2367		assert_eq!(process.get_all_resources().len(),0);
2368		assert_eq!(process.get_all_verifications().len(),0);
2369		assert_eq!(process.get_all_templates().len(),0);
2370		destroy_test_file(&file_name);
2371	}
2372
2373	#[test]
2374	fn test_set_process_meta_twice() {
2375		let file_name = "test_process_meta_set_twice.ebml".to_string();
2376		create_test_file(&file_name,generate_ebml_set_meta_twice());
2377		let process = read_ebml(&file_name);
2378		assert_eq!(process.get_number(),"Second Number");
2379		assert_eq!(process.get_title(),"Second Title");
2380		assert_eq!(process.get_author(),"Second Author");
2381		assert_eq!(process.get_reviewer(),"Second Reviewer");
2382		assert_eq!(process.get_subject(),"Second Subject");
2383		assert_eq!(process.get_subject_image(),"Second SubjectImage");
2384		assert_eq!(process.get_product(),"Second Product");
2385		assert_eq!(process.get_product_image(),"Second ProductImage");
2386		destroy_test_file(&file_name);
2387	}
2388
2389	#[test]
2390	fn test_read_file_1000_sections() {
2391		let file_name = "test_1000_sections.ebml".to_string();
2392		create_test_file(&file_name,generate_ebml_with_1000_sections());
2393		let process = read_ebml(&file_name);
2394		assert_eq!(process.get_all_sections().len(),1000);
2395		destroy_test_file(&file_name);
2396	}
2397
2398	#[test]
2399	fn test_read_file_1000_steps() {
2400		let file_name = "test_1000_steps.ebml".to_string();
2401		create_test_file(&file_name,generate_ebml_with_1000_steps_in_one_section());
2402		let process = read_ebml(&file_name);
2403		assert_eq!(process.get_all_sections().len(),1);
2404		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1000);
2405		destroy_test_file(&file_name);
2406	}
2407
2408	#[test]
2409	fn test_read_file_1000_verifications() {
2410		let file_name = "test_1000_verifications.ebml".to_string();
2411		create_test_file(&file_name,generate_ebml_with_1000_verifications_in_one_step());
2412		let process = read_ebml(&file_name);
2413		assert_eq!(process.get_all_sections().len(),1);
2414		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2415		assert_eq!(process.get_all_verifications().len(),1000);
2416		assert_eq!(process.get_all_verifications()[500].1,"Step 1.1");
2417		destroy_test_file(&file_name);
2418	}
2419
2420	#[test]
2421	fn test_read_file_1000_resources() {
2422		let file_name = "test_1000_resources.ebml".to_string();
2423		create_test_file(&file_name,generate_ebml_with_1000_resources_in_one_step());
2424		let process = read_ebml(&file_name);
2425		assert_eq!(process.get_all_sections().len(),1);
2426		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2427		assert_eq!(process.get_all_resources().len(),1000);
2428		assert_eq!(process.get_all_calibrated_resources().len(),500);
2429		assert_eq!(process.get_all_resources()[500].1,"Step 1.1");
2430		destroy_test_file(&file_name);
2431	}
2432
2433	#[test]
2434	fn test_read_file_1000_actions() {
2435		let file_name = "test_1000_actions.ebml".to_string();
2436		create_test_file(&file_name,generate_ebml_with_1000_actions_in_one_step());
2437		let process = read_ebml(&file_name);
2438		assert_eq!(process.get_all_sections().len(),1);
2439		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2440		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
2441		match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2442			SubStep::ActionSequence(list) => assert_eq!(list.len(),1000),
2443			_ => assert!(1==0),
2444		};		
2445		destroy_test_file(&file_name);
2446	}
2447
2448	#[test]
2449	fn test_read_file_300_tpv_700_non_tpv() {
2450		let file_name = "test_300_tpv_700_non_tpv.ebml".to_string();
2451		create_test_file(&file_name,generate_ebml_with_300_tpv_700_non_tpv_actions_in_one_step());
2452		let process = read_ebml(&file_name);
2453		assert_eq!(process.get_tpv_count(),300);
2454		assert_eq!(process.get_non_tpv_count(),700);
2455		destroy_test_file(&file_name);
2456	}
2457
2458	#[test]
2459	fn test_read_file_1000_objectives(){
2460		let file_name = "test_1000_objectives.ebml".to_string();
2461		create_test_file(&file_name,generate_ebml_with_1000_objectives_in_one_step());
2462		let process = read_ebml(&file_name);
2463		assert_eq!(process.get_all_sections().len(),1);
2464		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2465		assert_eq!(process.get_all_objectives().len(),1000);
2466		destroy_test_file(&file_name);
2467	}
2468
2469	#[test]
2470	fn test_read_file_1000_out_of_scopes(){
2471		let file_name = "test_1000_out_of_scopes.ebml".to_string();
2472		create_test_file(&file_name,generate_ebml_with_1000_out_of_scopes_in_one_step());
2473		let process = read_ebml(&file_name);
2474		assert_eq!(process.get_all_sections().len(),1);
2475		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2476		assert_eq!(process.get_all_out_of_scopes().len(),1000);
2477		destroy_test_file(&file_name);
2478	}
2479
2480	#[test]
2481	fn test_read_file_whitespace_first_part() {
2482		let file_name = "test_whitespace_first_part.ebml".to_string();
2483		create_test_file(&file_name,generate_ebml_with_diabolical_whitespace_first_part());
2484		let process = read_ebml(&file_name);
2485		assert_eq!(process.get_all_sections().len(),10);
2486		destroy_test_file(&file_name);
2487	}
2488
2489	#[test]
2490	fn test_extract_section_and_step_triggers() {
2491		let file_name = "test_extract_section_triggers.ebml".to_string();
2492		create_test_file(&file_name,generate_ebml_with_diabolical_section_and_step_triggers());
2493		let process = read_ebml(&file_name);
2494		// SHOULD BE: Three Sections, each with three Steps, but formatted hella goofy
2495		assert_eq!(process.get_all_sections().len(),3);
2496		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),3);
2497		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),6);
2498		assert_eq!(process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps().len(),1);
2499		assert_eq!(process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps().len(),1);
2500		assert_eq!(process.get_all_sections()[1].get_all_steps().len(),3);
2501		assert_eq!(process.get_all_sections()[1].get_all_steps()[0].get_all_sub_steps().len(),1);
2502		assert_eq!(process.get_all_sections()[1].get_all_steps()[1].get_all_sub_steps().len(),1);
2503		assert_eq!(process.get_all_sections()[1].get_all_steps()[2].get_all_sub_steps().len(),1);
2504		assert_eq!(process.get_all_sections()[2].get_all_steps().len(),3);
2505		assert_eq!(process.get_all_sections()[2].get_all_steps()[0].get_all_sub_steps().len(),1);
2506		assert_eq!(process.get_all_sections()[2].get_all_steps()[1].get_all_sub_steps().len(),1);
2507		assert_eq!(process.get_all_sections()[2].get_all_steps()[2].get_all_sub_steps().len(),1);
2508		destroy_test_file(&file_name);
2509	}
2510
2511	#[test]
2512	fn test_action_lines() {
2513		let file_name = "test_action_lines.ebml".to_string();
2514		create_test_file(&file_name,generate_ebml_with_diabolical_actions());
2515		let process = read_ebml(&file_name);
2516		assert_eq!(process.get_all_sections().len(),1);
2517		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),3);
2518		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
2519			match substep {
2520				SubStep::ActionSequence(list) => {
2521					assert_eq!(list.len(),6);
2522					for a in list { assert_eq!(a.get_tpv(),&true); }
2523				},
2524				_ => (),
2525			}
2526		}
2527		for substep in process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps() {
2528			match substep {
2529				SubStep::ActionSequence(list) => {
2530					assert_eq!(list.len(),12);
2531					for a in list { assert_eq!(a.get_tpv(),&true); }
2532				},
2533				_ => (),
2534			}
2535		}
2536		for substep in process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps() {
2537			match substep {
2538				SubStep::ActionSequence(list) => {
2539					assert_eq!(list.len(),8);
2540					for a in list { assert_eq!(a.get_tpv(),&false); }
2541				},
2542				_ => (),
2543			}
2544		}
2545		destroy_test_file(&file_name);
2546	}
2547
2548	#[test]
2549	fn test_verification_methods() {
2550		let file_name = "test_verification_methods.ebml".to_string();
2551		create_test_file(&file_name,generate_ebml_with_diabolical_verification_methods());
2552		let process = read_ebml(&file_name);
2553		assert_eq!(process.get_all_sections().len(),1);
2554		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),5);
2555		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),};}
2556		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),};}
2557		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),};}
2558		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),};}
2559		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),};}
2560		destroy_test_file(&file_name);
2561	}
2562
2563	#[test]
2564	fn test_image_lines() {
2565		let file_name = "test_image_lines.ebml".to_string();
2566		create_test_file(&file_name,generate_ebml_with_diabolical_image_lines());
2567		let process = read_ebml(&file_name);
2568		// SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string())
2569		assert_eq!(process.get_all_sections().len(),1);
2570		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),3);
2571		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),5);
2572		assert_eq!(process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps().len(),5);
2573		assert_eq!(process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps().len(),5);
2574		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() { 
2575			match substep {
2576				SubStep::Image(f,c) => {
2577					match f.as_str() {"filename.ext" => (), _ => assert!(1==0),};
2578					match c.as_str() {"Caption" => (), _ => assert!(1==0),};
2579				},
2580				_ => assert!(1==0),
2581			};
2582		};
2583		for substep in process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps() { 
2584			match substep {
2585				SubStep::Image(f,c) => {
2586					match f.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2587					match c.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2588				},
2589				_ => assert!(1==0),
2590			};
2591		};
2592		for substep in process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps() { 
2593			match substep {
2594				SubStep::Image(f,c) => {
2595					match f.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2596					match c.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2597				},
2598				_ => assert!(1==0),
2599			};
2600		};
2601		
2602		destroy_test_file(&file_name);
2603	}
2604
2605	#[test]
2606	fn test_resource_lines() {
2607		let file_name = "test_resource_lines.ebml".to_string();
2608		create_test_file(&file_name,generate_ebml_with_diabolical_resources());
2609		let process = read_ebml(&file_name);
2610		assert_eq!(process.get_all_sections().len(),1);
2611		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),2);
2612		let mut count_one_point_one = 0;
2613		let mut count_one_point_two = 0;
2614		for (resource,stepno) in process.get_all_resources() {
2615			match stepno.as_str() {
2616				"Step 1.1"	=> {
2617					count_one_point_one +=1;
2618					assert_eq!(resource.get_name().as_str(),"Nominal");
2619				},
2620				"Step 1.2"	=> {
2621					count_one_point_two +=1;
2622					assert_eq!(resource.get_name().as_str(),"ERROR: NO RESOURCE IDENTIFIED");
2623				},
2624				_ 			=> assert!(1==0),
2625			};
2626		}
2627		assert_eq!(count_one_point_one,5);
2628		assert_eq!(count_one_point_two,5);
2629
2630		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_resources().len(),5);
2631		for resource in process.get_all_sections()[0].get_all_steps()[0].get_resources() {
2632			assert_eq!(resource.get_name().as_str(),"Nominal");
2633		}
2634		assert_eq!(process.get_all_sections()[0].get_all_steps()[1].get_resources().len(),5);
2635		for resource in process.get_all_sections()[0].get_all_steps()[1].get_resources() {
2636			assert_eq!(resource.get_name().as_str(),"ERROR: NO RESOURCE IDENTIFIED");
2637		}
2638		destroy_test_file(&file_name);
2639	}
2640
2641	#[test]
2642	fn test_calibrated_resource_lines() {
2643		let file_name = "test_calibrated_resource_lines.ebml".to_string();
2644		create_test_file(&file_name,generate_ebml_with_diabolical_calibrated_resources());
2645		let process = read_ebml(&file_name);
2646		assert_eq!(process.get_all_sections().len(),1);
2647		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2648		assert_eq!(process.get_all_resources().len(),21);
2649		assert_eq!(process.get_all_calibrated_resources().len(),20);
2650		destroy_test_file(&file_name);
2651	}
2652
2653	#[test]
2654	fn test_get_all_commands_count() {
2655		let file_name = "test_get_all_commands_count.ebml".to_string();
2656		create_test_file(&file_name,generate_ebml_with_diabolical_section_and_step_triggers());
2657		let process = read_ebml(&file_name);
2658		// SHOULD BE: Three Sections, each with three Steps, and a total of 9 command lines
2659		assert_eq!(process.get_all_command_lines().len(),9);
2660		destroy_test_file(&file_name);
2661	}
2662
2663	#[test]
2664	fn test_csv_table_embedded_1000_rows() {
2665		let file_name = "test_csv_table_embedded_1000_rows.ebml".to_string();
2666		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_1000_rows());
2667		let process = read_ebml(&file_name);
2668		assert_eq!(process.get_all_sections().len(),1);
2669		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2670		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
2671		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2672			SubStep::Table(table) => table,
2673			_ => &Table::new(),
2674		};
2675		let (rows,cols) = table.get_size();
2676		assert_eq!(rows,1000);
2677		assert_eq!(cols,10);
2678		destroy_test_file(&file_name);
2679	}
2680
2681	#[test]
2682	fn test_csv_table_embedded_data_rows_wrong_lengths() {
2683		let file_name = "test_csv_table_embedded_rows_wrong_lengths.ebml".to_string();
2684		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_rows_wrong_lengths());
2685		let process = read_ebml(&file_name);
2686		assert_eq!(process.get_all_sections().len(),1);
2687		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2688		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
2689		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2690			SubStep::Table(table) => table,
2691			_ => &Table::new(),
2692		};
2693		let (rows,cols) = table.get_size();
2694		assert_eq!(rows,15);
2695		assert_eq!(cols,10);
2696		assert_eq!(table.get_row(1)[9],"".to_string());
2697		assert_eq!(table.get_row(9)[1],"".to_string());
2698		assert_eq!(table.get_row(14)[9],"Ten".to_string());
2699		destroy_test_file(&file_name);
2700	}
2701
2702	#[test]
2703	fn test_csv_table_embedded_edge_cases() {
2704		let file_name = "test_csv_table_embedded_edge_cases.ebml".to_string();
2705		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_edge_cases());
2706		let process = read_ebml(&file_name);
2707		assert_eq!(process.get_all_sections().len(),1);
2708		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2709		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),9);
2710		
2711		// First table: 
2712		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2713			SubStep::Table(table) => table,
2714			_ => &Table::new(),
2715		};
2716		let (rows,cols) = table.get_size();
2717		assert_eq!(rows,5);
2718		assert_eq!(cols,3);
2719		
2720		// Second table: 
2721		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] {
2722			SubStep::Table(table) => table,
2723			_ => &Table::new(),
2724		};
2725		let (rows,cols) = table.get_size();
2726		assert_eq!(rows,0);
2727		assert_eq!(cols,0);
2728
2729		// Seventh table: 
2730		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[6] {
2731			SubStep::Table(table) => table,
2732			_ => &Table::new(),
2733		};
2734		let (rows,cols) = table.get_size();
2735		assert_eq!(rows,8);
2736		assert_eq!(cols,1);
2737		assert_eq!(table.get_caption(),"Caption text");
2738
2739		// Ninth SubStep is the Eighth table...: 
2740		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[8] {
2741			SubStep::Table(table) => table,
2742			_ => &Table::new(),
2743		};
2744		let (rows,cols) = table.get_size();
2745		assert_eq!(rows,8);
2746		assert_eq!(cols,14);
2747		assert_eq!(table.get_caption(),"Caption text");
2748		assert_eq!(table.get_row(3)[4],"CUCU".to_string());
2749		assert_eq!(table.get_row(0)[0],"".to_string());
2750
2751		destroy_test_file(&file_name);
2752	}
2753
2754	#[test]
2755	fn test_csv_table_embedded_no_end_line() {
2756		let file_name = "test_csv_table_embedded_no_end_line.ebml".to_string();
2757		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_no_end_line());
2758		let process = read_ebml(&file_name);
2759		assert_eq!(process.get_all_sections().len(),1);
2760		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2761		// The number of SubSteps is tricky here... if working as expected, we should have:
2762		// > ONE Table as the first SubStep
2763		// > Three Warning SubSteps
2764		// So four SubSteps total...
2765		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),4);
2766		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2767			SubStep::Table(table) => table,
2768			_ => &Table::new(),
2769		};
2770		let (rows,cols) = table.get_size();
2771		// The number of rows in this ONE table is tricky... because the user looks like they wanted two tables, with three warnings in between.
2772		// If working as expected, we should have:
2773		// > 5 intended CSV rows
2774		// > 3 Warning rows that are unintentionally read in as CSV rows because the CSV End is missing
2775		// > 1 CSV Start line following the warnings, read in as CSV unintentionally
2776		// > 5 intended CSV rows, but intended for a second table
2777		// 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...
2778		assert_eq!(rows,14);
2779		assert_eq!(cols,10);
2780		destroy_test_file(&file_name);		
2781	}
2782
2783	#[test]
2784	fn test_csv_table_embedded_no_start_line() {
2785		let file_name = "test_csv_table_embedded_no_start_line.ebml".to_string();
2786		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_no_start_line());
2787		let process = read_ebml(&file_name);
2788		assert_eq!(process.get_all_sections().len(),1);
2789		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2790		// The number of SubSteps is tricky here... if working as expected, we should have:
2791		// > ZERO tables...
2792		// > ONE Context
2793		// > ONE Warning
2794		// So two SubSteps, but neither of them should be Table
2795		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),2);
2796		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] { SubStep::Context(_) => true, _ => false, });
2797		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] { SubStep::Warning(_) => true, _ => false, });
2798		destroy_test_file(&file_name);
2799	}
2800
2801	#[test]
2802	fn test_csv_table_external() {
2803		let file_name_ebml = "././test_csv_table_external.ebml".to_string();
2804		create_test_file(&file_name_ebml,generate_ebml_with_csv_table_external());
2805		
2806		let file_name_csv = "././test.csv".to_string();
2807		create_test_file(&file_name_csv,generate_csv_table_external());		
2808
2809		let process = read_ebml(&file_name_ebml);
2810		
2811		assert_eq!(process.get_all_sections().len(),1);
2812		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2813		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),4);
2814		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] { SubStep::Table(_) => true, _ => false, });
2815		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2816			SubStep::Table(table) => table,
2817			_ => &Table::new(),
2818		};
2819		let (rows,cols) = table.get_size();
2820		assert_eq!(rows,4);
2821		assert_eq!(cols,3);
2822		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] { SubStep::Warning(_) => true, _ => false, });
2823		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[2] { SubStep::Warning(_) => true, _ => false, });
2824		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[3] { SubStep::Warning(_) => true, _ => false, });
2825		
2826		destroy_test_file(&file_name_ebml);
2827		destroy_test_file(&file_name_csv);
2828	}
2829
2830	#[test]
2831	fn test_csv_table_external_stressing() {
2832		let file_name_ebml = "././test_csv_table_external_stressing.ebml".to_string();
2833		create_test_file(&file_name_ebml,generate_ebml_with_csv_table_external_stressing());
2834		
2835		let file_name_csv = "././test.csv".to_string();
2836		create_test_file(&file_name_csv,generate_csv_table_external());		
2837
2838		let process = read_ebml(&file_name_ebml);
2839
2840		// Running totals:
2841		// One Section
2842		// One Step
2843		// Seven SubSteps, all tables
2844		// > 0-4 : (4,3)
2845		// > 5   : (3,10)
2846		// > 6   : (4,3)
2847		assert_eq!(process.get_all_sections().len(),1);
2848		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2849		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),7);
2850
2851		// 0
2852		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] { SubStep::Table(_) => true, _ => false, });
2853		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2854			SubStep::Table(table) => table,
2855			_ => &Table::new(),
2856		};
2857		let (rows,cols) = table.get_size();
2858		assert_eq!(rows,4);
2859		assert_eq!(cols,3);
2860
2861		// 1
2862		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] { SubStep::Table(_) => true, _ => false, });
2863		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] {
2864			SubStep::Table(table) => table,
2865			_ => &Table::new(),
2866		};
2867		let (rows,cols) = table.get_size();
2868		assert_eq!(rows,4);
2869		assert_eq!(cols,3);
2870
2871		// 2
2872		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[2] { SubStep::Table(_) => true, _ => false, });
2873		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[2] {
2874			SubStep::Table(table) => table,
2875			_ => &Table::new(),
2876		};
2877		let (rows,cols) = table.get_size();
2878		assert_eq!(rows,4);
2879		assert_eq!(cols,3);
2880
2881		// 3
2882		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[3] { SubStep::Table(_) => true, _ => false, });
2883		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[3] {
2884			SubStep::Table(table) => table,
2885			_ => &Table::new(),
2886		};
2887		let (rows,cols) = table.get_size();
2888		assert_eq!(rows,4);
2889		assert_eq!(cols,3);
2890
2891		// 4
2892		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[4] { SubStep::Table(_) => true, _ => false, });
2893		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[4] {
2894			SubStep::Table(table) => table,
2895			_ => &Table::new(),
2896		};
2897		let (rows,cols) = table.get_size();
2898		assert_eq!(rows,4);
2899		assert_eq!(cols,3);
2900
2901		// 5
2902		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[5] { SubStep::Table(_) => true, _ => false, });
2903		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[5] {
2904			SubStep::Table(table) => table,
2905			_ => &Table::new(),
2906		};
2907		let (rows,cols) = table.get_size();
2908		assert_eq!(rows,3);
2909		assert_eq!(cols,10);
2910
2911		// 6
2912		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[6] { SubStep::Table(_) => true, _ => false, });
2913		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[6] {
2914			SubStep::Table(table) => table,
2915			_ => &Table::new(),
2916		};
2917		let (rows,cols) = table.get_size();
2918		assert_eq!(rows,4);
2919		assert_eq!(cols,3);
2920
2921		destroy_test_file(&file_name_ebml);
2922		destroy_test_file(&file_name_csv);
2923	}
2924
2925	// helper functions to generate ebml for new aliases added >0.80.0
2926
2927	fn generate_ebml_with_section_alias() -> Vec<String> {
2928		let mut new_vec_of_strings:Vec<String> = vec![];
2929		new_vec_of_strings.push("Section|Old-style Section".to_string());
2930		new_vec_of_strings.push("SEC|Alias Section".to_string());
2931		new_vec_of_strings.push(" s e c |Alias Section".to_string());
2932		new_vec_of_strings.push(" SE   c|Alias Section".to_string());
2933		return new_vec_of_strings;
2934		// 4 sections (1 old-style, 3 new alias)
2935	}
2936
2937	fn generate_ebml_with_section_reference_alias() -> Vec<String> {
2938		let mut new_vec_of_strings:Vec<String> = vec![];
2939		new_vec_of_strings.push("Section Reference|Old-style Section Reference|Section Title".to_string());
2940		new_vec_of_strings.push("SECREF|Alias Section|Section Title".to_string());
2941		new_vec_of_strings.push(" s e c r e f |Alias Section|Section Title".to_string());
2942		new_vec_of_strings.push(" SE   c   RE   f   |      Alias Section     | Section Title".to_string());
2943		return new_vec_of_strings;
2944		// 4 sections references (1 old-style, 3 new alias)
2945	}
2946
2947	fn generate_ebml_with_step_alias() -> Vec<String> {
2948		let mut new_vec_of_strings:Vec<String> = vec![];
2949		new_vec_of_strings.push("Section|One section".to_string());
2950		new_vec_of_strings.push("Step|Old-style Step".to_string());
2951		new_vec_of_strings.push("STP|Alias Step".to_string());
2952		new_vec_of_strings.push(" s t p |Alias Step".to_string());
2953		new_vec_of_strings.push(" ST    p|Alias Step".to_string());
2954		return new_vec_of_strings;
2955		// 1 section, 4 steps (1 old-style, 3 new alias)
2956	}
2957
2958	fn generate_ebml_with_context_alias() -> Vec<String> {
2959		let mut new_vec_of_strings:Vec<String> = vec![];
2960		new_vec_of_strings.push("Section|One section".to_string());
2961		new_vec_of_strings.push("Step|One step".to_string());
2962		new_vec_of_strings.push("Context|Old-style Context".to_string());
2963		new_vec_of_strings.push("Comment|Alias Context".to_string());
2964		new_vec_of_strings.push(" c o m m e n t |Alias Context".to_string());
2965		new_vec_of_strings.push("TXT|Alias Context".to_string());
2966		new_vec_of_strings.push(" t x t |Alias Context".to_string());
2967		new_vec_of_strings.push("CMT|Alias Context".to_string());
2968		new_vec_of_strings.push(" c m t |Alias Context".to_string());
2969		return new_vec_of_strings;
2970		// 1 section, 1 step, 7 substeps that are all Context (1 old-style, 6 new alias)
2971	}
2972
2973	fn generate_ebml_with_command_alias() -> Vec<String> {
2974		let mut new_vec_of_strings:Vec<String> = vec![];
2975		new_vec_of_strings.push("Section|One section".to_string());
2976		new_vec_of_strings.push("Step|One step".to_string());
2977		new_vec_of_strings.push("Command|Old-style Command".to_string());
2978		new_vec_of_strings.push("CMD|Alias Command".to_string());
2979		new_vec_of_strings.push(" c m d |Alias Command".to_string());
2980		new_vec_of_strings.push(" > |Alias Command".to_string());
2981		new_vec_of_strings.push("    %     |Alias Command".to_string());
2982		new_vec_of_strings.push("  $    |Alias Command".to_string());
2983		new_vec_of_strings.push("#    |Alias Command".to_string());
2984		return new_vec_of_strings;
2985		// 1 section, 1 step, 7 substeps that are all Command (1 old-style, 6 new alias)
2986	}
2987
2988	fn generate_ebml_with_image_alias() -> Vec<String> {
2989		let mut new_vec_of_strings:Vec<String> = vec![];
2990		new_vec_of_strings.push("Section|One section".to_string());
2991		new_vec_of_strings.push("Step|One step".to_string());
2992		new_vec_of_strings.push("Image|Old-style Image filename|Old-style Image caption".to_string());
2993		new_vec_of_strings.push("IMG|Alias Image filename|Alias Image caption".to_string());
2994		new_vec_of_strings.push(" i m g |Alias Image filename|Alias Image caption".to_string());
2995		new_vec_of_strings.push("PICTURE|Alias Image filename|Alias Image caption".to_string());
2996		new_vec_of_strings.push(" p i c t u r e |Alias Image filename|Alias Image caption".to_string());
2997		new_vec_of_strings.push("PIC|Alias Image filename|Alias Image caption".to_string());
2998		new_vec_of_strings.push(" p i c |Alias Image filename|Alias Image caption".to_string());
2999		new_vec_of_strings.push("FIGURE|Alias Image filename|Alias Image caption".to_string());
3000		new_vec_of_strings.push(" f i g u r e |Alias Image filename|Alias Image caption".to_string());
3001		new_vec_of_strings.push("FIG|Alias Image filename|Alias Image caption".to_string());
3002		new_vec_of_strings.push(" f i g |Alias Image filename|Alias Image caption".to_string());
3003		return new_vec_of_strings;
3004		// 1 section, 1 step, 11 substeps that are all Image (1 old-style, 10 new alias)
3005	}
3006
3007	fn generate_ebml_with_action_alias() -> Vec<String> {
3008		let mut new_vec_of_strings:Vec<String> = vec![];
3009		new_vec_of_strings.push("Section|One section".to_string());
3010		new_vec_of_strings.push("Step|One step".to_string());
3011		new_vec_of_strings.push("Action|Old-style Action|Old-style Expectation|Old-style TPV".to_string());
3012		new_vec_of_strings.push("DO|Alias Action|Alias Expectation|Alias TPV".to_string());
3013		new_vec_of_strings.push(" d o |Alias Action|Alias Expectation|Alias TPV".to_string());
3014		new_vec_of_strings.push("  D    o |Alias Action|Alias Expectation|Alias TPV".to_string());
3015		new_vec_of_strings.push(" dO |Alias Action|Alias Expectation|Alias TPV".to_string());
3016		return new_vec_of_strings;
3017		// 1 section, 1 step, 1 substep that is an ActionSequence of length 5 (1 old-style, 4 new alias)
3018	}
3019
3020	fn generate_ebml_with_warning_alias() -> Vec<String> {
3021		let mut new_vec_of_strings:Vec<String> = vec![];
3022		new_vec_of_strings.push("Section|One section".to_string());
3023		new_vec_of_strings.push("Step|One step".to_string());
3024		new_vec_of_strings.push("Warning|Old-style Warning".to_string());
3025		new_vec_of_strings.push("WARN|Alias Warning".to_string());
3026		new_vec_of_strings.push(" w a r n |Alias Warning".to_string());
3027		new_vec_of_strings.push("WRN|Alias Warning".to_string());
3028		new_vec_of_strings.push(" w r n |Alias Warning".to_string());
3029		new_vec_of_strings.push("WAR|Alias Warning".to_string());
3030		new_vec_of_strings.push(" w a r |Alias Warning".to_string());
3031		new_vec_of_strings.push("ALERT|Alias Warning".to_string());
3032		new_vec_of_strings.push(" a l e r t |Alias Warning".to_string());
3033		new_vec_of_strings.push("  !  |Alias Warning".to_string());
3034		return new_vec_of_strings;
3035		// 1 section, 1 step, 10 substeps that are all Warnings (1 old-style, 9 new alias)
3036	}
3037
3038	fn generate_ebml_with_verification_alias() -> Vec<String> {
3039		let mut new_vec_of_strings:Vec<String> = vec![];
3040		new_vec_of_strings.push("Section|One section".to_string());
3041		new_vec_of_strings.push("Step|One step".to_string());
3042		new_vec_of_strings.push("Verification|Old-style Verification ReqID|Old-style Verification Text|Method".to_string());
3043		new_vec_of_strings.push("VER|Alias Verification ReqID|Alias Verification Text|Method".to_string());
3044		new_vec_of_strings.push(" v e r |Alias Verification ReqID|Alias Verification Text|Method".to_string());
3045		new_vec_of_strings.push("REQUIREMENT|Alias Verification ReqID|Alias Verification Text|Method".to_string());
3046		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());
3047		new_vec_of_strings.push("REQ|Alias Verification ReqID|Alias Verification Text|Method".to_string());
3048		new_vec_of_strings.push(" r e q |Alias Verification ReqID|Alias Verification Text|Method".to_string());
3049		return new_vec_of_strings;
3050		// 1 section, 1 step, 7 substeps that are all Verifications (1 old-style, 6 new alias)
3051	}
3052
3053	fn generate_ebml_with_resource_alias() -> Vec<String> {
3054		let mut new_vec_of_strings:Vec<String> = vec![];
3055		new_vec_of_strings.push("Section|One section".to_string());
3056		new_vec_of_strings.push("Step|One step".to_string());
3057		new_vec_of_strings.push("Resource|Old-style Resource|Old-style Calibration".to_string());
3058		new_vec_of_strings.push("RES|Alias Resource|Alias Calibration".to_string());
3059		new_vec_of_strings.push(" r e s |Alias Resource|Alias Calibration".to_string());
3060		return new_vec_of_strings;
3061		// 1 section, 1 step, 3 substeps that are all Resources (1 old-style, 2 new alias)
3062	}
3063
3064	fn generate_ebml_with_objective_alias() -> Vec<String> {
3065		let mut new_vec_of_strings:Vec<String> = vec![];
3066		new_vec_of_strings.push("Section|One section".to_string());
3067		new_vec_of_strings.push("Step|One step".to_string());
3068		new_vec_of_strings.push("Objective|Old-style Objective".to_string());
3069		new_vec_of_strings.push("OBJ|Alias Objective".to_string());
3070		new_vec_of_strings.push(" o b j |Alias Objective".to_string());
3071		return new_vec_of_strings;
3072		// 1 section, 1 step, 3 substeps that are all Objectives (1 old-style, 2 new alias)
3073	}
3074
3075	fn generate_ebml_with_out_of_scope_alias() -> Vec<String> {
3076		let mut new_vec_of_strings:Vec<String> = vec![];
3077		new_vec_of_strings.push("Section|One section".to_string());
3078		new_vec_of_strings.push("Step|One step".to_string());
3079		new_vec_of_strings.push("Out of Scope|Old-style Out of Scope".to_string());
3080		new_vec_of_strings.push("OOS|Alias Objective".to_string());
3081		new_vec_of_strings.push(" o o s |Alias Out of Scope".to_string());
3082		return new_vec_of_strings;
3083		// 1 section, 1 step, 3 substeps that are all Out of Scopes (1 old-style, 2 new alias)
3084	}
3085
3086	fn generate_ebml_with_revision_alias() -> Vec<String> {
3087		let mut new_vec_of_strings:Vec<String> = vec![];
3088		new_vec_of_strings.push(" Revision |      Successful Revision      |    Successful Description       ".to_string());
3089		new_vec_of_strings.push(" REV |      Successful Revision      |    Successful Description       ".to_string());
3090		new_vec_of_strings.push(" r e v |      Successful Revision      |    Successful Description       ".to_string());
3091		new_vec_of_strings.push("Section|One section".to_string());
3092		new_vec_of_strings.push("Step|One step".to_string());
3093		return new_vec_of_strings;
3094		// 3 Revisions, each of which should be Rev "Successful Revision" with reason of "Successful Description"
3095	}
3096
3097	fn generate_ebml_with_template_alias() -> Vec<String> {
3098		let mut new_vec_of_strings:Vec<String> = vec![];
3099		new_vec_of_strings.push("Template | Nominal.css".to_string());
3100		new_vec_of_strings.push("CSS|Alias.css".to_string());
3101		new_vec_of_strings.push(" c s s |Alias.css".to_string());
3102		return new_vec_of_strings;
3103		// 3 Templates, one old-style and 2 alias
3104	}
3105
3106	// use the helper functions, and confirm that EBML intent is read correctly
3107
3108	#[test]
3109	fn test_section_alias() {
3110		let file_name = "test_section_alias.ebml".to_string();
3111		create_test_file(&file_name,generate_ebml_with_section_alias());
3112		let process = read_ebml(&file_name);
3113		assert_eq!(process.get_all_sections().len(),4);
3114		destroy_test_file(&file_name);
3115	}
3116
3117	#[test]
3118	fn test_step_alias() {
3119		let file_name = "test_step_alias.ebml".to_string();
3120		create_test_file(&file_name,generate_ebml_with_step_alias());
3121		let process = read_ebml(&file_name);
3122		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),4);
3123		destroy_test_file(&file_name);
3124	}
3125
3126	#[test]
3127	fn test_context_alias() {
3128		let file_name = "test_context_alias.ebml".to_string();
3129		create_test_file(&file_name,generate_ebml_with_context_alias());
3130		let process = read_ebml(&file_name);
3131		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),7);
3132		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3133			match substep { SubStep::Context(..) => println!(">>Context line found, as expected!"), _ => panic!(">>Should be a 'Context'")};
3134		}
3135		destroy_test_file(&file_name);
3136	}
3137
3138	#[test]
3139	fn test_command_alias() {
3140		let file_name = "test_command_alias.ebml".to_string();
3141		create_test_file(&file_name,generate_ebml_with_command_alias());
3142		let process = read_ebml(&file_name);
3143		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),7);
3144		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3145			match substep { SubStep::Command(..) => println!(">>Command line found, as expected!"), _ => panic!(">>Should be a 'Command'")};
3146		}
3147		destroy_test_file(&file_name);
3148	}
3149
3150	#[test]
3151	fn test_image_alias() {
3152		let file_name = "test_image_alias.ebml".to_string();
3153		create_test_file(&file_name,generate_ebml_with_image_alias());
3154		let process = read_ebml(&file_name);
3155		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),11);
3156		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3157			match substep { SubStep::Image(..) => println!(">>Image line found, as expected!"), _ => panic!(">>Should be a 'Image'")};
3158		}
3159		destroy_test_file(&file_name);
3160	}
3161
3162	#[test]
3163	fn test_action_alias() {
3164		let file_name = "test_action_alias.ebml".to_string();
3165		create_test_file(&file_name,generate_ebml_with_action_alias());
3166		let process = read_ebml(&file_name);
3167		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
3168		match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
3169			SubStep::ActionSequence(list) => assert_eq!(list.len(),5),
3170			_ => panic!(">>Should be a 'ActionSequence'"),
3171		};
3172		destroy_test_file(&file_name);
3173	}
3174
3175	#[test]
3176	fn test_warning_alias() {
3177		let file_name = "test_warning_alias.ebml".to_string();
3178		create_test_file(&file_name,generate_ebml_with_warning_alias());
3179		let process = read_ebml(&file_name);
3180		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),10);
3181		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3182			match substep { SubStep::Warning(..) => println!(">>Warning line found, as expected!"), _ => panic!(">>Should be a 'Warning'")};
3183		}
3184		destroy_test_file(&file_name);
3185	}
3186
3187	#[test]
3188	fn test_verification_alias() {
3189		let file_name = "test_verification_alias.ebml".to_string();
3190		create_test_file(&file_name,generate_ebml_with_verification_alias());
3191		let process = read_ebml(&file_name);
3192		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),7);
3193		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3194			match substep { SubStep::Verification(..) => println!(">>Verification line found, as expected!"), _ => panic!(">>Should be a 'Verification'")};
3195		}
3196		destroy_test_file(&file_name);
3197	}
3198
3199	#[test]
3200	fn test_resource_alias() {
3201		let file_name = "test_resource_alias.ebml".to_string();
3202		create_test_file(&file_name,generate_ebml_with_resource_alias());
3203		let process = read_ebml(&file_name);
3204		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),3);
3205		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3206			match substep { SubStep::Resource(..) => println!(">>Resource line found, as expected!"), _ => panic!(">>Should be a 'Resource'")};
3207		}
3208		destroy_test_file(&file_name);
3209	}
3210
3211	#[test]
3212	fn test_objective_alias() {
3213		let file_name = "test_objective_alias.ebml".to_string();
3214		create_test_file(&file_name,generate_ebml_with_objective_alias());
3215		let process = read_ebml(&file_name);
3216		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),3);
3217		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3218			match substep { SubStep::Objective(..) => println!(">>Objective line found, as expected!"), _ => panic!(">>Should be a 'Objective'")};
3219		}
3220		destroy_test_file(&file_name);
3221	}
3222
3223	#[test]
3224	fn test_out_of_scope_alias() {
3225		let file_name = "test_out_of_scope_alias.ebml".to_string();
3226		create_test_file(&file_name,generate_ebml_with_out_of_scope_alias());
3227		let process = read_ebml(&file_name);
3228		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),3);
3229		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
3230			match substep { SubStep::OutOfScope(..) => println!(">>Out of Scope line found, as expected!"), _ => panic!(">>Should be a 'Out of Scope'")};
3231		}
3232		destroy_test_file(&file_name);
3233	}
3234
3235	#[test]
3236	fn test_revision_alias() {
3237		let file_name = "test_revision_alias.ebml".to_string();
3238		create_test_file(&file_name,generate_ebml_with_revision_alias());
3239		let process = read_ebml(&file_name);
3240		assert_eq!(process.get_all_revisions().len(),3);
3241		for (rev,desc) in process.get_all_revisions() {
3242			assert_eq!(rev,"Successful Revision");
3243			assert_eq!(desc,"Successful Description");
3244		}
3245		destroy_test_file(&file_name);
3246	}
3247
3248	#[test]
3249	fn test_template_alias() {
3250		let file_name = "test_template_alias.ebml".to_string();
3251		create_test_file(&file_name,generate_ebml_with_template_alias());
3252		let process = read_ebml(&file_name);
3253		assert_eq!(process.get_all_templates().len(),3);
3254		destroy_test_file(&file_name);
3255	}
3256
3257	// for stanhope features added between 0.95.0 and 0.103.0
3258
3259	#[test]
3260	fn test_process_get_step_count() {
3261		let file_name = "test_process_get_step_count.ebml".to_string();
3262		create_test_file(&file_name,generate_ebml_with_1000_steps_in_one_section());
3263		let process = read_ebml(&file_name);
3264		assert_eq!(process.get_step_count(),1000);
3265		destroy_test_file(&file_name);
3266	}
3267
3268	#[test]
3269	fn test_process_get_all_context_lines() {
3270		let file_name = "test_process_get_all_context_lines.ebml".to_string();
3271		create_test_file(&file_name,generate_ebml_with_context_alias());
3272		let process = read_ebml(&file_name);
3273		assert_eq!(process.get_all_context_lines().len(),7);
3274		destroy_test_file(&file_name);
3275	}
3276
3277	#[test]
3278	fn test_process_get_all_images() {
3279		let file_name = "test_process_get_all_images.ebml".to_string();
3280		create_test_file(&file_name,generate_ebml_with_diabolical_image_lines());
3281		let process = read_ebml(&file_name);
3282		assert_eq!(process.get_all_images().len(),15);
3283		destroy_test_file(&file_name);
3284	}
3285
3286	#[test]
3287	fn test_process_get_unique_image_count() {
3288		let file_name = "test_process_get_unique_image_count.ebml".to_string();
3289		create_test_file(&file_name,generate_ebml_with_diabolical_image_lines());
3290		let process = read_ebml(&file_name);
3291		assert_eq!(process.get_unique_image_count(),2);
3292		destroy_test_file(&file_name);
3293	}
3294
3295	#[test]
3296	fn test_process_get_missing_image_count() {
3297		let file_name = "test_process_get_missing_image_count.ebml".to_string();
3298		create_test_file(&file_name,generate_ebml_with_diabolical_image_lines());
3299		let process = read_ebml(&file_name);
3300		assert_eq!(process.get_missing_image_count(),2);
3301		assert_eq!(process.get_missing_images().len(),2);
3302		destroy_test_file(&file_name);
3303	}
3304
3305	#[test]
3306	fn test_process_get_section_reference_count() {
3307		let file_name = "test_process_get_section_reference_count.ebml".to_string();
3308		create_test_file(&file_name,generate_ebml_with_section_reference_alias());
3309		let process = read_ebml(&file_name);
3310		assert_eq!(process.get_section_reference_count(),4);
3311		destroy_test_file(&file_name);
3312	}
3313
3314	#[test]
3315	fn test_process_set_and_get_process_file() {
3316		let test_process_file = "Not a real EBML file";
3317		let mut new_proc = Process::new();
3318		assert_ne!(new_proc.get_process_file().to_string(),test_process_file.to_string());
3319		new_proc.set_process_file(test_process_file);
3320		assert_eq!(new_proc.get_process_file().to_string(),test_process_file.to_string());
3321	}
3322
3323	#[test]
3324	fn test_process_set_and_get_full_source() {
3325		let test_full_source = vec!["First Line of fake EBML".to_string(),"Second Line of fake EBML".to_string()];
3326		let mut new_proc = Process::new();
3327		assert_ne!(new_proc.get_full_source(),&test_full_source);
3328		new_proc.set_full_source(test_full_source.clone());
3329		assert_eq!(new_proc.get_full_source(),&test_full_source);
3330	}
3331
3332	#[test]
3333	fn test_section_set_get_relative_path() {
3334		let test_relative_path = "Not a real Process folder path";
3335		let mut new_sec = Section::new();
3336		assert_ne!(new_sec.get_relative_path().to_string(),test_relative_path.to_string());
3337		new_sec.set_relative_path(test_relative_path);
3338		assert_eq!(new_sec.get_relative_path().to_string(),test_relative_path.to_string());
3339	}
3340
3341}