From 1f78541f07e81e75ddc38a0f0afeeea709ea1b18 Mon Sep 17 00:00:00 2001
From: Phil <phil@grmr.de>
Date: Fri, 10 Nov 2017 19:37:13 +0100
Subject: [PATCH] Add tests, reorganize and add length awareness

---
 README.md     |  2 --
 src/lib.rs    |  4 +--
 src/parser.rs | 56 ++++++++++++++++------------------
 src/tests.rs  | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 111 insertions(+), 34 deletions(-)
 create mode 100644 src/tests.rs

diff --git a/README.md b/README.md
index 14f7a3f..1d00e55 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,6 @@ it and implement a decoder it.
 
 ## ToDo
 
-- [ ] make sure we do not parse more than we are supposed to do (limit ourselves by `size`)
-- [ ] port the table driven tests from go
 - [ ] write example cli that pretty prints the fixture's contents
 - [ ] create documentation/tutorial/walkthrough and give it to `nom` community
 	- [ ] describt binary format (result of re)
diff --git a/src/lib.rs b/src/lib.rs
index 1e6d860..4e753dc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,11 +2,12 @@
 extern crate nom;
 
 pub mod parser;
+mod tests;
+
 use std::fmt;
 
 #[derive(Debug)]
 pub struct Pattern {
-    size: u64,
     version: String,
     tempo: f32,
     lines: Vec<Line>,
@@ -41,7 +42,6 @@ impl fmt::Display for Line {
                     "{}{}",
                     if i%4 == 0 { "|" } else {""},
                     match *x { 0 => "-", 1 => "x", _ => "?" }
-
                 ).as_str();
         }
 
diff --git a/src/parser.rs b/src/parser.rs
index 9ee8556..ea613c8 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -17,18 +17,16 @@ named!(parse_line<&[u8], Line>,
     )
 );
 
-named!(parse_pattern<&[u8], Pattern>,
+/*
+ * _Assumption_: All provided data is to be interpreted
+ */
+named!(parse_pattern_inner<&[u8], Pattern>,
     do_parse!(
-                 tag!(b"SPLICE")    >>
-        size:    be_u64             >>
         version: take_str!(32)      >>
         tempo:   le_f32             >>
-        // TODO: make sure we do not parse more than we are supposed to do
-        //       limit ourselves by `size`
         lines:   many0!(parse_line) >>
         (
             Pattern{
-                size: size,
                 version: version.trim_matches('\u{0}').to_string(),
                 tempo: tempo,
                 lines: lines,
@@ -37,30 +35,28 @@ named!(parse_pattern<&[u8], Pattern>,
     )
 );
 
+named!(parse_pattern<&[u8], Pattern>,
+    do_parse!(
+        // make sure magic bytes are present
+        tag!(b"SPLICE")                                       >>
+        // read the length field and give that many bytes to `parse_pattern_inner`
+        data: map!(length_data!(be_u64), parse_pattern_inner) >>
+        (
+            match data {
+                // ignore all unused bytes at the end
+                IResult::Done(_,pattern) => pattern,
+                // XXX: what type of input could trigger this?
+                // TODO: make it not panic and handle it gracefully
+                // TODO: add extra testcase, that triggers this path
+                _ => panic!("this should never be reached")
+            }
+        )
+    )
+);
+
+/*
+ * Wrapper around inner macros.
+ */
 pub fn parse(i: &[u8]) -> IResult<&[u8], Pattern> {
     parse_pattern(i)
 }
-
-/* - - - - - - - */
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn it_works() {}
-
-    #[test]
-    fn test_fixtures() {
-        // TODO: implement(port) table based test
-        let b = include_bytes!("../fixtures/pattern_1.splice");
-
-        match parse_pattern(b) {
-            IResult::Done(_, p) => {
-                println!("{}", p);
-                panic!("yes.")
-            },
-            _ => panic!("nope.")
-        }
-    }
-}
diff --git a/src/tests.rs b/src/tests.rs
new file mode 100644
index 0000000..50b3057
--- /dev/null
+++ b/src/tests.rs
@@ -0,0 +1,83 @@
+#[test]
+fn gochallenge_tests() {
+    use parser::parse;
+    use nom::IResult;
+
+    use std::io::prelude::*;
+    use std::fs::File;
+
+    let table: Vec<(&str, &str)> = vec![
+        (
+            "fixtures/pattern_1.splice",
+            "Saved with HW Version: 0.808-alpha
+Tempo: 120
+(0) kick	|x---|x---|x---|x---|
+(1) snare	|----|x---|----|x---|
+(2) clap	|----|x-x-|----|----|
+(3) hh-open	|--x-|--x-|x-x-|--x-|
+(4) hh-close	|x---|x---|----|x--x|
+(5) cowbell	|----|----|--x-|----|
+"
+        ),
+        (
+            "fixtures/pattern_2.splice",
+            "Saved with HW Version: 0.808-alpha
+Tempo: 98.4
+(0) kick	|x---|----|x---|----|
+(1) snare	|----|x---|----|x---|
+(3) hh-open	|--x-|--x-|x-x-|--x-|
+(5) cowbell	|----|----|x---|----|
+"
+        ),
+        (
+            "fixtures/pattern_3.splice",
+            "Saved with HW Version: 0.808-alpha
+Tempo: 118
+(40) kick	|x---|----|x---|----|
+(1) clap	|----|x---|----|x---|
+(3) hh-open	|--x-|--x-|x-x-|--x-|
+(5) low-tom	|----|---x|----|----|
+(12) mid-tom	|----|----|x---|----|
+(9) hi-tom	|----|----|-x--|----|
+",
+        ),
+        (
+            "fixtures/pattern_4.splice",
+            "Saved with HW Version: 0.909
+Tempo: 240
+(0) SubKick	|----|----|----|----|
+(1) Kick	|x---|----|x---|----|
+(99) Maracas	|x-x-|x-x-|x-x-|x-x-|
+(255) Low Conga	|----|x---|----|x---|
+",
+        ),
+        (
+            "fixtures/pattern_5.splice",
+            "Saved with HW Version: 0.708-alpha
+Tempo: 999
+(1) Kick	|x---|----|x---|----|
+(2) HiHat	|x-x-|x-x-|x-x-|x-x-|
+",
+        ),
+    ];
+
+    for r in table.iter() {
+        println!("expected of {}:\n{}", r.0, r.1);
+
+        let mut buf: Vec<u8> = vec![];
+        let mut file = File::open(r.0).expect("file exists");
+        let _ = file.read_to_end(&mut buf).expect("file can be read");
+
+        match parse(&buf[..]) {
+            IResult::Done(_, pattern) => {
+                println!("Parsing is done.");
+                assert_eq!(r.1, format!("{}", pattern));
+            }
+            e => {
+                panic!("something went wrong. {:?}", e);
+            }
+        }
+
+        println!("--------------");
+    }
+}
-- 
GitLab