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
7//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
8// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
9// ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░▌       ▐░▌▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
10// ▐░▌               ▐░▌     ▐░▌       ▐░▌▐░▌       ▐░▌▐░▌               ▐░▌     
11// ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     ▐░█▄▄▄▄▄▄▄█░▌▐░▌       ▐░▌▐░▌               ▐░▌     
12// ▐░░░░░░░░░░░▌     ▐░▌     ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌               ▐░▌     
13//  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     ▐░█▀▀▀▀█░█▀▀ ▐░▌       ▐░▌▐░▌               ▐░▌     
14//           ▐░▌     ▐░▌     ▐░▌     ▐░▌  ▐░▌       ▐░▌▐░▌               ▐░▌     
15//  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     ▐░▌      ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
16// ▐░░░░░░░░░░░▌     ▐░▌     ▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
17//  ▀▀▀▀▀▀▀▀▀▀▀       ▀       ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀      
18                                                                              
19/// **Top-level struct containing all information read from the markup file**
20/// 
21/// All fields are private, but are accessible with "get" functions, e.g. Process.get_number() returns a reference to the Process's number (&String).
22pub struct Process {
23
24	/// Document Number, in whatever format makes sense for the organization's document control system
25	number: String,
26
27	/// 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
28	/// * First String: "Rev" text, e.g. "A" for a document's Revision A
29	/// * Second String: "What changed?" text, e.g. "New section added to initialize the system prior to start"
30	all_revisions: Vec<(String,String)>,
31	
32	/// Human-readable Title of the process document, hopefully with enough meaning for the team to understand the purpose of the process
33	title: String,
34
35	/// Human-readable category of process document, such as "Test Procedure" or "Work Instruction" or "Maintenance Procedure" etc.
36	process_type: String,
37
38	/// Human that composed the markup file in its current Revision, i.e. who currently owns the knowledge in this process
39	author: String,
40
41	/// 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
42	reviewer: String,
43
44	/// The thing that this process applies to, i.e. the primary input into the Process, be it tangible or abstract
45	subject: String,
46
47	/// File name for an image of the Subject
48	subject_image: String,
49
50	/// If this process changes the *Subject* into something else, then that something else is the *Product*
51	product: String,
52
53	/// File name for an image of the Product
54	product_image: String,
55
56	/// Compiled list of Objective statements found throughout the document when they are achieved
57	all_objectives: Vec<(String,String)>,
58
59	/// Compiled list of Out-of-Scope statements found throughout the document when they are achieved
60	all_out_of_scopes: Vec<(String,String)>,
61
62	/// Ordered set of all Step-filled sections of the document
63	all_sections: Vec<Section>,
64
65	/// Cummulative list of all Resources identified in all Steps
66	all_resources: Vec<(Resource,String)>,
67
68	/// Unique list of all resources that need calibration to be used in this process
69	all_calibrated_resources: Vec<Resource>,
70
71	/// Cummulative list of all Verifications of Requirements performed in all Steps
72	all_verifications: Vec<(Requirement,String)>,
73
74	/// File names for custom templates (CSS) to use that augment the vanilla template
75	/// * Company-specific
76	/// * Project-specific
77	/// * Security environment-specific
78	/// * etc.
79	all_templates: Vec<String>,
80}
81
82/// Each Section struct contains all Steps within that document section
83pub struct Section {
84
85	/// What the section is named, hopefully descriptive enough that operators know what to expect, and can anticipate operations
86	title: String,
87
88	/// Ordered set of all Steps in the section (each Step is rich with Sub-Step information as well)
89	all_steps: Vec<Step>,
90}
91
92/// Each Step struct contains all Actions, Images, Commands, Resources, Verifications, and Warnings contained in that step
93pub struct Step {
94
95	/// Text of the step, analogous to the Title of a Section except at the Step-level
96	text: String,
97
98	/// Ordered set of Resources identified in the Step, wherever they are mentioned
99	resources: Vec<Resource>,
100
101	/// Ordered set of SubSteps identified, be them sets of Action lines, Commands, Warnings, or Verification events
102	/// 
103	/// *Note: Resources are not included as SubSteps themselves, although other lines within a Step are*
104	all_sub_steps: Vec<SubStep>,
105}
106
107/// Each Action struct is a single operation to perform, with an expected result, and an optional Two-Party Verification flag
108pub struct Action {
109
110	/// What action the operator is to perform
111	perform: String,
112
113	/// What operator is to expect after performing the action
114	expect: String,
115
116	/// Is two party verification (TPV) required for this step?
117	tpv: bool,
118}
119
120/// Each Requirement struct represents a formal "shall" statement whose evidence of verification is produced in a Step of a Process
121pub struct Requirement {
122
123	/// Unique ID for the requirement
124	id: String,
125
126	/// Full text of the requirement, which includes the word "shall."
127	/// 
128	/// Example of *good requirement* wording:
129	/// 
130	/// (When \[SUB/SYSTEM or VARIABLE\] is \[MODE/STATE or VALUE or RANGE\],) \[AGENT\] shall \[BEHAVE\] (within \[TOLERANCE\]). (Note: [CLARIFYING NOTES OR REFERENCES])
131	text: String,
132
133	/// 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.
134	method: VerificationMethod,
135}
136
137/// Resoures are named things that are needed to perform a Step - without them the Step can't be performed
138pub struct Resource { 
139
140	/// 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.
141	name: String,
142
143	/// Does this resource need to be calibrated to be used in this process?
144	calibration: bool,
145}
146
147/// A flexible table of arbitrary number of rows and columns, with a caption to describe the table within the process
148pub struct Table {
149
150	/// Descriptive string to display alongside the table
151	caption: String,
152
153	/// The complete array of table data, with the Vec<Vec<>> representing Row<Col<>>
154	/// 
155	/// Example: to access the entire header (first) row of the table
156	/// > array\[0\]
157	/// 
158	/// Example: to access the 8th column of the 10th row of the table
159	/// > array\[9\]\[7\]
160	/// 
161	/// 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.
162	array: Vec<Vec<String>>,
163
164}
165
166//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄        ▄  ▄         ▄  ▄▄       ▄▄ 
167// ▐░░░░░░░░░░░▌▐░░▌      ▐░▌▐░▌       ▐░▌▐░░▌     ▐░░▌
168// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌░▌     ▐░▌▐░▌       ▐░▌▐░▌░▌   ▐░▐░▌
169// ▐░▌          ▐░▌▐░▌    ▐░▌▐░▌       ▐░▌▐░▌▐░▌ ▐░▌▐░▌
170// ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌   ▐░▌▐░▌       ▐░▌▐░▌ ▐░▐░▌ ▐░▌
171// ▐░░░░░░░░░░░▌▐░▌  ▐░▌  ▐░▌▐░▌       ▐░▌▐░▌  ▐░▌  ▐░▌
172// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌   ▐░▌ ▐░▌▐░▌       ▐░▌▐░▌   ▀   ▐░▌
173// ▐░▌          ▐░▌    ▐░▌▐░▌▐░▌       ▐░▌▐░▌       ▐░▌
174// ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌     ▐░▐░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌       ▐░▌
175// ▐░░░░░░░░░░░▌▐░▌      ▐░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌
176//  ▀▀▀▀▀▀▀▀▀▀▀  ▀        ▀▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀ 
177
178/// 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.
179pub enum SubStep {
180
181	/// Each objective of the process achieved in this step has a single SubStep of type Objective, simply passing through the string found
182	Objective(String),
183
184	/// 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
185	OutOfScope(String),
186
187	/// 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.
188	ActionSequence(Vec<Action>),
189
190	/// Each command row has a String that is verbatim what the operator should type (or paste) into a computer system
191	Command(String),
192
193	/// 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)
194	Image(String,String),
195
196	/// Warning text is something the author wants to force the operator to read prior to proceeding
197	Warning(String),
198
199	/// Each Verification row includes sufficient information to form a Verification struct
200	Verification(Requirement),
201
202	/// 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
203	Resource(Resource),
204
205	/// Vanilla text blocks, to be used sparingly lest the process document get too wordy
206	Context(String),
207
208	/// Arbitrary-sized table from comma-separated data
209	Table(Table),
210}
211
212/// Verification methods include one-of-a-kind systems as well as high-volume production (e.g. Sampling)
213pub enum VerificationMethod {
214
215	/// *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.
216	Demonstration,
217
218	/// *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. 
219	Inspection,
220
221	/// *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.
222	Analysis,
223
224	/// *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>])
225	Test,
226
227	/// 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.
228	/// 
229	/// [<https://sebokwiki.org/wiki/System_Verification#Methods_and_Techniques>]
230	Sampling,
231
232}
233
234//  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄        ▄  ▄▄▄▄▄▄▄▄▄▄▄ 
235// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░▌      ▐░▌▐░░░░░░░░░░░▌
236// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░▌░▌     ▐░▌▐░█▀▀▀▀▀▀▀▀▀ 
237// ▐░▌          ▐░▌       ▐░▌▐░▌▐░▌    ▐░▌▐░▌          
238// ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌       ▐░▌▐░▌ ▐░▌   ▐░▌▐░▌          
239// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌  ▐░▌  ▐░▌▐░▌          
240// ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░▌   ▐░▌ ▐░▌▐░▌          
241// ▐░▌          ▐░▌       ▐░▌▐░▌    ▐░▌▐░▌▐░▌          
242// ▐░▌          ▐░█▄▄▄▄▄▄▄█░▌▐░▌     ▐░▐░▌▐░█▄▄▄▄▄▄▄▄▄ 
243// ▐░▌          ▐░░░░░░░░░░░▌▐░▌      ▐░░▌▐░░░░░░░░░░░▌
244//  ▀            ▀▀▀▀▀▀▀▀▀▀▀  ▀        ▀▀  ▀▀▀▀▀▀▀▀▀▀▀ 
245                                                    
246/// Read and interpret an entire Easy Button Markup Language (EBML) file, and return a "Process" struct.
247/// 
248/// A single Process struct holds all found information about a process, and is itself enough to extract it and produce complex documents.
249pub fn read_ebml(file_name:&String) -> Process {
250
251	use std::{
252		fs::File,
253		io::{self, BufRead, BufReader},
254		path::Path,
255	};
256
257	// empty string input (instead of the name of a file to read) returns a blank Process with no further processing
258	if file_name==&String::from("") {
259		return Process::new();
260	}
261
262	// function to return all the lines in a file back as a vec of strings
263	fn lines_from_file(filename: impl AsRef<Path>) -> io::Result<Vec<String>> {
264		BufReader::new(File::open(filename)?).lines().collect()
265	}
266
267	// Instantiate a blank Process to be filled with found information
268	let mut new_proc = Process::new();
269
270	// Use the function to pull all the lines out of the markup file
271	let lines = lines_from_file(file_name).expect("Could not load lines");
272
273	// initialize a couple of counters that become the basis of Section numbering and Step numbering below
274	let mut sec_cnt = 0;
275    let mut stp_cnt = 0;
276
277    // Now loop through all the lines of the input file
278    for (ii,line) in lines.iter().enumerate() {
279
280    	// if it's a comment line, then skip this instance of the loop
281    	if line.trim().find("//") == Some(0) { continue }
282
283    	// delimit the line by the bar/pipe character
284    	let all_parts: Vec<&str> = line.split('|').collect();
285
286    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
287    	let first_part: &str = all_parts[0];
288
289    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
290    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
291
292    	// "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
293    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
294    	
295    	// Here's where the actual processing goes. At the PROCESS level, we're looking for process metadata (author, revision, etc.)
296    	// 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
297    	// We don't actually process "Step" here, nor any of the line types that comprise Steps/SubSteps, but we still increment the Step counter
298    	// For simple line types, we just pass the entire "remainder" into the struct field
299    	// For more complex line types, specialized functions take over and perform further processing of the remainder (and possibly the Section/Step counters)
300        match trim_whitespace_make_uppercase(first_part).as_str() {
301			"TEMPLATE" 		=> new_proc.add_template(remainder),
302			"NUMBER"	 	=> new_proc.set_number(remainder),
303			"TITLE"		 	=> new_proc.set_title(remainder),
304			"PROCESSTYPE"	=> new_proc.set_process_type(remainder),
305			"AUTHOR"	 	=> new_proc.set_author(remainder),
306			"REVIEWER"		=> new_proc.set_reviewer(remainder),
307			"REVISION" 		=> new_proc.add_revision(remainder),
308			"SUBJECT" 		=> new_proc.set_subject(remainder),
309			"SUBJECTIMAGE"	=> new_proc.set_subject_image(remainder),
310			"PRODUCT" 		=> new_proc.set_product(remainder),
311			"PRODUCTIMAGE"	=> new_proc.set_product_image(remainder),
312			"SECTION" 		=> {
313				new_proc.add_section(extract_next_section(&lines[ii..lines.len()],&file_name));
314				sec_cnt += 1;
315				stp_cnt = 0;
316			},
317			"STEP"		 	=> {
318				stp_cnt += 1;
319				//println!("Step {}.{}",sec_cnt,stp_cnt);
320			}
321			"ACTION"	 	=> (), //println!("Valid \"{}\", skipping...",&line[0..index]),
322			"COMMAND" 		=> (), //println!("Valid \"{}\", skipping...",&line[0..index]),
323			"CONTEXT"		=> (), 
324			"IMAGE"		 	=> (), //println!("Valid \"{}\", skipping...",&line[0..index]),
325			"WARNING"		=> (), //println!("Valid \"{}\", skipping...",&line[0..index]),
326			"CSVSTART"		=> (),
327			"CSVEND"		=> (),
328			"CSVFILE"		=> (),
329			"VERIFICATION"	=> new_proc.add_verification(parse_verification_line(remainder),"Step ".to_string()+&sec_cnt.to_string()+"."+&stp_cnt.to_string()),
330			"RESOURCE"		=> {
331				new_proc.add_resource(parse_resource_line(remainder),"Step ".to_string()+&sec_cnt.to_string()+"."+&stp_cnt.to_string());
332				new_proc.add_calibrated_resource(parse_resource_line(remainder));
333			},
334			"OBJECTIVE"		=> new_proc.add_objective(remainder.to_string(),"Step ".to_string()+&sec_cnt.to_string()+"."+&stp_cnt.to_string()),
335			"OUTOFSCOPE"	=> new_proc.add_out_of_scope(remainder.to_string(),"Step ".to_string()+&sec_cnt.to_string()+"."+&stp_cnt.to_string()),
336			""	=> (),
337    		_ 	=> (),
338        }
339
340    }
341
342    // Now that the Process information is extracted and placed into the struct, it's returned
343    new_proc
344
345}
346
347/// Robustify string (slice) matching by trimming out all of the whitespace
348/// 
349/// Use it like this:
350/// 
351/// match **trim_whitespace_make_uppercase**(slice_to_compare)**.as_str()** {<br/>
352/// 	"THING" => do_thing,<br/>
353/// 	""		=> (),<br/>
354/// 	_ 		=> (),<br/>
355/// }
356fn trim_whitespace_make_uppercase(s: &str) -> String { s.split_whitespace().collect::<Vec<_>>().join("").to_uppercase() }
357
358/// Examine the remainder of lines in a file, and return the next Section
359/// A Section is composed of a series of steps, so this is but one layer in that hierarchy
360fn extract_next_section(some_lines:&[String],ebml_file:&str) -> Section {
361
362	// Create a new, blank Section struct to fill with process information (Steps)
363	let mut new_section:Section = Section::new();
364	
365	// We are scanning through lines looking for content within this section. Basically:
366	//
367	// Section|This Section
368	// Step|First step in this section
369	// ...|...
370	// Section|Start of the NEXT Section--the line above this should be the last
371	//
372	// 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
373	let mut section_count = 0;
374	
375	// Loop to break when we have more than one section, called 'one_section
376	'one_section: for (ii,line) in some_lines.iter().enumerate() {
377
378		// delimit the line by the bar/pipe character
379    	let all_parts: Vec<&str> = line.split('|').collect();
380    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
381    	let first_part: &str = all_parts[0];
382    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
383    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
384    	// "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
385    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
386
387    	match trim_whitespace_make_uppercase(first_part).as_str() {
388    		// First encounter: name this Section with the content
389			// Second encounter: stop!
390			"SECTION" => { 
391				section_count +=1;
392				if section_count > 1 { break 'one_section };
393				new_section.set_title(remainder);
394				},
395			"STEP" => new_section.add_step(extract_next_step(&some_lines[ii..some_lines.len()],ebml_file)),
396			// Conscious decision to ignore anything that isn't within a step (can change in the future?)
397			// This logic also ignores comment lines, as the comment characters would polute the "Section" or "Step" text left of the bar/pipe
398			_ => (),
399		};
400	}
401	
402	// Return the completed Section
403	new_section
404
405}
406
407/// When a "Step" line is encountered within a Section, this function performs an analogous extraction to "extract_next_section" but adapted for SubStep mining.
408fn extract_next_step(few_lines:&[String],ebml_file:&str) -> Step {
409
410	// Initialize the output, which is a single Step struct
411	// The remainder of this routine will be filling this struct with the lines between the first Step and the next Step
412	let mut new_step:Step = Step::new();
413	
414	// In order to know where to start and stop, initialize a counter for the Step
415	let mut step_count = 0;
416
417	// Consecutive Action rows are grouped into ActionSequence items, so this flag answers, "is the Action line I'm reading a FIRST action line?"
418	// Every non-action line resets this to TRUE
419	// When an Action line is encountered this is set to FALSE
420	let mut allow_action = true;
421
422	// Similar trick for "CSV Start"
423	let mut allow_csv_start = true;
424	
425	// Named loop 'one_step is so named because when we find the second instance of Step then we're done
426	'one_step: for (ii,line) in few_lines.iter().enumerate() {
427
428		// delimit the line by the bar/pipe character
429    	let all_parts: Vec<&str> = line.split('|').collect();
430    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
431    	let first_part: &str = all_parts[0];
432    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
433    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
434    	// "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
435    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
436
437    	match trim_whitespace_make_uppercase(first_part).as_str() {
438			// First encounter: name this step with the content
439			// Second encounter: stop!
440			"STEP" => { 
441				step_count +=1;
442				if step_count > 1 { break 'one_step };
443				new_step.set_text(remainder);
444				allow_action = true;
445				},
446			// If the previous row was NOT an Action row, then start an ActionSequence fetch, which is analogous to this function
447			// Otherwise, skip the Action row because it was already processed by the function that produces ActionSequence objects
448			"ACTION" => {
449				if allow_action {
450					new_step.add_sub_step(extract_next_action_sequence(&few_lines[ii..few_lines.len()]));
451					allow_action = false;
452				} else {()};
453				},
454			// Commands are simple: assume the entire text is the verbatim text to display as a command
455			"COMMAND" => {
456				new_step.add_sub_step(SubStep::Command(remainder.to_string()));
457				allow_action = true;
458				},
459			// Image lines go to a subroutine that finds the image file name as well as the caption
460			"IMAGE" => {
461				new_step.add_sub_step(parse_image_line(remainder));
462				allow_action = true;
463				},
464			// Warning lines are simple like Command lines: just use all the text as the Warning text
465			"WARNING" => {
466				new_step.add_sub_step(SubStep::Warning(remainder.to_string()));
467				allow_action = true;
468				},
469			// Verification lines are multi-part, so a subroutine is needed to make sense of the information
470			"VERIFICATION" => {
471				new_step.add_sub_step(SubStep::Verification(parse_verification_line(remainder)));
472				allow_action = true;
473				},
474			// Resource is another simple line: all the text is the Resource name text
475			// However, at the Step level there's a field that pulls in all resoureces from all steps, so we need to push that, too.
476			"RESOURCE" => {
477				new_step.add_sub_step(SubStep::Resource(parse_resource_line(remainder)));
478				// the line below is what pushes this found Resouce line up to the Step level field, as well as adding the SubStep
479				new_step.add_resource(parse_resource_line(remainder));
480				allow_action = true;
481				},
482			// Objective is another simple line of text
483			"OBJECTIVE" => {
484				new_step.add_sub_step(SubStep::Objective(remainder.to_string()));
485				allow_action = true;
486				},
487			// "Out of Scope" is another simple line of text
488			"OUTOFSCOPE" => {
489				new_step.add_sub_step(SubStep::OutOfScope(remainder.to_string()));
490				allow_action = true;
491				},
492			// Context is another simple line of text
493			"CONTEXT" => {
494				new_step.add_sub_step(SubStep::Context(remainder.to_string()));
495				allow_action = true;
496				},
497			// Opening of a block of comma-separated values embedded in the EBML file to display as a table
498			"CSVSTART" => {
499				if allow_csv_start {
500					new_step.add_sub_step(SubStep::Table(extract_embedded_csv(&few_lines[ii..few_lines.len()])));
501					allow_csv_start = false;
502				} else {()};
503				},
504			// Closing of a block of comma-separated values embedded in the EBML file
505			"CSVEND" => allow_csv_start = true,
506			// One-line call to reference an external file that contains comma-separated values to display as a table
507			"CSVFILE" => new_step.add_sub_step(SubStep::Table(extract_external_csv(remainder,ebml_file))),
508			// If there's any other kind of line, skip
509			_ => (), 
510		};
511	}
512	
513	// Return the filled Step struct
514	new_step
515
516}
517
518/// When an "Action" line is encountered within a Step, we want to group it with all consecutive Action lines that follow
519/// 
520/// The result is a type of SubStep called an ActionSequence
521/// 
522/// Action Sequences can be a single Action, or many actions
523fn extract_next_action_sequence(couple_lines:&[String]) -> SubStep {
524
525	// Initialize the vector of Actions that make up the ActionSequence type of SubStep
526	let mut new_action_sequence:Vec<Action> = vec![];
527	
528	// Named loop 'consecutive_actions so named because the first non-Action will break the loop
529	'consecutive_actions: for line in couple_lines.iter() {
530
531		// delimit the line by the bar/pipe character
532    	let all_parts: Vec<&str> = line.split('|').collect();
533    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
534    	let first_part: &str = all_parts[0];
535    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
536    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
537    	// "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
538    	let remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
539
540		match trim_whitespace_make_uppercase(first_part).as_str() {    	
541			"ACTION" => new_action_sequence.push(parse_action_line(remainder)),
542			_ => break 'consecutive_actions,
543		};
544	}
545	
546	// Return the SubStep enum of type ActionSequence, with the Vec<Action> as the data
547	SubStep::ActionSequence(new_action_sequence)
548
549}
550
551/// 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.
552/// 
553/// Additional information is contained within the "CSV Start" line itself in EBML bar delimiting syntax.
554/// 
555/// 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.
556fn extract_embedded_csv(couple_lines:&[String]) -> Table {
557	
558	// Initialize the output
559	let mut new_table = Table::new();
560
561	// This flag is to fix an issue found in testing. Yay testing!
562	let mut already_started = false;
563
564	// Named loop 'csv_lines
565	'csv_lines: for line in couple_lines.iter() {
566
567		// delimit the line by the bar/pipe character
568    	let all_parts: Vec<&str> = line.split('|').collect();
569    	// in this markup language, the text to the left of the first bar indicates what kind of line it is
570    	let first_part: &str = all_parts[0];
571    	// Everything to the right of the first bar is important for conditional data extraction (there could also be more pipes)
572    	let first_remainder: Vec<&str> = line.splitn(2,'|').collect();
573    	// "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
574    	let _remainder: &str = if first_remainder.len() > 1 { first_remainder[1] } else { "" };
575
576		match (trim_whitespace_make_uppercase(first_part).as_str(),already_started) {    	
577			("CSVSTART",false) => {
578				match all_parts.len() {
579					2 => new_table.set_caption(all_parts[1]),
580					1 => (),
581					_ => new_table.set_caption(all_parts[1]),
582				}
583			},
584			("CSVEND",true) => break 'csv_lines,
585			_ => {
586				let csv_parts_str:Vec<&str> = line.split(',').collect();
587				let mut new_row:Vec<String> = vec![];
588				for ii in 0..csv_parts_str.len() {
589					new_row.push(csv_parts_str[ii].to_string());
590				}
591				new_table.add_row(new_row);
592			},
593		};
594		already_started = true;
595	}
596
597	// Return the Table struct
598	new_table
599}
600
601/// 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.
602/// 
603/// 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.
604/// 
605/// 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.
606fn extract_external_csv(line:&str,ebml_file:&str) -> Table {
607	
608	/// Single-use private function within a private function to return the "array" information from a CSV file reference
609	fn open_csv_file_and_return_data_array(file_name:&str) -> Vec<Vec<String>> {
610		use std::{
611			fs::File,
612			io::{self, BufRead, BufReader},
613			path::Path,
614		};
615
616		// empty string input (instead of the name of a file to read) returns a blank Process with no further processing
617		if file_name==&String::from("") {
618			return vec![];
619		}
620
621		// function to return all the lines in a file back as a vec of strings
622		fn lines_from_file(filename: impl AsRef<Path>) -> io::Result<Vec<String>> {
623			BufReader::new(File::open(filename)?).lines().collect()
624		}
625
626		// Use the function to pull all the lines out of the markup file
627		let lines = lines_from_file(file_name).expect("Could not load lines");
628
629		let mut data_array:Vec<Vec<String>> = vec![];
630		for line in lines {
631			let mut new_csv_data_line:Vec<String> = vec![];
632			let all_parts: Vec<&str> = line.split(',').collect();
633			for part in all_parts {
634				new_csv_data_line.push(part.trim_start().trim_end().to_string());
635			}
636			data_array.push(new_csv_data_line);
637		}
638		data_array
639	}
640
641	// Initialize the output
642	let mut new_table = Table::new();
643	let all_parts: Vec<&str> = line.split('|').collect();
644
645	// TODO: robustify the line below, because it's built on an unstable house of cards
646	let path_parts = ebml_file.split('/').collect::<Vec<_>>();
647	let csv_file_to_open = path_parts[0].to_owned() + "/" + path_parts[1] + "/" + all_parts[0].trim_start().trim_end();
648	//println!("{}",csv_file_to_open);
649	
650	// One 'part' in the remainder means there's only a file reference in the CSV File line, e.g. "CSV File | file.csv"
651	// Two 'parts' in the remainder means there's also a caption, e.g. "CSV File | file.csv | Caption text"
652	// 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.
653	match all_parts.len() {
654		1 => {
655			for row in open_csv_file_and_return_data_array(&csv_file_to_open) { new_table.add_row(row) };
656		},
657		2 => {
658			for row in open_csv_file_and_return_data_array(&csv_file_to_open) { new_table.add_row(row) };
659			new_table.set_caption(all_parts[1]);
660		},
661		_ => {
662			for row in open_csv_file_and_return_data_array(&csv_file_to_open) { new_table.add_row(row) };
663			new_table.set_caption(all_parts[1]);
664		},
665	}
666	new_table // return the completed Table struct, complete with "array" and "caption" information
667}
668
669/// For a single Action line, extract the information and return an Action struct
670fn parse_action_line(line:&str) -> Action {
671	// Default value of "Two Party Verification" field is FALSE
672	// delimit the line by the bar/pipe character
673	let a: Vec<&str> = line.split('|').collect();
674	match a.len() {
675		2 => Action { perform:a[0].to_string(), expect:a[1].to_string(), tpv:false },
676		1 => Action { perform:a[0].to_string(), expect:"ERROR: NO EXPECTED VALUE PROVIDED".to_string(), tpv:false },
677		0 => Action { perform:"ERROR: NO ACTION PROVIDED".to_string(), expect:"ERROR: NO EXPECTED VALUE PROVIDED".to_string(), tpv:false },
678		// 3 is expected, and the wildcard case covers 3 or more
679		_ => Action { perform:a[0].to_string(), expect:a[1].to_string(), tpv: 
680			match trim_whitespace_make_uppercase(a[2]).as_str() {
681				"TPV"|"T"|"TRUE"|"Y"|"YES"|"TWOPARTYVERIFICATION" => true,
682				_ => false,
683			}
684		},
685	}
686}
687
688
689/// For an "Image" line, parse the two output pieces of information and return as an Image type SubStep
690fn parse_image_line(line:&str) -> SubStep {
691
692	// Delimit the input string by bar/pipe characters and respond based on the number of substrings
693	let all_parts: Vec<&str> = line.split('|').collect();
694	match all_parts.len() {
695		
696		// If only one string, then the image file and caption are the same
697		// If only one string but it's empty, then include a default path and file name for a placeholder image
698		1 => {
699			if all_parts[0]=="" {
700				return SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string())
701			} else {
702				return SubStep::Image(all_parts[0].to_string(),all_parts[0].to_string())
703			}
704		},
705		// If for some reason the length of the segment vector is zero (??), then placeholder
706		0 => return SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string()),
707		// Two is expected here: the first is the image file name, and the second is the caption
708		// Wildcard means 3 or more string segments, so use first two as in nominal path
709		_ => {
710			if all_parts[0]=="" {
711				if all_parts[1]=="" {
712					return SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string());
713				} else {
714					return SubStep::Image("../assets/placeholderImage-small.png".to_string(),all_parts[1].to_string());
715				}
716			} else {
717				if all_parts[1]=="" {
718					return SubStep::Image(all_parts[0].to_string(),all_parts[0].to_string())
719				} else {
720					return SubStep::Image(all_parts[0].to_string(),all_parts[1].to_string())
721				}				
722			}
723		},
724	}
725
726}
727
728/// Verification lines have three expected pieces of information; this parsing function needs to be robust to all cases
729fn parse_verification_line(line:&str) -> Requirement {
730
731	// Delimit the input string by bar/pipe characters and respond based on the number of substrings
732	let all_parts: Vec<&str> = line.split('|').collect();
733	match all_parts.len() {
734		// Two means we will pass an unknown string to the method, leaving that handling to the other function
735		2 => return Requirement{id:all_parts[0].trim().to_string(),text:all_parts[1].trim().to_string(),method:which_method("???"),},
736		// One string part means we don't have requirement text or method, so unknown values are filled
737		1 => return Requirement{id:all_parts[0].trim().to_string(),text:"???".to_string(),method:which_method("???"),},
738		0 => return Requirement{id:"???".to_string(),text:"???".to_string(),method:which_method("???"),},
739		// Three is expected: ID field is first, then Requirement text, then the method of verificaiton (derived in another function)
740		_ => return Requirement{id:all_parts[0].trim().to_string(),text:all_parts[1].trim().to_string(),method:which_method(all_parts[2].trim()),},
741	}
742
743}
744
745/// Interpret a user string into an enum variant
746fn which_method(m:&str) -> VerificationMethod {
747	// Be robust and case insensitive
748	// Commonly used terms for these verification methods are single letter e.g. D, I, A, S, T
749	match trim_whitespace_make_uppercase(m).as_str() {
750		"DEMONSTRATION"|"DEMO"|"D"	=> VerificationMethod::Demonstration,
751		"INSPECTION"|"I"			=> VerificationMethod::Inspection,
752		"ANALYSIS"|"A"				=> VerificationMethod::Analysis,
753		"SAMPLING"|"SAMPLE"|"S"		=> VerificationMethod::Sampling,
754		"TEST"|"T"					=> VerificationMethod::Test,
755		// Sampling is the least expected of these for Stanhope processes
756		// Making Sampling the default is a pseudo-flag that something is wrong
757		// TODO: add an enum variant that covers the ??? case
758		_ => VerificationMethod::Sampling,
759	}
760}
761
762/// Pass the first part as the descriptive string for the resource
763/// The second part is a flag to determine if this resource needs to be calibrated to be used in this process
764fn parse_resource_line(line:&str) -> Resource {
765	let all_parts: Vec<&str> = line.split('|').collect();
766
767	let cal:bool = match all_parts.len() {
768		0|1 => false,
769		_ => match trim_whitespace_make_uppercase(all_parts[1]).as_str() {
770				"C"|"CAL"|"CALIB"|"CALIBRATE"|"CALIBRATED"|"CALIBRATION"|"Y"|"YES"|"T"|"TRUE" => true,
771				_ => false,
772			}
773	};
774
775	match all_parts[0] {
776		""	=> Resource { name: "ERROR: NO RESOURCE IDENTIFIED".to_string(), calibration:cal, },
777		_ 	=> Resource { name: all_parts[0].to_string(), calibration:cal, },
778	}
779}
780
781
782//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄       ▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄           
783// ▐░░░░░░░░░░░▌▐░░▌     ▐░░▌▐░░░░░░░░░░░▌▐░▌          
784//  ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌   ▐░▐░▌▐░█▀▀▀▀▀▀▀█░▌▐░▌          
785//      ▐░▌     ▐░▌▐░▌ ▐░▌▐░▌▐░▌       ▐░▌▐░▌          
786//      ▐░▌     ▐░▌ ▐░▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌          
787//      ▐░▌     ▐░▌  ▐░▌  ▐░▌▐░░░░░░░░░░░▌▐░▌          
788//      ▐░▌     ▐░▌   ▀   ▐░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌          
789//      ▐░▌     ▐░▌       ▐░▌▐░▌          ▐░▌          
790//  ▄▄▄▄█░█▄▄▄▄ ▐░▌       ▐░▌▐░▌          ▐░█▄▄▄▄▄▄▄▄▄ 
791// ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌          ▐░░░░░░░░░░░▌
792//  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀            ▀▀▀▀▀▀▀▀▀▀▀ 
793
794/// Functions necessary to create ::new() structs, as well as "get" every private field publicly, and to change or append private fields publicly
795impl Process {
796
797	/// Return Process struct with placeholder text in String fields, and empty vectors for Vec fields
798	pub fn new() -> Process {
799		Process { 
800			number: "NO NUMBER IN EBML FILE - THIS IS PLACEHOLDER TEXT".to_string(),
801			all_revisions: vec![],
802			title: "NO TITLE IN EBML FILE".to_string(),
803			process_type: "NO PROCESS TYPE IDENTIFIED IN EBML FILE".to_string(),
804			author: "NO AUTHOR IN EBML FILE".to_string(),
805			reviewer: "NO REVIEWER IN EBML FILE".to_string(),
806			subject: "NO SUBJECT IN EBML FILE".to_string(),
807			subject_image: "DEFAULT-PLACEHOLDER-IMAGE.png".to_string(),
808			product: "N/A".to_string(),
809			product_image: "N/A".to_string(),
810			all_objectives: vec![],
811			all_out_of_scopes: vec![],
812			all_sections: vec![],
813			all_resources: vec![],
814			all_calibrated_resources: vec![],
815			all_verifications: vec![],
816			all_templates: vec![],
817		}
818	}
819
820	/// Return reference to the **Document Number** string
821	pub fn get_number(&self) -> &String { &self.number }
822	/// Return reference to the LATEST **Revision** (first listed in the markup file)
823	pub fn get_revision(&self) -> &String { if self.all_revisions.len()==0 { &self.number } else { &self.all_revisions[0].0 } }
824	/// Return reference to the vector of touples describing each Revision and what changed
825	pub fn get_all_revisions(&self) -> &Vec<(String,String)> { &self.all_revisions }
826	/// Return reference to the **Title** string
827	pub fn get_title(&self) -> &String { &self.title }
828	/// Return reference to the **Process Type** string
829	pub fn get_process_type(&self) -> &String { &self.process_type }
830	/// Return reference to the **Author** string
831	pub fn get_author(&self) -> &String { &self.author }
832	/// Return reference to the **Reviewer** string
833	pub fn get_reviewer(&self) -> &String { &self.reviewer }
834	/// Return reference to the **Subject** string
835	pub fn get_subject(&self) -> &String { &self.subject }
836	/// Return reference to the file name of an **Image of the Subject** as string
837	pub fn get_subject_image(&self) -> &String { &self.subject_image }
838	/// Return reference to the **Product** string
839	pub fn get_product(&self) -> &String { &self.product }
840	/// Return reference to the file name of an **Image of the Product** as string
841	pub fn get_product_image(&self) -> &String { &self.product_image }
842	/// Return reference to a **Vector of Sections**, in the order they appear in the markup
843	pub fn get_all_sections(&self) -> &Vec<Section> { &self.all_sections }
844	/// Return reference to a **Vector of Requirement Tuples**, each with a Requirement struct and a string containing the Step number
845	pub fn get_all_verifications(&self) -> &Vec<(Requirement,String)> { &self.all_verifications }
846	/// Return reference to a **Vector of Tuples** describing each objective and at what step it is achieved
847	pub fn get_all_objectives(&self) -> &Vec<(String,String)> { &self.all_objectives }
848	/// Return reference to a **Vector of Tuples** describing each out-of-scope declaration and at from which step
849	pub fn get_all_out_of_scopes(&self) -> &Vec<(String,String)> { &self.all_out_of_scopes }
850	/// Return reference to a **Vector of Templates** as strings, one for each CSS file needed for the process
851	pub fn get_all_templates(&self) -> &Vec<String> { &self.all_templates }
852	/// Return reference to a **Vector of Resource Tuples**, each with a Resource struct and a string containing the Step number
853	pub fn get_all_resources(&self) -> &Vec<(Resource,String)> { &self.all_resources }
854	/// Return reference to a **Vector of Resources** (a unique set of only those that need calibration)
855	pub fn get_all_calibrated_resources(&self) -> &Vec<Resource> { &self.all_calibrated_resources }
856	/// Return the total number of Action lines that require Two-Party Verification (TPV)
857	pub fn get_tpv_count(&self) -> usize {
858		let mut counter:usize = 0;
859		for sec in self.get_all_sections() {
860			for stp in sec.get_all_steps() {
861				for sub in stp.get_all_sub_steps() {
862					match sub {
863						SubStep::ActionSequence(acts) => {
864							for act in acts {
865								if act.get_tpv().clone() { counter += 1; }
866							}
867						},
868						_ => (),
869					}
870				}
871			}
872		}
873		counter
874	}
875	/// Return the total number of Action lines that don't require Two-Party Verification (no TPV)
876	pub fn get_non_tpv_count(&self) -> usize {
877		let mut counter:usize = 0;
878		for sec in self.get_all_sections() {
879			for stp in sec.get_all_steps() {
880				for sub in stp.get_all_sub_steps() {
881					match sub {
882						SubStep::ActionSequence(acts) => {
883							for act in acts {
884								if !act.get_tpv().clone() { counter += 1; }
885							}
886						},
887						_ => (),
888					}
889				}
890			}
891		}
892		counter
893	}
894	/// 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
895	pub fn get_all_command_lines(&self) -> Vec<(String,String)> {
896    	let mut all_command_lines = vec![];
897    	let mut sec_count:u16 = 0;
898		for sec in self.get_all_sections() {
899			sec_count += 1;
900			let mut step_count:u16 = 0;
901			for stp in sec.get_all_steps() {
902				step_count += 1;
903				for sub in stp.get_all_sub_steps() {
904					match sub {
905						SubStep::Command(txt) => {
906							all_command_lines.push((txt.to_string(),("Step ".to_owned()+&sec_count.to_string()+"."+&step_count.to_string()).to_string()));
907						},
908						_ => (),
909					}
910				}
911			}
912		}
913		all_command_lines
914	}
915
916	/// Used in verbose mode to display contents of the struct to stdout
917	pub fn display_process_to_stdout(&self) {
918		println!("==============================================================");
919		println!("{}",&self.get_title());
920		if self.get_all_objectives().len() > 0 {
921			println!(" ↪ Objectives:");
922			for (jj,(obj,_snum)) in self.get_all_objectives().into_iter().enumerate() {
923				println!("    {}. {}",jj+1,obj);
924			}
925		}
926		println!(" ↪ Document Number:          {}",&self.get_number());
927		println!(" ↪ Process Type:             {}",&self.get_process_type());
928		println!(" ↪ Current Revision:         {}",&self.get_revision());
929		println!(" ↪ Author of this Rev:       {}",&self.get_author());
930		println!(" ↪ Reviewer of this Rev:     {}",&self.get_reviewer());
931		println!(" ↪ Subject of process:       {}",&self.get_subject());
932		println!(" ↪ Image of Subject:         {}",&self.get_subject_image());
933		println!(" ↪ Product produced:         {}",&self.get_product());
934		println!(" ↪ Image of Product:         {}",&self.get_product_image());
935		println!(" ↪ Count of Objectives:      {}",&self.get_all_objectives().len());
936		println!(" ↪ Count of Out of Scopes:   {}",&self.get_all_out_of_scopes().len());
937		println!(" ↪ Count of Sections:        {}",&self.get_all_sections().len());
938		println!(" ↪ Count of Resources:       {}",&self.get_all_resources().len());
939		println!(" ↪ Count of Verifications:   {}",&self.get_all_verifications().len());
940		println!(" ↪ Count of Templates:       {}",&self.get_all_templates().len());
941		println!(" ↪ Count of TPV Actions:     {}",&self.get_tpv_count());
942		println!(" ↪ Count of Non-TPV Actions: {}",&self.get_non_tpv_count());
943		println!("==============================================================");
944	}
945
946	/// Change the Document Number
947	pub fn set_number(&mut self, doc_num: &str) {
948		self.number = String::from(doc_num.trim());
949	}
950	/// Add a Revision Tuple to the Vector "all_revisions"
951	pub fn add_revision(&mut self, rev_line: &str) {
952		// Delimit the line into its bar/piped chunks
953		let chunks:Vec<&str> = rev_line.split('|').collect();
954		// If the first chunk isn't an empty string, then use it (otherwise use default text)
955		let rev_str = if !(chunks[0]=="") { chunks[0] } else { &"[No Rev]" };
956		// Use the second chunk if it exists and isn't an empty string
957		let chg_str = if chunks.len()<2 { &"[No description provided by Author]" } else if chunks[1]=="" { &"[No description provided by Author]" } else { chunks[1] };
958		self.all_revisions.push((String::from(rev_str.trim()),String::from(chg_str.trim())));
959	}
960	/// Change the Document Title
961	pub fn set_title(&mut self, title: &str) {
962		self.title = String::from(title.trim());
963	}
964	/// Change the Process Type
965	pub fn set_process_type(&mut self, process_type: &str) {
966		self.process_type = String::from(process_type.trim());
967	}
968	/// Change the Document Author
969	pub fn set_author(&mut self, author: &str) {
970		self.author = String::from(author.trim());
971	}
972	/// Change the Document Reviewer
973	pub fn set_reviewer(&mut self, reviewer: &str) {
974		self.reviewer = String::from(reviewer.trim());
975	}
976	/// Change the Document Subject
977	pub fn set_subject(&mut self, subject: &str) {
978		self.subject = String::from(subject.trim());
979	}
980	/// Change the Image file name of the Document Subject
981	pub fn set_subject_image(&mut self, subject_image: &str) {
982		self.subject_image = String::from(subject_image.trim());
983	}
984	/// Change the Document Product
985	pub fn set_product(&mut self, product: &str) {
986		self.product = String::from(product.trim());
987	}
988	/// Change the Image file name of the Document Product
989	pub fn set_product_image(&mut self, product_image: &str) {
990		self.product_image = String::from(product_image.trim());
991	}
992	/// Add a Template file to the Vector of Template files (strings)
993	pub fn add_template(&mut self, template: &str) {
994		self.all_templates.push(String::from(template.trim()));
995	}
996	/// Add a Section to the Vector of Section structs
997	pub fn add_section(&mut self, section: Section) {
998		self.all_sections.push(section);
999	}
1000	/// Add a Verification tuple to the Vector of Verification tuples
1001	pub fn add_verification(&mut self, requirement: Requirement, step:String) {
1002		self.all_verifications.push((requirement,step));
1003	}
1004	/// Add an Objective tuple to the Vector of Objective tuples
1005	pub fn add_objective(&mut self, objective:String, step:String) {
1006		self.all_objectives.push((objective,step));
1007	}
1008	/// Add an Out-of-Scope tuple to the Vector of Out-of-Scope tuples
1009	pub fn add_out_of_scope(&mut self, out_of_scope:String, step:String) {
1010		self.all_out_of_scopes.push((out_of_scope,step));
1011	}
1012	/// Add a Resource struct to the Vector of Resource structs
1013	pub fn add_resource(&mut self, resource: Resource, step:String) {
1014		self.all_resources.push((resource,step));
1015	}
1016	/// Add a Resource struct to the Vector of calibrated Resource structs
1017	pub fn add_calibrated_resource(&mut self, resource: Resource) {
1018		if *resource.get_calibration() { self.all_calibrated_resources.push(resource); }
1019	}
1020	
1021}
1022
1023/// Section needs "get" and "set/add" functions as well, to read and write private fields
1024impl Section {
1025
1026	/// Blank string for the title, and empty Vec initialized for the list of Steps
1027	fn new() -> Section {
1028		Section {
1029			title: "".to_string(),
1030			all_steps: vec![],
1031		}
1032	}
1033
1034	/// Return reference to **Section Title** as string
1035	pub fn get_title(&self) -> &String { &self.title }
1036	/// Return reference to **Vector of Step structs**
1037	pub fn get_all_steps(&self) -> &Vec<Step> { &self.all_steps }
1038
1039	/// Change the **Section Title**
1040	fn set_title(&mut self, title: &str) {
1041		self.title = String::from(title);
1042	}
1043	/// Add a complete Step struct to the Vector of Step structs
1044	fn add_step(&mut self, step: Step) {
1045		self.all_steps.push(step);
1046	}
1047}
1048
1049/// Step needs "get" and "set/add" functions as well, to read and write private fields
1050impl Step {
1051
1052	/// Empty string for the Step text, and empty vectors for 'resources' and 'all_sub_steps'
1053	pub fn new() -> Step {
1054		Step {
1055			text: "".to_string(),
1056			resources: vec![],
1057			all_sub_steps: vec![],
1058		}
1059	}
1060
1061	/// Return reference to **Step text** as string
1062	pub fn get_text(&self) -> &String { &self.text }
1063	/// Return reference to **Vector of Resource structs** for this step
1064	pub fn get_resources(&self) -> &Vec<Resource> { &self.resources }
1065	/// Return reference to **Vector of SubStep structs** for all SubSteps in this Step
1066	pub fn get_all_sub_steps(&self) -> &Vec<SubStep> { &self.all_sub_steps }
1067
1068	/// Change the text of this Step
1069	fn set_text(&mut self, text: &str) {
1070		self.text = String::from(text);
1071	}
1072	/// Add a complete SubStep to the ordered Vector of SubStep structs
1073	fn add_sub_step(&mut self, sub_step: SubStep) {
1074		self.all_sub_steps.push(sub_step);
1075	}
1076	/// Add a complete Resource struct to the ordered Vector of Resource structs
1077	fn add_resource(&mut self,r:Resource) {
1078		self.resources.push(r);
1079	}
1080
1081}
1082
1083/// Since Action structs are created directly in a function outside the struct at read-time, we just need "get" functions
1084impl Action {
1085	/// Return reference to the "Action to Perform" string
1086	pub fn get_perform(&self) -> &String { &self.perform }
1087	/// Return reference to the "Expected Outcome" string
1088	pub fn get_expect(&self) -> &String { &self.expect }
1089	/// Return reference to the "Two Party Verification" boolean
1090	pub fn get_tpv(&self) -> &bool { &self.tpv }
1091}
1092
1093/// Since Requirement structs are created directly in a function outside the struct at read-time, we just need "get" functions
1094impl Requirement { 
1095	/// Return reference to the **Requirement ID** string
1096	pub fn get_id(&self) -> &String { &self.id }
1097	/// Return reference to the **Requirement Text** string
1098	pub fn get_text(&self) -> &String { &self.text }
1099	/// Return reference to the **Verification Method** as a string, even though that's not how's it's stored
1100	pub fn get_method(&self) -> String {
1101		// The Verification Method is stored as a VerificationMethod enum, so we need to map that to String values
1102		match &self.method {
1103			VerificationMethod::Demonstration => "Demonstration".to_string(),
1104			VerificationMethod::Inspection => "Inspection".to_string(),
1105			VerificationMethod::Analysis => "Analysis".to_string(),
1106			VerificationMethod::Sampling => "Sampling".to_string(),
1107			VerificationMethod::Test => "Test".to_string(),
1108		}
1109	}
1110}
1111
1112/// Simple public "get" funciton for the one struct field
1113impl Resource {
1114	/// Return reference to the **Resource Name** string
1115	pub fn get_name(&self) -> &String { &self.name }
1116
1117	/// Return the boolean that indicates if the resource requires calibration
1118	pub fn get_calibration(&self) -> &bool { &self.calibration }
1119}
1120
1121/// 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.
1122impl Table {
1123
1124	/// Empty string for the default caption, and an empty array for the default table data
1125	pub fn new() -> Table {
1126		Table {
1127			caption: "".to_string(),
1128			array: vec![],
1129		}
1130	}
1131
1132	/// Return reference to the table caption
1133	pub fn get_caption(&self) -> &String { &self.caption }
1134	/// Change the table caption field
1135	fn set_caption(&mut self, caption: &str) {
1136		self.caption = String::from(caption.trim_start().trim_end());
1137	}
1138
1139	/// Return a tuple with the number of rows and the number of columns in the table data array.
1140	/// 
1141	/// Assumptions:
1142	/// 1. if there are zero rows, there are zero columns
1143	/// 1. the number of columns in the first row is the number of columns that **should** be in every row
1144	pub fn get_size(&self) -> (usize,usize) {
1145		
1146		let rows = self.array.len();
1147
1148		let columns:usize = match rows {
1149			0 => 0,
1150			1 => self.array[0].len(),
1151			_ => self.array[0].len(), // TODO
1152		};
1153		
1154		(rows,columns)
1155	}
1156
1157	/// Return a vector with all cell contents for an indexed row number.
1158	/// 
1159	/// Example: to return the contents of the first row,
1160	/// > my_table.get_row(0)
1161	/// 
1162	/// Example: to return the conents of the 10th row,
1163	/// > my_table.get_row(9)
1164	pub fn get_row(&self,row_num:usize) -> Vec<String> {
1165		let (rows,_columns) = self.get_size();
1166		match &row_num <= &(rows-1) {
1167			true 	=> self.array[row_num].clone(),
1168			false 	=> panic!("table reference out of bounds"),
1169		}
1170	}
1171
1172	/// Append the bottom of the table data array with another row of data.
1173	/// 
1174	/// Assumptions:
1175	/// 1. if this is the first row to be pushed, then it has the **correct** number of columns
1176	/// 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
1177	/// 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
1178	fn add_row(&mut self,row:Vec<String>) {
1179		let (rows,columns) = self.get_size();
1180		match rows {
1181			0 => self.array.push(row),
1182			_ => {
1183				let mut new_row:Vec<String> = vec![];
1184				for ii in 0..columns {
1185					if ii < row.len() {
1186						new_row.push(row[ii].clone().trim_start().trim_end().to_string());
1187					} else {
1188						new_row.push("".to_string());
1189					}
1190				}
1191				self.array.push(new_row)
1192			},
1193		};
1194	}
1195
1196}
1197
1198
1199//  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄ 
1200// ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
1201//  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ 
1202//      ▐░▌     ▐░▌          ▐░▌               ▐░▌     
1203//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     
1204//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
1205//      ▐░▌     ▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     
1206//      ▐░▌     ▐░▌                    ▐░▌     ▐░▌     
1207//      ▐░▌     ▐░█▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄█░▌     ▐░▌     
1208//      ▐░▌     ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     
1209//       ▀       ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀      
1210
1211#[cfg(test)]
1212mod tests {
1213    // Note this useful idiom: importing names from outer (for mod tests) scope.
1214	use super::*;
1215	use std::fs;
1216	use std::fs::OpenOptions;
1217	use std::io::Write;
1218
1219	// Test the struct impl functions
1220	#[test]
1221	fn test_process_new() {
1222		Process::new();
1223	}
1224
1225	#[test]
1226    fn test_process_get_functions() {
1227		
1228		let p:Process = Process::new();
1229
1230		assert_eq!(&p.number,p.get_number());
1231		println!("struct Process / Result of get_number() -> {:?}",p.get_number());
1232
1233		assert_eq!(&p.process_type,p.get_process_type());
1234		println!("struct Process / Result of get_process_type() -> {:?}",p.get_process_type());
1235		
1236		assert_eq!(&p.number,p.get_revision());
1237		println!("struct Process / Result of get_revision() -> {:?}",p.get_revision());
1238		
1239		assert!(p.all_revisions.len()==0);
1240		let _is_right_type:&Vec<(String,String)> = p.get_all_revisions();
1241		println!("struct Process / Result of get_all_revisions() -> {:?}",p.get_all_revisions());
1242
1243		assert_eq!(&p.title,p.get_title());
1244		println!("struct Process / Result of get_title() -> {:?}",p.get_title());
1245
1246		assert_eq!(&p.author,p.get_author());
1247		println!("struct Process / Result of get_author() -> {:?}",p.get_author());
1248
1249		assert_eq!(&p.reviewer,p.get_reviewer());
1250		println!("struct Process / Result of get_reviewer() -> {:?}",p.get_reviewer());
1251
1252		assert_eq!(&p.subject,p.get_subject());
1253		println!("struct Process / Result of get_subject() -> {:?}",p.get_subject());
1254
1255		assert_eq!(&p.subject_image,p.get_subject_image());
1256		println!("struct Process / Result of get_subject_image() -> {:?}",p.get_subject_image());
1257
1258		assert_eq!(&p.product,p.get_product());
1259		println!("struct Process / Result of get_product() -> {:?}",p.get_product());
1260
1261		assert_eq!(&p.product_image,p.get_product_image());
1262		println!("struct Process / Result of get_product_image() -> {:?}",p.get_product_image());
1263
1264		assert_eq!(p.get_tpv_count(),0);
1265		println!("struct Process / Result of get_tpv_count() -> {:?}",p.get_tpv_count());
1266
1267		assert_eq!(p.get_non_tpv_count(),0);
1268		println!("struct Process / Result of get_non_tpv_count() -> {:?}",p.get_non_tpv_count());
1269
1270		assert!(p.all_sections.len()==0);
1271		let _is_right_type:&Vec<Section> = p.get_all_sections();
1272		println!("struct Process / Result of get_all_sections() -> [it's empty]");
1273
1274		assert!(p.all_verifications.len()==0);
1275		let _is_right_type:&Vec<(Requirement,String)> = p.get_all_verifications();
1276		println!("struct Process / Result of get_all_sections() -> [it's empty]");
1277
1278		assert!(p.all_templates.len()==0);
1279		let _is_right_type:&Vec<String> = p.get_all_templates();
1280		println!("struct Process / Result of get_all_templates() -> [it's empty]");
1281
1282		assert!(p.all_resources.len()==0);
1283		let _is_right_type:&Vec<(Resource,String)> = p.get_all_resources();
1284		println!("struct Process / Result of get_all_resources() -> [it's an empty]");
1285
1286		assert!(p.all_calibrated_resources.len()==0);
1287		let _is_right_type:&Vec<Resource> = p.get_all_calibrated_resources();
1288		println!("struct Process / Result of get_all_calibrated_resources() -> [it's an empty]");
1289
1290		assert!(p.all_objectives.len()==0);
1291		let _is_right_type:&Vec<(String,String)> = p.get_all_objectives();
1292		println!("struct Process / Result of get_all_objectives() -> [it's an empty]");
1293
1294		assert!(p.all_out_of_scopes.len()==0);
1295		let _is_right_type:&Vec<(String,String)> = p.get_all_out_of_scopes();
1296		println!("struct Process / Result of get_all_out_of_scopes() -> [it's an empty]");
1297
1298	}
1299
1300	#[test]
1301	fn test_process_display_process_to_stdout() {
1302
1303		let p:Process = Process::new();
1304		p.display_process_to_stdout();
1305
1306	}
1307
1308	#[test]
1309	fn test_process_set_functions() {
1310
1311		let mut p:Process = Process::new();
1312
1313		p.set_number("SET_PROCESS_DOC_NUMBER");
1314		p.add_revision("SET_REV|SET_REV_CHANGE");
1315		p.set_title("SET_PROCESS_TITLE");
1316		p.set_author("SET_AUTHOR");
1317		p.set_reviewer("SET_REVIEWER");
1318		p.set_subject("SET_SUBJECT");
1319		p.set_subject_image("SET_SUBJECT_IMAGE");
1320		p.set_product("SET_PRODUCT");
1321		p.set_product_image("SET_PRODUCT_IMAGE");
1322		p.add_template("SET_TEMPLATE");
1323		p.add_section(Section{title:"SET_SECTION_TITLE".to_string(),all_steps:vec![],});
1324		p.add_verification(Requirement{id:"SET_REQUIREMENT_ID".to_string(),text:"SET_REQUIREMENT_TEXT".to_string(),method:VerificationMethod::Sampling,},"SET_VERIFICATION_STEP".to_string());
1325		p.add_resource(Resource{name:"SET_RESOURCE_NAME".to_string(),calibration:false},"SET_RESOURCE_STEP".to_string());
1326		p.add_objective("SET_OBJECTIVE_TEXT".to_string(),"SET_OBJECTIVE_STEP".to_string());
1327		p.add_out_of_scope("SET_OBJECTIVE_TEXT".to_string(),"SET_OBJECTIVE_STEP".to_string());
1328
1329		p.display_process_to_stdout();
1330
1331	}
1332
1333	#[test]
1334	fn test_section_new() {
1335		Section::new();
1336	}
1337
1338	#[test]
1339	fn test_section_get_functions() {
1340
1341		let s:Section = Section::new();
1342
1343		assert_eq!(&s.title,s.get_title());
1344		println!("struct Section / Result of get_title() -> {:?}",s.get_title());
1345
1346		assert!(s.get_all_steps().len()==0);
1347		let _is_right_type:&Vec<Step> = s.get_all_steps();
1348		println!("struct Section / Result of get_all_steps -> [it's empty]");
1349
1350	}
1351
1352	#[test]
1353	fn test_section_set_functions() {
1354
1355		let mut s:Section = Section::new();
1356
1357		s.set_title("SET_SECTION_TITLE");
1358		s.add_step(Step{text:"SET_SECTION_STEP_TEXT".to_string(),resources:vec![],all_sub_steps:vec![],});
1359
1360	}
1361
1362	#[test]
1363	fn test_step_new() {
1364		Step::new();
1365	}
1366
1367	#[test]
1368	fn test_step_get_functions() {
1369
1370		let stp:Step = Step::new();
1371
1372		assert_eq!(&stp.text,stp.get_text());
1373		println!("struct Step / Result of get_text() -> {:?}",stp.get_text());
1374
1375		assert!(stp.get_resources().len()==0);
1376		let _is_right_type:&Vec<Resource> = stp.get_resources();
1377		println!("struct Step / Result of get_resources() -> [it's empty]");
1378
1379		assert!(stp.get_all_sub_steps().len()==0);
1380		let _is_right_type:&Vec<SubStep> = stp.get_all_sub_steps();
1381		println!("struct Step / Result of get_all_sub_steps() -> [it's empty]");
1382
1383	}
1384
1385	#[test]
1386	fn test_step_set_functions() {
1387
1388		let mut stp:Step = Step::new();
1389
1390		stp.set_text("SET_STEP_TEXT");
1391		stp.add_sub_step(SubStep::Warning("SET_STEP_SUB_STEP_WARNING".to_string()));
1392		stp.add_resource(Resource{name:"SET_STEP_SUB_STEP_RESOURCE".to_string(),calibration:false});
1393
1394	}
1395
1396	#[test]
1397	fn test_action_get_functions() {
1398
1399		let a:Action = parse_action_line("ACTION|EXPECTED|TPV_TEXT");
1400
1401		assert_eq!(&a.perform,a.get_perform());
1402		println!("struct Action / Result of get_perform() -> {:?}",a.get_perform());
1403
1404		assert_eq!(&a.expect,a.get_expect());
1405		println!("struct Action / Result of get_expect() -> {:?}",a.get_expect());
1406
1407		assert_eq!(&a.tpv,a.get_tpv());
1408		println!("struct Action / Result of get_tpv() -> {:?}",a.get_tpv());
1409
1410	}
1411
1412	#[test]
1413	fn test_requirement_get_functions() {
1414
1415		let r:Requirement = parse_verification_line("RID|REQ_TEXT|VER_METH");
1416
1417		assert_eq!(&r.id,r.get_id());
1418		println!("struct Requirement / Result of get_id() -> {:?}",r.get_id());
1419
1420		assert_eq!(&r.text,r.get_text());
1421		println!("struct Requirement / Result of get_text() -> {:?}",r.get_text());
1422
1423		assert_eq!("Sampling",r.get_method());
1424		println!("struct Requirement / Result of get_method() -> {:?}",r.get_method());
1425
1426	}
1427
1428	#[test]
1429	fn test_resource_get_functions() {
1430
1431		let res:Resource = Resource{name:"RESOURCE_NAME".to_string(),calibration:false};
1432
1433		assert_eq!(&res.name,res.get_name());
1434		println!("struct Resource / Result of get_name() -> {:?}",res.get_name());
1435
1436		assert_eq!(&res.calibration,res.get_calibration());
1437		println!("struct Resource / Result of get_calibration() -> {:?}",res.get_calibration());
1438
1439	}
1440
1441	#[test]
1442	fn test_read_file_blank() {
1443		let _is_right_type:Process = read_ebml(&"".to_string());
1444	}
1445
1446	// Some test-only functions for creating and destroying files to feed to read_ebml
1447
1448	fn create_test_file(filename:&str, lines:Vec<String>) {
1449		let mut new_file = OpenOptions::new()
1450		            .read(true)
1451		            .write(true)
1452		            .create(true)
1453		            .open(filename)
1454		            .expect("Could not open the file!");
1455        for line in lines {
1456        	new_file.write({line+"\n"}.as_bytes()).expect("Could not write line to test-only file!");
1457        }
1458	}
1459
1460	fn destroy_test_file(filename:&str) {
1461		let _ = fs::remove_file(filename);
1462	}
1463
1464	fn generate_ebml_with_diabolical_comments() -> Vec<String> {
1465		let mut new_vec_of_strings:Vec<String> = vec![];
1466		new_vec_of_strings.push("//".to_string());
1467		new_vec_of_strings.push("///".to_string());
1468		new_vec_of_strings.push("// /".to_string());
1469		new_vec_of_strings.push("/////////".to_string());
1470		new_vec_of_strings.push("//\\\\\\\\\\".to_string());
1471		new_vec_of_strings.push("\\\\Section|Section title".to_string());
1472		new_vec_of_strings.push("\n\n\n".to_string());
1473		new_vec_of_strings.push("//Section|This is a section! Maybe?".to_string());
1474		new_vec_of_strings.push("//Step|This is a step! Maybe?".to_string());
1475		new_vec_of_strings.push("\n\n\n".to_string());
1476		new_vec_of_strings.push("/ /".to_string());
1477		new_vec_of_strings.push("/Section/".to_string());
1478		new_vec_of_strings.push("/Section|Section title/".to_string());
1479		new_vec_of_strings.push("\n\n\n".to_string());
1480		new_vec_of_strings.push(" //".to_string());
1481		new_vec_of_strings.push(" / / / / / / / / ".to_string());
1482		return new_vec_of_strings;
1483	}
1484
1485	fn generate_ebml_with_1000_sections() -> Vec<String> {
1486		let mut new_vec_of_strings:Vec<String> = vec![];
1487		for ii in 0..1000 {
1488			new_vec_of_strings.push("  sEc tIo      N  |Section ".to_string()+&(ii+1).to_string());
1489		}
1490		return new_vec_of_strings;
1491	}
1492
1493	fn generate_ebml_with_1000_steps_in_one_section() -> Vec<String> {
1494		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1".to_string()];
1495		for ii in 0..1000 {
1496			new_vec_of_strings.push("  s T e P   |Step 1.".to_string()+&(ii+1).to_string());
1497		}
1498		return new_vec_of_strings;
1499	}
1500
1501	fn generate_ebml_with_1000_verifications_in_one_step() -> Vec<String> {
1502		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1503		for ii in 0..1000 {
1504			new_vec_of_strings.push("  vEr iFi cAt iOn   |R".to_string()+&(ii+1).to_string()+"|Requirement text|demo");
1505		}
1506		return new_vec_of_strings;
1507	}
1508
1509	fn generate_ebml_with_1000_resources_in_one_step() -> Vec<String> {
1510		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1511		for ii in 0..500 {
1512			new_vec_of_strings.push("  rEs oUr cE   |Really Important Tool #".to_string()+&(ii+1).to_string());
1513		}
1514		for ii in 500..1000 {
1515			new_vec_of_strings.push("  rEs oUr cE   |Really Important Tool #".to_string()+&(ii+1).to_string()+"|cal");
1516		}
1517		return new_vec_of_strings;
1518	}
1519
1520	fn generate_ebml_with_diabolical_calibrated_resources() -> Vec<String> {
1521		// "C"|"CAL"|"CALIB"|"CALIBRATE"|"CALIBRATED"|CALIBRATION"|"Y"|"YES"|"T"|"TRUE"
1522		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1523		new_vec_of_strings.push("Resource|Calibrated Resource|c    ".to_string());
1524		new_vec_of_strings.push("Resource|Calibrated Resource| c   ".to_string());
1525		new_vec_of_strings.push("Resource|Calibrated Resource|  c  ".to_string());
1526		new_vec_of_strings.push("Resource|Calibrated Resource|   c ".to_string());
1527		new_vec_of_strings.push("Resource|Calibrated Resource|    c".to_string());
1528		new_vec_of_strings.push("Resource|Calibrated Resource|C    ".to_string());
1529		new_vec_of_strings.push("Resource|Calibrated Resource| C   ".to_string());
1530		new_vec_of_strings.push("Resource|Calibrated Resource|  C  ".to_string());
1531		new_vec_of_strings.push("Resource|Calibrated Resource|   C ".to_string());
1532		new_vec_of_strings.push("Resource|Calibrated Resource|    C".to_string());
1533		new_vec_of_strings.push("Resource|Calibrated Resource| c    A    l    ".to_string());
1534		new_vec_of_strings.push("Resource|Calibrated Resource|CA    L".to_string());
1535		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b ".to_string());
1536		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b rate ".to_string());
1537		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b rate  d ".to_string());
1538		new_vec_of_strings.push("Resource|Calibrated Resource| c a l i b rat   ion ".to_string());
1539		new_vec_of_strings.push("Resource|Calibrated Resource|      y         ".to_string());
1540		new_vec_of_strings.push("Resource|Calibrated Resource|   y  e  s      ".to_string());
1541		new_vec_of_strings.push("Resource|Calibrated Resource|   t      ".to_string());
1542		new_vec_of_strings.push("Resource|Calibrated Resource|   t r u e   ".to_string());
1543		// 20 calibrated
1544
1545		// And... one that isn't. So... this process should have 20 calibrated (but 21 total resources...)
1546		new_vec_of_strings.push("Resource|Calibrated Resource| nopey dopey!!! ".to_string());
1547
1548		return new_vec_of_strings;
1549
1550	}
1551
1552	fn generate_ebml_with_1000_actions_in_one_step() -> Vec<String> {
1553		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1554		for _ii in 0..1000 {
1555			new_vec_of_strings.push(" a CT i o N      |Thing to do|Thing to Expect|TPV".to_string());
1556			//new_vec_of_strings.push("Action|Thing to do|Thing to Expect|TPV".to_string());
1557		}
1558		return new_vec_of_strings;
1559	}
1560
1561	fn generate_ebml_with_300_tpv_700_non_tpv_actions_in_one_step() -> Vec<String> {
1562		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1563		for _ii in 0..300 {
1564			new_vec_of_strings.push(" aC  tI  oN |Thing to do|Thing to Expect|TPV".to_string());
1565		}
1566		for _ii in 0..700 {
1567			new_vec_of_strings.push("actio      N|Thing to do|Thing to Expect".to_string());
1568		}
1569		return new_vec_of_strings;
1570	}
1571
1572	fn generate_ebml_with_1000_objectives_in_one_step() -> Vec<String> {
1573		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1574		for ii in 0..1000 {
1575			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());
1576		}
1577		return new_vec_of_strings;
1578	}
1579
1580	fn generate_ebml_with_1000_out_of_scopes_in_one_step() -> Vec<String> {
1581		let mut new_vec_of_strings:Vec<String> = vec!["Section|Section 1\nStep|Step 1.1".to_string()];
1582		for ii in 0..1000 {
1583			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());
1584		}
1585		return new_vec_of_strings;
1586	}
1587
1588	fn generate_ebml_set_meta_twice() -> Vec<String> {
1589		let mut new_vec_of_strings:Vec<String> = vec![];
1590		new_vec_of_strings.push("Number|First Number".to_string());
1591		new_vec_of_strings.push("Number|Second Number".to_string());
1592		new_vec_of_strings.push("Title|First Title".to_string());
1593		new_vec_of_strings.push("Title|Second Title".to_string());
1594		new_vec_of_strings.push("Author|First Author".to_string());
1595		new_vec_of_strings.push("Author|Second Author".to_string());
1596		new_vec_of_strings.push("Reviewer|First Reviewer".to_string());
1597		new_vec_of_strings.push("Reviewer|Second Reviewer".to_string());
1598		new_vec_of_strings.push("Subject|First Subject".to_string());
1599		new_vec_of_strings.push("Subject|Second Subject".to_string());
1600		new_vec_of_strings.push("SubjectImage|First SubjectImage".to_string());
1601		new_vec_of_strings.push("SubjectImage|Second SubjectImage".to_string());
1602		new_vec_of_strings.push("Product|First Product".to_string());
1603		new_vec_of_strings.push("Product|Second Product".to_string());
1604		new_vec_of_strings.push("ProductImage|First ProductImage".to_string());
1605		new_vec_of_strings.push("ProductImage|Second ProductImage".to_string());
1606		new_vec_of_strings.push("ProcessType|First ProcessType".to_string());
1607		new_vec_of_strings.push("ProcessType|Second ProcessType".to_string());
1608		return new_vec_of_strings;
1609	}
1610
1611	fn generate_ebml_with_diabolical_whitespace_first_part() -> Vec<String> {
1612		let mut new_vec_of_strings:Vec<String> = vec![];
1613		new_vec_of_strings.push("Section|Section Title".to_string());
1614		new_vec_of_strings.push(" Section|Section Title".to_string());
1615		new_vec_of_strings.push("  Section|Section Title".to_string());
1616		new_vec_of_strings.push("Section |Section Title".to_string());
1617		new_vec_of_strings.push("Section  |Section Title".to_string());
1618		new_vec_of_strings.push(" Section |Section Title".to_string());
1619		new_vec_of_strings.push(" S e c t i o n |Section Title".to_string());
1620		new_vec_of_strings.push("Sec      ti        on       |Section Title".to_string());
1621		new_vec_of_strings.push(" S                    ection|Section Title".to_string());
1622		new_vec_of_strings.push("Section|Section Title".to_string());
1623		// 10 total
1624		return new_vec_of_strings;
1625	}
1626
1627	fn generate_ebml_with_diabolical_section_and_step_triggers() -> Vec<String> {
1628		let mut new_vec_of_strings:Vec<String> = vec![];
1629		// Three sections, each with three steps, let's get goofy AF
1630		new_vec_of_strings.push("Section |Section Title".to_string());
1631		new_vec_of_strings.push(" S t e p |Step Text".to_string());
1632		new_vec_of_strings.push(" A c t i o n |Do This|Expect This|TPV".to_string());
1633		new_vec_of_strings.push(" C o m m a n d |Command Text".to_string());
1634		new_vec_of_strings.push(" I m a g e |ImageFile.Ext|Image Caption".to_string());
1635		new_vec_of_strings.push(" W a r n i n g |Warning Text".to_string());
1636		new_vec_of_strings.push(" V e r i f i c a t i o n |RID|Req Text|T".to_string());
1637		new_vec_of_strings.push(" R e s o u r c e |Resource Text".to_string());
1638		new_vec_of_strings.push("St  ep   |Step Text".to_string());
1639		new_vec_of_strings.push("Com man d|Command Text".to_string());
1640		new_vec_of_strings.push("     Step|Step Text".to_string());
1641		new_vec_of_strings.push(" Command |Command Text".to_string());
1642		new_vec_of_strings.push(" S e c t i o n |Section Title".to_string());
1643		new_vec_of_strings.push("St ep|Step Text".to_string());
1644		new_vec_of_strings.push("C om ma n d|Command Text".to_string());
1645		new_vec_of_strings.push("St  ep|Step Text".to_string());
1646		new_vec_of_strings.push("Command          |Command Text".to_string());
1647		new_vec_of_strings.push("St   ep|Step Text".to_string());
1648		new_vec_of_strings.push(" command |Command Text".to_string());
1649		new_vec_of_strings.push("SECTION|Section Title".to_string());
1650		new_vec_of_strings.push("STEP|Step Text".to_string());
1651		new_vec_of_strings.push(" C O M M A N D|Command Text".to_string());
1652		new_vec_of_strings.push("S T E P |Step Text".to_string());
1653		new_vec_of_strings.push("   CO   MM   AND   |Command Text".to_string());
1654		new_vec_of_strings.push("ST   EP|Step Text".to_string());
1655		new_vec_of_strings.push(" c o MM a n D     |Command Text".to_string());
1656		return new_vec_of_strings;
1657	}
1658
1659	fn generate_ebml_with_diabolical_actions() -> Vec<String> {
1660		let mut new_vec_of_strings:Vec<String> = vec![];
1661		// One Section, three Steps, one ActionSequence apiece
1662		new_vec_of_strings.push("Section |The one and only section".to_string());
1663		new_vec_of_strings.push("Step|lots of bars".to_string());
1664		// ActionSequence should have length 6
1665		new_vec_of_strings.push("Action|Nominal|Nominal|TPV".to_string());
1666		new_vec_of_strings.push("Action|Nominal|Nominal|TPV|".to_string());
1667		new_vec_of_strings.push("Action|Nominal|Nominal|TPV||".to_string());
1668		new_vec_of_strings.push("Action|Nominal|Nominal|TPV|||".to_string());
1669		new_vec_of_strings.push("Action|Nominal|Nominal|TPV||||".to_string());
1670		new_vec_of_strings.push("Action|||TPV|||".to_string());
1671		new_vec_of_strings.push("Step|push the TPV limits - all should be true".to_string());
1672		// ActionSequence should have length 12
1673		new_vec_of_strings.push("Action|Nominal|Nominal|TPV".to_string());
1674		new_vec_of_strings.push("Action|Nominal|Nominal|tpv".to_string());
1675		new_vec_of_strings.push("Action|Nominal|Nominal|TRUE".to_string());
1676		new_vec_of_strings.push("Action|Nominal|Nominal|true".to_string());
1677		new_vec_of_strings.push("Action|Nominal|Nominal|T".to_string());
1678		new_vec_of_strings.push("Action|Nominal|Nominal|t".to_string());
1679		new_vec_of_strings.push("Action|Nominal|Nominal|Two Party Verification".to_string());
1680		new_vec_of_strings.push("Action|Nominal|Nominal|Y".to_string());
1681		new_vec_of_strings.push("Action|Nominal|Nominal|y".to_string());
1682		new_vec_of_strings.push("Action|Nominal|Nominal|Yes".to_string());
1683		new_vec_of_strings.push("Action|Nominal|Nominal|yes".to_string());
1684		new_vec_of_strings.push("Action|Nominal|Nominal|YES".to_string());
1685		new_vec_of_strings.push("Step|these TPVs should be false".to_string());
1686		// ActionSequence should have length 8
1687		new_vec_of_strings.push("Action|Nominal|TPV".to_string());
1688		new_vec_of_strings.push("Action|TPV".to_string());
1689		new_vec_of_strings.push("Action|Nominal|Nominal|naw|TPV".to_string());
1690		new_vec_of_strings.push("Action|Nominal|Nominal|naw|??|TPV".to_string());
1691		new_vec_of_strings.push("Action|Nominal|Nominal|naw|??|??|TPV".to_string());
1692		new_vec_of_strings.push("Action|Nominal|Nominal||??|??|TPV".to_string());
1693		new_vec_of_strings.push("Action|Nominal|Nominal|||??|TPV".to_string());
1694		new_vec_of_strings.push("Action|Nominal|Nominal||||TPV".to_string());
1695		return new_vec_of_strings;
1696	}
1697
1698	fn generate_ebml_with_diabolical_verification_methods() -> Vec<String> {
1699 		let mut new_vec_of_strings:Vec<String> = vec![];
1700		// One Section
1701		new_vec_of_strings.push("Section |The one and only section".to_string());
1702		new_vec_of_strings.push("Step|Analysis".to_string());
1703		// First step has 7 Verification SubSteps, all of them with VerificationMethod of Analysis
1704		new_vec_of_strings.push("Verification|A001|Analysis|Analysis".to_string());
1705		new_vec_of_strings.push("VERIFICATION|A002|Analysis|ANALYSIS".to_string());
1706		new_vec_of_strings.push("verification|A003|Analysis|analysis".to_string());
1707		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());
1708		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());
1709		new_vec_of_strings.push("Verification|A006|Analysis|A".to_string());
1710		new_vec_of_strings.push("Verification|A007|Analysis|        a".to_string());
1711		new_vec_of_strings.push("Step|Inspection".to_string());
1712		// Second step has 7 Verification SubSteps, all of them with VerificationMethod of Inspection
1713		new_vec_of_strings.push("Verification|I001|Inspection|Inspection".to_string());
1714		new_vec_of_strings.push("VERIFICATION|I002|Inspection|INSPECTION".to_string());
1715		new_vec_of_strings.push("verification|I003|Inspection|inspection".to_string());
1716		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());
1717		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());
1718		new_vec_of_strings.push("Verification|I006|Inspection|I".to_string());
1719		new_vec_of_strings.push("Verification|I007|Inspection|        i".to_string());
1720		new_vec_of_strings.push("Step|Test".to_string());
1721		// Third step has 7 Verification SubSteps, all of them with VerificationMethod of Test
1722		new_vec_of_strings.push("Verification|T001|Test|Test".to_string());
1723		new_vec_of_strings.push("VERIFICATION|T002|Test|TEST".to_string());
1724		new_vec_of_strings.push("verification|T003|Test|test".to_string());
1725		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());
1726		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());
1727		new_vec_of_strings.push("Verification|T006|Test|T".to_string());
1728		new_vec_of_strings.push("Verification|T007|Test|        t".to_string());
1729		new_vec_of_strings.push("Step|Sampling".to_string());
1730		// Third step has 9 Verification SubSteps, all of them with VerificationMethod of Sampling
1731		new_vec_of_strings.push("Verification|S001|Sampling|Sampling".to_string());
1732		new_vec_of_strings.push("VERIFICATION|S002|Sampling|SAMPLING".to_string());
1733		new_vec_of_strings.push("verification|S003|Sampling|sampling".to_string());
1734		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());
1735		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());
1736		new_vec_of_strings.push("Verification|S006|Sampling|S".to_string());
1737		new_vec_of_strings.push("Verification|S007|Sampling|        s".to_string());
1738		new_vec_of_strings.push("Verification|S008|Sampling| SAMPLE".to_string());
1739		new_vec_of_strings.push("Verification|S009|Sampling|        s a m PLE   ".to_string());
1740		new_vec_of_strings.push("Step|Demonstration".to_string());
1741		// Third step has 9 Verification SubSteps, all of them with VerificationMethod of Demonstration
1742		new_vec_of_strings.push("Verification|D001|Demonstration|Demonstration".to_string());
1743		new_vec_of_strings.push("VERIFICATION|D002|Demonstration|DEMONSTRATION".to_string());
1744		new_vec_of_strings.push("verification|D003|Demonstration|demonstration".to_string());
1745		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());
1746		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());
1747		new_vec_of_strings.push("Verification|D006|Demonstration|D".to_string());
1748		new_vec_of_strings.push("Verification|D007|Demonstration|        d".to_string());
1749		new_vec_of_strings.push("Verification|D007|Demonstration| DEMO".to_string());
1750		new_vec_of_strings.push("Verification|D007|Demonstration|        d EM o   ".to_string());
1751		/*
1752		"DEMONSTRATION"|"DEMO"|"D"	=> VerificationMethod::Demonstration,
1753		"INSPECTION"|"I"			=> VerificationMethod::Inspection,
1754		"ANALYSIS"|"A"				=> VerificationMethod::Analysis,
1755		"SAMPLING"|"SAMPLE"|"S"		=> VerificationMethod::Sampling,
1756		"TEST"|"T"					=> VerificationMethod::Test,
1757		*/
1758		return new_vec_of_strings;
1759	}
1760
1761	fn generate_ebml_with_diabolical_image_lines() -> Vec<String> {
1762		let mut new_vec_of_strings:Vec<String> = vec![];
1763		// One Section
1764		new_vec_of_strings.push("Section |The one and only section".to_string());
1765		new_vec_of_strings.push("Step|Strange three-part image lines".to_string());
1766		// First step has 5 Images
1767		new_vec_of_strings.push("Image|filename.ext|Caption".to_string());
1768		new_vec_of_strings.push("IMAGE|filename.ext|Caption".to_string());
1769		new_vec_of_strings.push("image|filename.ext|Caption".to_string());
1770		new_vec_of_strings.push("  iM  Ag     E   |filename.ext|Caption".to_string());
1771		new_vec_of_strings.push("           IMage |filename.ext|Caption".to_string());
1772
1773		new_vec_of_strings.push("Step|lots of bars, empty parts".to_string());
1774		// Second step has 5 Images, all of which should have default text for filename and Caption
1775		new_vec_of_strings.push("image |".to_string());
1776		new_vec_of_strings.push("image ||".to_string());
1777		new_vec_of_strings.push("image |||".to_string());
1778		new_vec_of_strings.push("image ||||".to_string());
1779		new_vec_of_strings.push("image |||||||||||||||".to_string());
1780
1781		new_vec_of_strings.push("Step|lots of bars, empty parts".to_string());
1782		// Third step has 5 Images, all of which should have default text for filename and Caption
1783		new_vec_of_strings.push("image |||filename.ext|Caption".to_string());
1784		new_vec_of_strings.push("image ||||filename.ext|Caption".to_string());
1785		new_vec_of_strings.push("image |||||filename.ext|Caption".to_string());
1786		new_vec_of_strings.push("image ||||||filename.ext|Caption".to_string());
1787		new_vec_of_strings.push("image |||||||filename.ext|Caption".to_string());
1788		return new_vec_of_strings;
1789	}
1790
1791	fn generate_ebml_with_diabolical_resources() -> Vec<String> {
1792		let mut new_vec_of_strings:Vec<String> = vec![];
1793		// One Section
1794		new_vec_of_strings.push("Section |The one and only section".to_string());
1795		new_vec_of_strings.push("Step|Strange resource lines".to_string());
1796		// First step has 5 Resources
1797		new_vec_of_strings.push("Resource|Nominal".to_string());
1798		new_vec_of_strings.push("RESOURCE|Nominal".to_string());
1799		new_vec_of_strings.push("resource|Nominal".to_string());
1800		new_vec_of_strings.push(" r e s o u r c e |Nominal".to_string());
1801		new_vec_of_strings.push(" rEs oUr cE      |Nominal".to_string());
1802		new_vec_of_strings.push("Step|Strange resource lines".to_string());
1803		// First step has 5 Resources
1804		new_vec_of_strings.push("Resource|".to_string());
1805		new_vec_of_strings.push("RESOURCE||".to_string());
1806		new_vec_of_strings.push("resource|||".to_string());
1807		new_vec_of_strings.push(" r e s o u r c e ||||".to_string());
1808		new_vec_of_strings.push(" rEs oUr cE      |||||".to_string());
1809		return new_vec_of_strings;
1810	}
1811
1812	fn generate_ebml_with_csv_table_embedded_1000_rows() -> Vec<String> {
1813		let mut new_vec_of_strings:Vec<String> = vec![];
1814		// One Section
1815		new_vec_of_strings.push("Section |The one and only section".to_string());
1816		new_vec_of_strings.push("Step|Strange resource lines".to_string());
1817		// First step has one embedded CSV table
1818		new_vec_of_strings.push("CSV Start | Caption text".to_string());
1819		for _ in 0..1000 {
1820			new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1821		}
1822		new_vec_of_strings.push("CSV End |".to_string());
1823		return new_vec_of_strings;
1824	}
1825
1826	fn generate_ebml_with_csv_table_embedded_rows_wrong_lengths() -> Vec<String> {
1827		let mut new_vec_of_strings:Vec<String> = vec![];
1828		// One Section
1829		new_vec_of_strings.push("Section |The one and only section".to_string());
1830		new_vec_of_strings.push("Step|One and only step".to_string());
1831		// First step has one embedded CSV table
1832		new_vec_of_strings.push("CSV Start | Caption text".to_string());
1833		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1834		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine".to_string());
1835		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight".to_string());
1836		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven".to_string());
1837		new_vec_of_strings.push("One,Two,Three,Four,Five,Six".to_string());
1838		new_vec_of_strings.push("One,Two,Three,Four,Five".to_string());
1839		new_vec_of_strings.push("One,Two,Three,Four".to_string());
1840		new_vec_of_strings.push("One,Two,Three".to_string());
1841		new_vec_of_strings.push("One,Two".to_string());
1842		new_vec_of_strings.push("One".to_string());
1843		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven".to_string());
1844		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve".to_string());
1845		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen".to_string());
1846		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen,Fourteen".to_string());
1847		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Twelve,Thirteen,Fourteen,Fifteen".to_string());
1848		new_vec_of_strings.push("CSV End |".to_string());
1849		return new_vec_of_strings;
1850	}
1851
1852	fn generate_ebml_with_csv_table_embedded_edge_cases() -> Vec<String> {
1853		let mut new_vec_of_strings:Vec<String> = vec![];
1854		// One Section
1855		new_vec_of_strings.push("Section |The one and only section".to_string());
1856		new_vec_of_strings.push("Step|One and only step".to_string());
1857		// First step has one embedded CSV table
1858		new_vec_of_strings.push("CSV Start | Caption text".to_string());
1859		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());
1860		new_vec_of_strings.push("Wait, so you're saying the line above is CSV and not EBML?".to_string());
1861		new_vec_of_strings.push("Yes, that's exactly what I'm saying. YOU are even a CSV line, my friend.".to_string());
1862		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());
1863		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());
1864		new_vec_of_strings.push("CSV End |".to_string());
1865		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Second
1866		new_vec_of_strings.push("CSV End |".to_string());
1867		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Third
1868		new_vec_of_strings.push("CSV End |".to_string());
1869		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Fourth
1870		new_vec_of_strings.push("CSV End |".to_string());
1871		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Fifth
1872		new_vec_of_strings.push("CSV End |".to_string());
1873		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Sixth
1874		new_vec_of_strings.push("CSV End |".to_string());
1875		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Seventh
1876		new_vec_of_strings.push("".to_string());
1877		new_vec_of_strings.push("".to_string());
1878		new_vec_of_strings.push("".to_string());
1879		new_vec_of_strings.push("".to_string());
1880		new_vec_of_strings.push("".to_string());
1881		new_vec_of_strings.push("".to_string());
1882		new_vec_of_strings.push("".to_string());
1883		new_vec_of_strings.push("".to_string());
1884		new_vec_of_strings.push("CSV End |".to_string());
1885		new_vec_of_strings.push("Command | rm -rf lol".to_string());	 // Eigth SUBSTEP (first command)
1886		new_vec_of_strings.push("CSV Start | Caption text".to_string()); // Ninth SUBSTEP (eighth table...)
1887		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
1888		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
1889		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
1890		new_vec_of_strings.push(",,,,CUCU,,,,,".to_string()); // That's the fifth column in the fourth row, of the ninth substep...
1891		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
1892		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
1893		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
1894		new_vec_of_strings.push(",,,,,,,,,,,,,".to_string());
1895		new_vec_of_strings.push("CSV End |".to_string());
1896		return new_vec_of_strings;
1897	}
1898
1899	fn generate_ebml_with_csv_table_embedded_no_end_line() -> Vec<String> {
1900		let mut new_vec_of_strings:Vec<String> = vec![];
1901		// One Section
1902		new_vec_of_strings.push("Section |The one and only section".to_string());
1903		new_vec_of_strings.push("Step|One and only step".to_string());
1904		// First step has one embedded CSV table, but the "Start" line is missing
1905		new_vec_of_strings.push("CSV Start | Caption text".to_string());
1906		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1907		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1908		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1909		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1910		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());	
1911		// new_vec_of_strings.push("CSV End |".to_string());
1912		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());
1913		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());
1914		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());
1915		new_vec_of_strings.push("CSV Start | This SHOULD be read as a CSV line for the ONE table SubStep...".to_string());
1916		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1917		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1918		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1919		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1920		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1921		//new_vec_of_strings.push("CSV End |".to_string());
1922		return new_vec_of_strings;
1923	}
1924
1925	fn generate_ebml_with_csv_table_embedded_no_start_line() -> Vec<String> {
1926		let mut new_vec_of_strings:Vec<String> = vec![];
1927		// One Section
1928		new_vec_of_strings.push("Section |The one and only section".to_string());
1929		new_vec_of_strings.push("Step|One and only step".to_string());
1930		// First step has one embedded CSV table, but the "Start" line is missing
1931		//new_vec_of_strings.push("CSV Start | Caption text".to_string());
1932		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1933		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine".to_string());
1934		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight".to_string());
1935		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven".to_string());
1936		new_vec_of_strings.push("One,Two,Three,Four,Five,Six".to_string());
1937		new_vec_of_strings.push("One,Two,Three,Four,Five".to_string());
1938		new_vec_of_strings.push("One,Two,Three,Four".to_string());
1939		new_vec_of_strings.push("One,Two,Three".to_string());
1940		new_vec_of_strings.push("One,Two".to_string());
1941		new_vec_of_strings.push("One".to_string());
1942		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());
1943		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven".to_string());
1944		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve".to_string());
1945		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen".to_string());
1946		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Thirteen,Fourteen".to_string());
1947		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Eleven,Twelve,Twelve,Thirteen,Fourteen,Fifteen".to_string());
1948		new_vec_of_strings.push("CSV End |".to_string());
1949		new_vec_of_strings.push("WARNING | This is the second SubStep that should be found...".to_string());
1950		return new_vec_of_strings;
1951	}
1952
1953	fn generate_ebml_with_csv_table_external() -> Vec<String> {
1954		let mut new_vec_of_strings:Vec<String> = vec![];
1955		// One Section
1956		new_vec_of_strings.push("Section |The one and only section".to_string());
1957		new_vec_of_strings.push("Step|One and only step".to_string());
1958		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
1959		new_vec_of_strings.push("WARNING | This is the second SubStep that should be found...".to_string());
1960		new_vec_of_strings.push("WARNING | This is the third SubStep that should be found...".to_string());
1961		new_vec_of_strings.push("WARNING | This is the fourth SubStep that should be found...".to_string());
1962		return new_vec_of_strings;
1963	}
1964
1965	fn generate_csv_table_external() -> Vec<String> {
1966		let mut new_vec_of_strings:Vec<String> = vec![];
1967		new_vec_of_strings.push("H1,H2,H3".to_string());
1968		new_vec_of_strings.push("D1,D2,D3".to_string());
1969		new_vec_of_strings.push("D4,D5,D6".to_string());
1970		new_vec_of_strings.push("D7,D8,D9".to_string());
1971		return new_vec_of_strings;
1972	}
1973
1974	fn generate_ebml_with_csv_table_external_stressing() -> Vec<String> {
1975		let mut new_vec_of_strings:Vec<String> = vec![];
1976		// One Section
1977		new_vec_of_strings.push("Section |The one and only section".to_string());
1978		new_vec_of_strings.push("Step|One and only step".to_string());
1979
1980		// First five valid SubStep lines: nominal... call an external file
1981		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
1982		new_vec_of_strings.push("      C S V  F i l e        |        test.csv            |           Caption text          ".to_string());
1983		new_vec_of_strings.push("csvfile|test.csv|Caption text".to_string());
1984		new_vec_of_strings.push("  cSvFiLe  |    test.csv| Caption text".to_string());
1985		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
1986
1987		// False SubSteps that won't be counted: typos
1988		new_vec_of_strings.push("CSVee File | test.csv | Caption text".to_string());
1989		new_vec_of_strings.push("CSV Flie | test.csv | Caption text".to_string());
1990		new_vec_of_strings.push("CSV Fille | test.csv | Caption text".to_string());
1991		new_vec_of_strings.push("CVS File | test.csv | Caption text".to_string());
1992		new_vec_of_strings.push("Cee Ess Vee File | test.csv | Caption text".to_string());
1993
1994		// Valid "embedded" CSV file call, with an "external" CSV call within it, which will be TWO SubSteps... but not more...
1995		// Furthermore, the "embedded" one will count the "external" CSV EBML line as one of its CSV lines... so it will have 3 rows...
1996		new_vec_of_strings.push("CSV Start | Caption text".to_string());
1997		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
1998		new_vec_of_strings.push("CSV File | test.csv | Caption text".to_string());
1999		new_vec_of_strings.push("One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten".to_string());
2000		new_vec_of_strings.push("CSV End |".to_string());
2001
2002		// Running totals:
2003		// One Section
2004		// One Step
2005		// Seven SubSteps, all tables
2006		// > 0-4 : (4,3)
2007		// > 5   : (3,10)
2008		// > 6   : (4,3)
2009		return new_vec_of_strings;
2010	}
2011	
2012	// end test-only functions	
2013	
2014	#[test]
2015	fn test_read_file_all_comments() {
2016		let file_name = "test_comments_only.ebml".to_string();
2017		create_test_file(&file_name,generate_ebml_with_diabolical_comments());
2018		let process = read_ebml(&file_name);
2019		assert_eq!(process.get_all_sections().len(),0);
2020		assert_eq!(process.get_all_resources().len(),0);
2021		assert_eq!(process.get_all_verifications().len(),0);
2022		assert_eq!(process.get_all_templates().len(),0);
2023		destroy_test_file(&file_name);
2024	}
2025
2026	#[test]
2027	fn test_set_process_meta_twice() {
2028		let file_name = "test_process_meta_set_twice.ebml".to_string();
2029		create_test_file(&file_name,generate_ebml_set_meta_twice());
2030		let process = read_ebml(&file_name);
2031		assert_eq!(process.get_number(),"Second Number");
2032		assert_eq!(process.get_title(),"Second Title");
2033		assert_eq!(process.get_author(),"Second Author");
2034		assert_eq!(process.get_reviewer(),"Second Reviewer");
2035		assert_eq!(process.get_subject(),"Second Subject");
2036		assert_eq!(process.get_subject_image(),"Second SubjectImage");
2037		assert_eq!(process.get_product(),"Second Product");
2038		assert_eq!(process.get_product_image(),"Second ProductImage");
2039		destroy_test_file(&file_name);
2040	}
2041
2042	#[test]
2043	fn test_read_file_1000_sections() {
2044		let file_name = "test_1000_sections.ebml".to_string();
2045		create_test_file(&file_name,generate_ebml_with_1000_sections());
2046		let process = read_ebml(&file_name);
2047		assert_eq!(process.get_all_sections().len(),1000);
2048		destroy_test_file(&file_name);
2049	}
2050
2051	#[test]
2052	fn test_read_file_1000_steps() {
2053		let file_name = "test_1000_steps.ebml".to_string();
2054		create_test_file(&file_name,generate_ebml_with_1000_steps_in_one_section());
2055		let process = read_ebml(&file_name);
2056		assert_eq!(process.get_all_sections().len(),1);
2057		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1000);
2058		destroy_test_file(&file_name);
2059	}
2060
2061	#[test]
2062	fn test_read_file_1000_verifications() {
2063		let file_name = "test_1000_verifications.ebml".to_string();
2064		create_test_file(&file_name,generate_ebml_with_1000_verifications_in_one_step());
2065		let process = read_ebml(&file_name);
2066		assert_eq!(process.get_all_sections().len(),1);
2067		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2068		assert_eq!(process.get_all_verifications().len(),1000);
2069		assert_eq!(process.get_all_verifications()[500].1,"Step 1.1");
2070		destroy_test_file(&file_name);
2071	}
2072
2073	#[test]
2074	fn test_read_file_1000_resources() {
2075		let file_name = "test_1000_resources.ebml".to_string();
2076		create_test_file(&file_name,generate_ebml_with_1000_resources_in_one_step());
2077		let process = read_ebml(&file_name);
2078		assert_eq!(process.get_all_sections().len(),1);
2079		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2080		assert_eq!(process.get_all_resources().len(),1000);
2081		assert_eq!(process.get_all_calibrated_resources().len(),500);
2082		assert_eq!(process.get_all_resources()[500].1,"Step 1.1");
2083		destroy_test_file(&file_name);
2084	}
2085
2086	#[test]
2087	fn test_read_file_1000_actions() {
2088		let file_name = "test_1000_actions.ebml".to_string();
2089		create_test_file(&file_name,generate_ebml_with_1000_actions_in_one_step());
2090		let process = read_ebml(&file_name);
2091		assert_eq!(process.get_all_sections().len(),1);
2092		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2093		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
2094		match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2095			SubStep::ActionSequence(list) => assert_eq!(list.len(),1000),
2096			_ => assert!(1==0),
2097		};		
2098		destroy_test_file(&file_name);
2099	}
2100
2101	#[test]
2102	fn test_read_file_300_tpv_700_non_tpv() {
2103		let file_name = "test_300_tpv_700_non_tpv.ebml".to_string();
2104		create_test_file(&file_name,generate_ebml_with_300_tpv_700_non_tpv_actions_in_one_step());
2105		let process = read_ebml(&file_name);
2106		assert_eq!(process.get_tpv_count(),300);
2107		assert_eq!(process.get_non_tpv_count(),700);
2108		destroy_test_file(&file_name);
2109	}
2110
2111	#[test]
2112	fn test_read_file_1000_objectives(){
2113		let file_name = "test_1000_objectives.ebml".to_string();
2114		create_test_file(&file_name,generate_ebml_with_1000_objectives_in_one_step());
2115		let process = read_ebml(&file_name);
2116		assert_eq!(process.get_all_sections().len(),1);
2117		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2118		assert_eq!(process.get_all_objectives().len(),1000);
2119		destroy_test_file(&file_name);
2120	}
2121
2122	#[test]
2123	fn test_read_file_1000_out_of_scopes(){
2124		let file_name = "test_1000_out_of_scopes.ebml".to_string();
2125		create_test_file(&file_name,generate_ebml_with_1000_out_of_scopes_in_one_step());
2126		let process = read_ebml(&file_name);
2127		assert_eq!(process.get_all_sections().len(),1);
2128		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2129		assert_eq!(process.get_all_out_of_scopes().len(),1000);
2130		destroy_test_file(&file_name);
2131	}
2132
2133	#[test]
2134	fn test_read_file_whitespace_first_part() {
2135		let file_name = "test_whitespace_first_part.ebml".to_string();
2136		create_test_file(&file_name,generate_ebml_with_diabolical_whitespace_first_part());
2137		let process = read_ebml(&file_name);
2138		assert_eq!(process.get_all_sections().len(),10);
2139		destroy_test_file(&file_name);
2140	}
2141
2142	#[test]
2143	fn test_extract_section_and_step_triggers() {
2144		let file_name = "test_extract_section_triggers.ebml".to_string();
2145		create_test_file(&file_name,generate_ebml_with_diabolical_section_and_step_triggers());
2146		let process = read_ebml(&file_name);
2147		// SHOULD BE: Three Sections, each with three Steps, but formatted hella goofy
2148		assert_eq!(process.get_all_sections().len(),3);
2149		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),3);
2150		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),6);
2151		assert_eq!(process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps().len(),1);
2152		assert_eq!(process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps().len(),1);
2153		assert_eq!(process.get_all_sections()[1].get_all_steps().len(),3);
2154		assert_eq!(process.get_all_sections()[1].get_all_steps()[0].get_all_sub_steps().len(),1);
2155		assert_eq!(process.get_all_sections()[1].get_all_steps()[1].get_all_sub_steps().len(),1);
2156		assert_eq!(process.get_all_sections()[1].get_all_steps()[2].get_all_sub_steps().len(),1);
2157		assert_eq!(process.get_all_sections()[2].get_all_steps().len(),3);
2158		assert_eq!(process.get_all_sections()[2].get_all_steps()[0].get_all_sub_steps().len(),1);
2159		assert_eq!(process.get_all_sections()[2].get_all_steps()[1].get_all_sub_steps().len(),1);
2160		assert_eq!(process.get_all_sections()[2].get_all_steps()[2].get_all_sub_steps().len(),1);
2161		destroy_test_file(&file_name);
2162	}
2163
2164	#[test]
2165	fn test_action_lines() {
2166		let file_name = "test_action_lines.ebml".to_string();
2167		create_test_file(&file_name,generate_ebml_with_diabolical_actions());
2168		let process = read_ebml(&file_name);
2169		assert_eq!(process.get_all_sections().len(),1);
2170		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),3);
2171		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() {
2172			match substep {
2173				SubStep::ActionSequence(list) => {
2174					assert_eq!(list.len(),6);
2175					for a in list { assert_eq!(a.get_tpv(),&true); }
2176				},
2177				_ => (),
2178			}
2179		}
2180		for substep in process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps() {
2181			match substep {
2182				SubStep::ActionSequence(list) => {
2183					assert_eq!(list.len(),12);
2184					for a in list { assert_eq!(a.get_tpv(),&true); }
2185				},
2186				_ => (),
2187			}
2188		}
2189		for substep in process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps() {
2190			match substep {
2191				SubStep::ActionSequence(list) => {
2192					assert_eq!(list.len(),8);
2193					for a in list { assert_eq!(a.get_tpv(),&false); }
2194				},
2195				_ => (),
2196			}
2197		}
2198		destroy_test_file(&file_name);
2199	}
2200
2201	#[test]
2202	fn test_verification_methods() {
2203		let file_name = "test_verification_methods.ebml".to_string();
2204		create_test_file(&file_name,generate_ebml_with_diabolical_verification_methods());
2205		let process = read_ebml(&file_name);
2206		assert_eq!(process.get_all_sections().len(),1);
2207		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),5);
2208		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),};}
2209		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),};}
2210		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),};}
2211		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),};}
2212		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),};}
2213		destroy_test_file(&file_name);
2214	}
2215
2216	#[test]
2217	fn test_image_lines() {
2218		let file_name = "test_image_lines.ebml".to_string();
2219		create_test_file(&file_name,generate_ebml_with_diabolical_image_lines());
2220		let process = read_ebml(&file_name);
2221		// SubStep::Image("../assets/placeholderImage-small.png".to_string(),"../assets/placeholderImage-small.png".to_string())
2222		assert_eq!(process.get_all_sections().len(),1);
2223		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),3);
2224		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),5);
2225		assert_eq!(process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps().len(),5);
2226		assert_eq!(process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps().len(),5);
2227		for substep in process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps() { 
2228			match substep {
2229				SubStep::Image(f,c) => {
2230					match f.as_str() {"filename.ext" => (), _ => assert!(1==0),};
2231					match c.as_str() {"Caption" => (), _ => assert!(1==0),};
2232				},
2233				_ => assert!(1==0),
2234			};
2235		};
2236		for substep in process.get_all_sections()[0].get_all_steps()[1].get_all_sub_steps() { 
2237			match substep {
2238				SubStep::Image(f,c) => {
2239					match f.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2240					match c.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2241				},
2242				_ => assert!(1==0),
2243			};
2244		};
2245		for substep in process.get_all_sections()[0].get_all_steps()[2].get_all_sub_steps() { 
2246			match substep {
2247				SubStep::Image(f,c) => {
2248					match f.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2249					match c.as_str() {"../assets/placeholderImage-small.png" => (), _ => assert!(1==0),};
2250				},
2251				_ => assert!(1==0),
2252			};
2253		};
2254		
2255		destroy_test_file(&file_name);
2256	}
2257
2258	#[test]
2259	fn test_resource_lines() {
2260		let file_name = "test_resource_lines.ebml".to_string();
2261		create_test_file(&file_name,generate_ebml_with_diabolical_resources());
2262		let process = read_ebml(&file_name);
2263		assert_eq!(process.get_all_sections().len(),1);
2264		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),2);
2265		let mut count_one_point_one = 0;
2266		let mut count_one_point_two = 0;
2267		for (resource,stepno) in process.get_all_resources() {
2268			match stepno.as_str() {
2269				"Step 1.1"	=> {
2270					count_one_point_one +=1;
2271					assert_eq!(resource.get_name().as_str(),"Nominal");
2272				},
2273				"Step 1.2"	=> {
2274					count_one_point_two +=1;
2275					assert_eq!(resource.get_name().as_str(),"ERROR: NO RESOURCE IDENTIFIED");
2276				},
2277				_ 			=> assert!(1==0),
2278			};
2279		}
2280		assert_eq!(count_one_point_one,5);
2281		assert_eq!(count_one_point_two,5);
2282
2283		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_resources().len(),5);
2284		for resource in process.get_all_sections()[0].get_all_steps()[0].get_resources() {
2285			assert_eq!(resource.get_name().as_str(),"Nominal");
2286		}
2287		assert_eq!(process.get_all_sections()[0].get_all_steps()[1].get_resources().len(),5);
2288		for resource in process.get_all_sections()[0].get_all_steps()[1].get_resources() {
2289			assert_eq!(resource.get_name().as_str(),"ERROR: NO RESOURCE IDENTIFIED");
2290		}
2291		destroy_test_file(&file_name);
2292	}
2293
2294	#[test]
2295	fn test_calibrated_resource_lines() {
2296		let file_name = "test_calibrated_resource_lines.ebml".to_string();
2297		create_test_file(&file_name,generate_ebml_with_diabolical_calibrated_resources());
2298		let process = read_ebml(&file_name);
2299		assert_eq!(process.get_all_sections().len(),1);
2300		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2301		assert_eq!(process.get_all_resources().len(),21);
2302		assert_eq!(process.get_all_calibrated_resources().len(),20);
2303		destroy_test_file(&file_name);
2304	}
2305
2306	#[test]
2307	fn test_get_all_commands_count() {
2308		let file_name = "test_get_all_commands_count.ebml".to_string();
2309		create_test_file(&file_name,generate_ebml_with_diabolical_section_and_step_triggers());
2310		let process = read_ebml(&file_name);
2311		// SHOULD BE: Three Sections, each with three Steps, and a total of 9 command lines
2312		assert_eq!(process.get_all_command_lines().len(),9);
2313		destroy_test_file(&file_name);
2314	}
2315
2316	#[test]
2317	fn test_csv_table_embedded_1000_rows() {
2318		let file_name = "test_csv_table_embedded_1000_rows.ebml".to_string();
2319		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_1000_rows());
2320		let process = read_ebml(&file_name);
2321		assert_eq!(process.get_all_sections().len(),1);
2322		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2323		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
2324		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2325			SubStep::Table(table) => table,
2326			_ => &Table::new(),
2327		};
2328		let (rows,cols) = table.get_size();
2329		assert_eq!(rows,1000);
2330		assert_eq!(cols,10);
2331		destroy_test_file(&file_name);
2332	}
2333
2334	#[test]
2335	fn test_csv_table_embedded_data_rows_wrong_lengths() {
2336		let file_name = "test_csv_table_embedded_rows_wrong_lengths.ebml".to_string();
2337		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_rows_wrong_lengths());
2338		let process = read_ebml(&file_name);
2339		assert_eq!(process.get_all_sections().len(),1);
2340		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2341		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),1);
2342		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2343			SubStep::Table(table) => table,
2344			_ => &Table::new(),
2345		};
2346		let (rows,cols) = table.get_size();
2347		assert_eq!(rows,15);
2348		assert_eq!(cols,10);
2349		assert_eq!(table.get_row(1)[9],"".to_string());
2350		assert_eq!(table.get_row(9)[1],"".to_string());
2351		assert_eq!(table.get_row(14)[9],"Ten".to_string());
2352		destroy_test_file(&file_name);
2353	}
2354
2355	#[test]
2356	fn test_csv_table_embedded_edge_cases() {
2357		let file_name = "test_csv_table_embedded_edge_cases.ebml".to_string();
2358		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_edge_cases());
2359		let process = read_ebml(&file_name);
2360		assert_eq!(process.get_all_sections().len(),1);
2361		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2362		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),9);
2363		
2364		// First table: 
2365		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2366			SubStep::Table(table) => table,
2367			_ => &Table::new(),
2368		};
2369		let (rows,cols) = table.get_size();
2370		assert_eq!(rows,5);
2371		assert_eq!(cols,3);
2372		
2373		// Second table: 
2374		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] {
2375			SubStep::Table(table) => table,
2376			_ => &Table::new(),
2377		};
2378		let (rows,cols) = table.get_size();
2379		assert_eq!(rows,0);
2380		assert_eq!(cols,0);
2381
2382		// Seventh table: 
2383		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[6] {
2384			SubStep::Table(table) => table,
2385			_ => &Table::new(),
2386		};
2387		let (rows,cols) = table.get_size();
2388		assert_eq!(rows,8);
2389		assert_eq!(cols,1);
2390		assert_eq!(table.get_caption(),"Caption text");
2391
2392		// Ninth SubStep is the Eighth table...: 
2393		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[8] {
2394			SubStep::Table(table) => table,
2395			_ => &Table::new(),
2396		};
2397		let (rows,cols) = table.get_size();
2398		assert_eq!(rows,8);
2399		assert_eq!(cols,14);
2400		assert_eq!(table.get_caption(),"Caption text");
2401		assert_eq!(table.get_row(3)[4],"CUCU".to_string());
2402		assert_eq!(table.get_row(0)[0],"".to_string());
2403
2404		destroy_test_file(&file_name);
2405	}
2406
2407	#[test]
2408	fn test_csv_table_embedded_no_end_line() {
2409		let file_name = "test_csv_table_embedded_no_end_line.ebml".to_string();
2410		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_no_end_line());
2411		let process = read_ebml(&file_name);
2412		assert_eq!(process.get_all_sections().len(),1);
2413		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2414		// The number of SubSteps is tricky here... if working as expected, we should have:
2415		// > ONE Table as the first SubStep
2416		// > Three Warning SubSteps
2417		// So four SubSteps total...
2418		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),4);
2419		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2420			SubStep::Table(table) => table,
2421			_ => &Table::new(),
2422		};
2423		let (rows,cols) = table.get_size();
2424		// The number of rows in this ONE table is tricky... because the user looks like they wanted two tables, with three warnings in between.
2425		// If working as expected, we should have:
2426		// > 5 intended CSV rows
2427		// > 3 Warning rows that are unintentionally read in as CSV rows because the CSV End is missing
2428		// > 1 CSV Start line following the warnings, read in as CSV unintentionally
2429		// > 5 intended CSV rows, but intended for a second table
2430		// 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...
2431		assert_eq!(rows,14);
2432		assert_eq!(cols,10);
2433		destroy_test_file(&file_name);		
2434	}
2435
2436	#[test]
2437	fn test_csv_table_embedded_no_start_line() {
2438		let file_name = "test_csv_table_embedded_no_start_line.ebml".to_string();
2439		create_test_file(&file_name,generate_ebml_with_csv_table_embedded_no_start_line());
2440		let process = read_ebml(&file_name);
2441		assert_eq!(process.get_all_sections().len(),1);
2442		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2443		// The number of SubSteps is tricky here... if working as expected, we should have:
2444		// > ZERO tables...
2445		// > ONE Context
2446		// > ONE Warning
2447		// So two SubSteps, but neither of them should be Table
2448		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),2);
2449		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] { SubStep::Context(_) => true, _ => false, });
2450		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] { SubStep::Warning(_) => true, _ => false, });
2451		destroy_test_file(&file_name);
2452	}
2453
2454	#[test]
2455	fn test_csv_table_external() {
2456		let file_name_ebml = "././test_csv_table_external.ebml".to_string();
2457		create_test_file(&file_name_ebml,generate_ebml_with_csv_table_external());
2458		
2459		let file_name_csv = "././test.csv".to_string();
2460		create_test_file(&file_name_csv,generate_csv_table_external());		
2461
2462		let process = read_ebml(&file_name_ebml);
2463		
2464		assert_eq!(process.get_all_sections().len(),1);
2465		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2466		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),4);
2467		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] { SubStep::Table(_) => true, _ => false, });
2468		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2469			SubStep::Table(table) => table,
2470			_ => &Table::new(),
2471		};
2472		let (rows,cols) = table.get_size();
2473		assert_eq!(rows,4);
2474		assert_eq!(cols,3);
2475		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] { SubStep::Warning(_) => true, _ => false, });
2476		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[2] { SubStep::Warning(_) => true, _ => false, });
2477		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[3] { SubStep::Warning(_) => true, _ => false, });
2478		
2479		destroy_test_file(&file_name_ebml);
2480		destroy_test_file(&file_name_csv);
2481	}
2482
2483	#[test]
2484	fn test_csv_table_external_stressing() {
2485		let file_name_ebml = "././test_csv_table_external_stressing.ebml".to_string();
2486		create_test_file(&file_name_ebml,generate_ebml_with_csv_table_external_stressing());
2487		
2488		let file_name_csv = "././test.csv".to_string();
2489		create_test_file(&file_name_csv,generate_csv_table_external());		
2490
2491		let process = read_ebml(&file_name_ebml);
2492
2493		// Running totals:
2494		// One Section
2495		// One Step
2496		// Seven SubSteps, all tables
2497		// > 0-4 : (4,3)
2498		// > 5   : (3,10)
2499		// > 6   : (4,3)
2500		assert_eq!(process.get_all_sections().len(),1);
2501		assert_eq!(process.get_all_sections()[0].get_all_steps().len(),1);
2502		assert_eq!(process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps().len(),7);
2503
2504		// 0
2505		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] { SubStep::Table(_) => true, _ => false, });
2506		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[0] {
2507			SubStep::Table(table) => table,
2508			_ => &Table::new(),
2509		};
2510		let (rows,cols) = table.get_size();
2511		assert_eq!(rows,4);
2512		assert_eq!(cols,3);
2513
2514		// 1
2515		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] { SubStep::Table(_) => true, _ => false, });
2516		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[1] {
2517			SubStep::Table(table) => table,
2518			_ => &Table::new(),
2519		};
2520		let (rows,cols) = table.get_size();
2521		assert_eq!(rows,4);
2522		assert_eq!(cols,3);
2523
2524		// 2
2525		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[2] { SubStep::Table(_) => true, _ => false, });
2526		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[2] {
2527			SubStep::Table(table) => table,
2528			_ => &Table::new(),
2529		};
2530		let (rows,cols) = table.get_size();
2531		assert_eq!(rows,4);
2532		assert_eq!(cols,3);
2533
2534		// 3
2535		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[3] { SubStep::Table(_) => true, _ => false, });
2536		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[3] {
2537			SubStep::Table(table) => table,
2538			_ => &Table::new(),
2539		};
2540		let (rows,cols) = table.get_size();
2541		assert_eq!(rows,4);
2542		assert_eq!(cols,3);
2543
2544		// 4
2545		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[4] { SubStep::Table(_) => true, _ => false, });
2546		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[4] {
2547			SubStep::Table(table) => table,
2548			_ => &Table::new(),
2549		};
2550		let (rows,cols) = table.get_size();
2551		assert_eq!(rows,4);
2552		assert_eq!(cols,3);
2553
2554		// 5
2555		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[5] { SubStep::Table(_) => true, _ => false, });
2556		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[5] {
2557			SubStep::Table(table) => table,
2558			_ => &Table::new(),
2559		};
2560		let (rows,cols) = table.get_size();
2561		assert_eq!(rows,3);
2562		assert_eq!(cols,10);
2563
2564		// 6
2565		assert!(match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[6] { SubStep::Table(_) => true, _ => false, });
2566		let table:&Table = match &process.get_all_sections()[0].get_all_steps()[0].get_all_sub_steps()[6] {
2567			SubStep::Table(table) => table,
2568			_ => &Table::new(),
2569		};
2570		let (rows,cols) = table.get_size();
2571		assert_eq!(rows,4);
2572		assert_eq!(cols,3);
2573
2574		destroy_test_file(&file_name_ebml);
2575		destroy_test_file(&file_name_csv);
2576	}
2577
2578}