@@ -132,6 +132,7 @@ pub enum Record {
132
132
} ,
133
133
Condition ( Condition ) ,
134
134
Comment ( Vec < String > ) ,
135
+ Newline ,
135
136
/// Internally injected record which should not occur in the test file.
136
137
Injected ( Injected ) ,
137
138
}
@@ -142,9 +143,18 @@ impl Record {
142
143
/// # Panics
143
144
/// If the record is an internally injected record which should not occur in the test file.
144
145
pub fn unparse ( & self , w : & mut impl std:: io:: Write ) -> std:: io:: Result < ( ) > {
146
+ write ! ( w, "{}" , self )
147
+ }
148
+ }
149
+
150
+ /// As is the standard for Display, does not print any trailing
151
+ /// newline except for records that always end with a blank line such
152
+ /// as Query and Statement.
153
+ impl std:: fmt:: Display for Record {
154
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
145
155
match self {
146
156
Record :: Include { loc : _, filename } => {
147
- write ! ( w , "include {}" , filename)
157
+ write ! ( f , "include {}" , filename)
148
158
}
149
159
Record :: Statement {
150
160
loc : _,
@@ -153,21 +163,22 @@ impl Record {
153
163
sql,
154
164
expected_count,
155
165
} => {
156
- write ! ( w , "statement " ) ?;
166
+ write ! ( f , "statement " ) ?;
157
167
match ( expected_count, expected_error) {
158
- ( None , None ) => write ! ( w , "ok" ) ?,
168
+ ( None , None ) => write ! ( f , "ok" ) ?,
159
169
( None , Some ( err) ) => {
160
170
if err. as_str ( ) . is_empty ( ) {
161
- write ! ( w , "error" ) ?;
171
+ write ! ( f , "error" ) ?;
162
172
} else {
163
- write ! ( w , "error {}" , err) ?;
173
+ write ! ( f , "error {}" , err) ?;
164
174
}
165
175
}
166
- ( Some ( cnt) , None ) => write ! ( w , "count {}" , cnt) ?,
176
+ ( Some ( cnt) , None ) => write ! ( f , "count {}" , cnt) ?,
167
177
( Some ( _) , Some ( _) ) => unreachable ! ( ) ,
168
178
}
169
- writeln ! ( w) ?;
170
- write ! ( w, "{}" , sql)
179
+ writeln ! ( f) ?;
180
+ // statement always end with a blank line
181
+ writeln ! ( f, "{}" , sql)
171
182
}
172
183
Record :: Query {
173
184
loc : _,
@@ -179,63 +190,65 @@ impl Record {
179
190
sql,
180
191
expected_results,
181
192
} => {
182
- write ! ( w , "query" ) ?;
193
+ write ! ( f , "query" ) ?;
183
194
if let Some ( err) = expected_error {
184
- writeln ! ( w , " error {}" , err) ?;
185
- return write ! ( w , "{}" , sql) ;
195
+ writeln ! ( f , " error {}" , err) ?;
196
+ return writeln ! ( f , "{}" , sql) ;
186
197
}
187
198
188
199
write ! (
189
- w ,
200
+ f ,
190
201
" {}" ,
191
202
type_string. iter( ) . map( |c| format!( "{c}" ) ) . join( "" )
192
203
) ?;
193
204
if let Some ( sort_mode) = sort_mode {
194
- write ! ( w , " {}" , sort_mode. as_str( ) ) ?;
205
+ write ! ( f , " {}" , sort_mode. as_str( ) ) ?;
195
206
}
196
207
if let Some ( label) = label {
197
- write ! ( w , " {}" , label) ?;
208
+ write ! ( f , " {}" , label) ?;
198
209
}
199
- writeln ! ( w ) ?;
200
- writeln ! ( w , "{}" , sql) ?;
210
+ writeln ! ( f ) ?;
211
+ writeln ! ( f , "{}" , sql) ?;
201
212
202
- write ! ( w , "----" ) ?;
213
+ write ! ( f , "----" ) ?;
203
214
for result in expected_results {
204
- write ! ( w , "\n {}" , result) ?;
215
+ write ! ( f , "\n {}" , result) ?;
205
216
}
206
- Ok ( ( ) )
217
+ // query always ends with a blank line
218
+ writeln ! ( f)
207
219
}
208
220
Record :: Sleep { loc : _, duration } => {
209
- write ! ( w , "sleep {}" , humantime:: format_duration( * duration) )
221
+ write ! ( f , "sleep {}" , humantime:: format_duration( * duration) )
210
222
}
211
223
Record :: Subtest { loc : _, name } => {
212
- write ! ( w , "subtest {}" , name)
224
+ write ! ( f , "subtest {}" , name)
213
225
}
214
226
Record :: Halt { loc : _ } => {
215
- write ! ( w , "halt" )
227
+ write ! ( f , "halt" )
216
228
}
217
229
Record :: Control ( c) => match c {
218
- Control :: SortMode ( m) => write ! ( w , "control sortmode {}" , m. as_str( ) ) ,
230
+ Control :: SortMode ( m) => write ! ( f , "control sortmode {}" , m. as_str( ) ) ,
219
231
} ,
220
232
Record :: Condition ( cond) => match cond {
221
233
Condition :: OnlyIf { engine_name } => {
222
- write ! ( w , "onlyif {}" , engine_name)
234
+ write ! ( f , "onlyif {}" , engine_name)
223
235
}
224
236
Condition :: SkipIf { engine_name } => {
225
- write ! ( w , "skipif {}" , engine_name)
237
+ write ! ( f , "skipif {}" , engine_name)
226
238
}
227
239
} ,
228
240
Record :: HashThreshold { loc : _, threshold } => {
229
- write ! ( w , "hash-threshold {}" , threshold)
241
+ write ! ( f , "hash-threshold {}" , threshold)
230
242
}
231
243
Record :: Comment ( comment) => {
232
244
let mut iter = comment. iter ( ) ;
233
- write ! ( w , "#{}" , iter. next( ) . unwrap( ) . trim_end( ) ) ?;
245
+ write ! ( f , "#{}" , iter. next( ) . unwrap( ) . trim_end( ) ) ?;
234
246
for line in iter {
235
- write ! ( w , "\n #{}" , line. trim_end( ) ) ?;
247
+ write ! ( f , "\n #{}" , line. trim_end( ) ) ?;
236
248
}
237
249
Ok ( ( ) )
238
250
}
251
+ Record :: Newline => Ok ( ( ) ) , // Display doesn't end with newline
239
252
Record :: Injected ( p) => panic ! ( "unexpected injected record: {:?}" , p) ,
240
253
}
241
254
}
@@ -391,6 +404,7 @@ fn parse_inner(loc: &Location, script: &str) -> Result<Vec<Record>, ParseError>
391
404
}
392
405
393
406
if line. is_empty ( ) {
407
+ records. push ( Record :: Newline ) ;
394
408
continue ;
395
409
}
396
410
@@ -603,11 +617,87 @@ fn parse_file_inner(loc: Location) -> Result<Vec<Record>, ParseError> {
603
617
604
618
#[ cfg( test) ]
605
619
mod tests {
606
- use crate :: parse_file;
620
+ use difference:: { Changeset , Difference } ;
621
+
622
+ use super :: * ;
607
623
608
624
#[ test]
609
625
fn test_include_glob ( ) {
610
626
let records = parse_file ( "../examples/include/include_1.slt" ) . unwrap ( ) ;
611
- assert_eq ! ( 14 , records. len( ) ) ;
627
+ assert_eq ! ( 16 , records. len( ) ) ;
628
+ }
629
+
630
+ #[ test]
631
+ fn test_basic ( ) {
632
+ parse_roundtrip ( "../examples/basic/basic.slt" )
633
+ }
634
+
635
+ /// Parses the specified file into Records, and ensures the
636
+ /// results of unparsing them are the same
637
+ ///
638
+ /// Prints a hopefully useful message on failure
639
+ fn parse_roundtrip ( filename : impl AsRef < Path > ) {
640
+ let filename = filename. as_ref ( ) ;
641
+ let input_contents = std:: fs:: read_to_string ( filename) . expect ( "reading file" ) ;
642
+
643
+ let records = parse_file ( filename) . expect ( "parsing to complete" ) ;
644
+
645
+ let unparsed = records
646
+ . iter ( )
647
+ . map ( |record| record. to_string ( ) )
648
+ . collect :: < Vec < _ > > ( ) ;
649
+
650
+ // Technically this will not always be the same due to some whitespace normalization
651
+ //
652
+ // query III
653
+ // select * from foo;
654
+ // ----
655
+ // 1 2
656
+ //
657
+ // Will print out collaposting the spaces between `query`
658
+ //
659
+ // query III
660
+ // select * from foo;
661
+ // ----
662
+ // 1 2
663
+ let output_contents = unparsed. join ( "\n " ) ;
664
+
665
+ let changeset = Changeset :: new ( & input_contents, & output_contents, "\n " ) ;
666
+
667
+ assert ! (
668
+ no_diffs( & changeset) ,
669
+ "Mismatch for {:?}\n \
670
+ *********\n \
671
+ diff:\n \
672
+ *********\n \
673
+ {}\n \n \
674
+ *********\n \
675
+ output:\n \
676
+ *********\n \
677
+ {}\n \n ",
678
+ filename,
679
+ UsefulDiffDisplay ( & changeset) ,
680
+ output_contents,
681
+ ) ;
682
+ }
683
+
684
+ /// returns true if there are no differences in the changeset
685
+ fn no_diffs ( changeset : & Changeset ) -> bool {
686
+ changeset
687
+ . diffs
688
+ . iter ( )
689
+ . all ( |diff| matches ! ( diff, Difference :: Same ( _) ) )
690
+ }
691
+
692
+ struct UsefulDiffDisplay < ' a > ( & ' a Changeset ) ;
693
+
694
+ impl < ' a > std:: fmt:: Display for UsefulDiffDisplay < ' a > {
695
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
696
+ self . 0 . diffs . iter ( ) . try_for_each ( |diff| match diff {
697
+ Difference :: Same ( x) => writeln ! ( f, "{x}" ) ,
698
+ Difference :: Add ( x) => writeln ! ( f, "+ {x}" ) ,
699
+ Difference :: Rem ( x) => writeln ! ( f, "- {x}" ) ,
700
+ } )
701
+ }
612
702
}
613
703
}
0 commit comments