Merge pull request #148 from farjump/fix-byte-slice-formatter

Always escape a slice of bytes
diff --git a/format/format.go b/format/format.go
index 226b892..06355d9 100644
--- a/format/format.go
+++ b/format/format.go
@@ -7,6 +7,7 @@
 	"fmt"
 	"reflect"
 	"strings"
+	"strconv"
 )
 
 // Use MaxDepth to set the maximum recursion depth when printing deeply nested objects
@@ -143,9 +144,6 @@
 	case reflect.Ptr:
 		return formatValue(value.Elem(), indentation)
 	case reflect.Slice:
-		if value.Type().Elem().Kind() == reflect.Uint8 {
-			return formatString(value.Bytes(), indentation)
-		}
 		return formatSlice(value, indentation)
 	case reflect.String:
 		return formatString(value.String(), indentation)
@@ -189,6 +187,10 @@
 }
 
 func formatSlice(v reflect.Value, indentation uint) string {
+	if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 && isPrintableString(string(v.Bytes())){
+		return formatString(v.Bytes(), indentation)
+	}
+
 	l := v.Len()
 	result := make([]string, l)
 	longest := 0
@@ -261,3 +263,15 @@
 
 	return false
 }
+
+/*
+Returns true when the string is entirely made of printable runes, false otherwise.
+*/
+func isPrintableString(str string) bool {
+	for _, runeValue := range str {
+		if !strconv.IsPrint(runeValue) {
+			return false
+		}
+	}
+	return true
+}
diff --git a/format/format_test.go b/format/format_test.go
index 59517fe..1391fe7 100644
--- a/format/format_test.go
+++ b/format/format_test.go
@@ -162,13 +162,19 @@
 		})
 
 		Describe("formatting []byte slices", func() {
-			It("should present them as strings", func() {
-				b := []byte("a\nb\nc")
-				Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `a
-    b
-    c`))
-			})
-		})
+      Context("when the slice is made of printable bytes", func () {
+        It("should present it as string", func() {
+          b := []byte("a b c")
+          Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `a b c`))
+        })
+      })
+      Context("when the slice contains non-printable bytes", func () {
+        It("should present it as slice", func() {
+          b := []byte("a b c\n\x01\x02\x03\xff\x1bH")
+          Ω(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:12, cap:\d+`,  `\[97, 32, 98, 32, 99, 10, 1, 2, 3, 255, 27, 72\]`))
+        })
+      })
+    })
 
 		Describe("formatting functions", func() {
 			It("should give the type and format values correctly", func() {