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