~ubuntu-branches/ubuntu/vivid/golang/vivid

« back to all changes in this revision

Viewing changes to src/pkg/mime/multipart/quotedprintable_test.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-08-20 14:06:23 UTC
  • mfrom: (14.1.23 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130820140623-b414jfxi3m0qkmrq
Tags: 2:1.1.2-2ubuntu1
* Merge from Debian unstable (LP: #1211749, #1202027). Remaining changes:
  - 016-armhf-elf-header.patch: Use correct ELF header for armhf binaries.
  - d/control,control.cross: Update Breaks/Replaces for Ubuntu
    versions to ensure smooth upgrades, regenerate control file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2012 The Go Authors. All rights reserved.
 
2
// Use of this source code is governed by a BSD-style
 
3
// license that can be found in the LICENSE file.
 
4
 
 
5
package multipart
 
6
 
 
7
import (
 
8
        "bufio"
 
9
        "bytes"
 
10
        "errors"
 
11
        "flag"
 
12
        "fmt"
 
13
        "io"
 
14
        "os/exec"
 
15
        "regexp"
 
16
        "sort"
 
17
        "strings"
 
18
        "testing"
 
19
        "time"
 
20
)
 
21
 
 
22
func TestQuotedPrintable(t *testing.T) {
 
23
        tests := []struct {
 
24
                in, want string
 
25
                err      interface{}
 
26
        }{
 
27
                {in: "", want: ""},
 
28
                {in: "foo bar", want: "foo bar"},
 
29
                {in: "foo bar=3D", want: "foo bar="},
 
30
                {in: "foo bar=\n", want: "foo bar"},
 
31
                {in: "foo bar\n", want: "foo bar\n"}, // somewhat lax.
 
32
                {in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF},
 
33
                {in: "foo bar=ab", want: "foo bar", err: "multipart: invalid quoted-printable hex byte 0x61"},
 
34
                {in: "foo bar=0D=0A", want: "foo bar\r\n"},
 
35
                {in: " A B        \r\n C ", want: " A B\r\n C"},
 
36
                {in: " A B =\r\n C ", want: " A B  C"},
 
37
                {in: " A B =\n C ", want: " A B  C"}, // lax. treating LF as CRLF
 
38
                {in: "foo=\nbar", want: "foobar"},
 
39
                {in: "foo\x00bar", want: "foo", err: "multipart: invalid unescaped byte 0x00 in quoted-printable body"},
 
40
                {in: "foo bar\xff", want: "foo bar", err: "multipart: invalid unescaped byte 0xff in quoted-printable body"},
 
41
 
 
42
                // Equal sign.
 
43
                {in: "=3D30\n", want: "=30\n"},
 
44
                {in: "=00=FF0=\n", want: "\x00\xff0"},
 
45
 
 
46
                // Trailing whitespace
 
47
                {in: "foo  \n", want: "foo\n"},
 
48
                {in: "foo  \n\nfoo =\n\nfoo=20\n\n", want: "foo\n\nfoo \nfoo \n\n"},
 
49
 
 
50
                // Tests that we allow bare \n and \r through, despite it being strictly
 
51
                // not permitted per RFC 2045, Section 6.7 Page 22 bullet (4).
 
52
                {in: "foo\nbar", want: "foo\nbar"},
 
53
                {in: "foo\rbar", want: "foo\rbar"},
 
54
                {in: "foo\r\nbar", want: "foo\r\nbar"},
 
55
 
 
56
                // Different types of soft line-breaks.
 
57
                {in: "foo=\r\nbar", want: "foobar"},
 
58
                {in: "foo=\nbar", want: "foobar"},
 
59
                {in: "foo=\rbar", want: "foo", err: "multipart: invalid quoted-printable hex byte 0x0d"},
 
60
                {in: "foo=\r\r\r \nbar", want: "foo", err: `multipart: invalid bytes after =: "\r\r\r \n"`},
 
61
 
 
62
                // Example from RFC 2045:
 
63
                {in: "Now's the time =\n" + "for all folk to come=\n" + " to the aid of their country.",
 
64
                        want: "Now's the time for all folk to come to the aid of their country."},
 
65
        }
 
66
        for _, tt := range tests {
 
67
                var buf bytes.Buffer
 
68
                _, err := io.Copy(&buf, newQuotedPrintableReader(strings.NewReader(tt.in)))
 
69
                if got := buf.String(); got != tt.want {
 
70
                        t.Errorf("for %q, got %q; want %q", tt.in, got, tt.want)
 
71
                }
 
72
                switch verr := tt.err.(type) {
 
73
                case nil:
 
74
                        if err != nil {
 
75
                                t.Errorf("for %q, got unexpected error: %v", tt.in, err)
 
76
                        }
 
77
                case string:
 
78
                        if got := fmt.Sprint(err); got != verr {
 
79
                                t.Errorf("for %q, got error %q; want %q", tt.in, got, verr)
 
80
                        }
 
81
                case error:
 
82
                        if err != verr {
 
83
                                t.Errorf("for %q, got error %q; want %q", tt.in, err, verr)
 
84
                        }
 
85
                }
 
86
        }
 
87
 
 
88
}
 
89
 
 
90
func everySequence(base, alpha string, length int, fn func(string)) {
 
91
        if len(base) == length {
 
92
                fn(base)
 
93
                return
 
94
        }
 
95
        for i := 0; i < len(alpha); i++ {
 
96
                everySequence(base+alpha[i:i+1], alpha, length, fn)
 
97
        }
 
98
}
 
99
 
 
100
var useQprint = flag.Bool("qprint", false, "Compare against the 'qprint' program.")
 
101
 
 
102
var badSoftRx = regexp.MustCompile(`=([^\r\n]+?\n)|([^\r\n]+$)|(\r$)|(\r[^\n]+\n)|( \r\n)`)
 
103
 
 
104
func TestQPExhaustive(t *testing.T) {
 
105
        if *useQprint {
 
106
                _, err := exec.LookPath("qprint")
 
107
                if err != nil {
 
108
                        t.Fatalf("Error looking for qprint: %v", err)
 
109
                }
 
110
        }
 
111
 
 
112
        var buf bytes.Buffer
 
113
        res := make(map[string]int)
 
114
        everySequence("", "0A \r\n=", 6, func(s string) {
 
115
                if strings.HasSuffix(s, "=") || strings.Contains(s, "==") {
 
116
                        return
 
117
                }
 
118
                buf.Reset()
 
119
                _, err := io.Copy(&buf, newQuotedPrintableReader(strings.NewReader(s)))
 
120
                if err != nil {
 
121
                        errStr := err.Error()
 
122
                        if strings.Contains(errStr, "invalid bytes after =:") {
 
123
                                errStr = "invalid bytes after ="
 
124
                        }
 
125
                        res[errStr]++
 
126
                        if strings.Contains(errStr, "invalid quoted-printable hex byte ") {
 
127
                                if strings.HasSuffix(errStr, "0x20") && (strings.Contains(s, "=0 ") || strings.Contains(s, "=A ") || strings.Contains(s, "= ")) {
 
128
                                        return
 
129
                                }
 
130
                                if strings.HasSuffix(errStr, "0x3d") && (strings.Contains(s, "=0=") || strings.Contains(s, "=A=")) {
 
131
                                        return
 
132
                                }
 
133
                                if strings.HasSuffix(errStr, "0x0a") || strings.HasSuffix(errStr, "0x0d") {
 
134
                                        // bunch of cases; since whitespace at the end of of a line before \n is removed.
 
135
                                        return
 
136
                                }
 
137
                        }
 
138
                        if strings.Contains(errStr, "unexpected EOF") {
 
139
                                return
 
140
                        }
 
141
                        if errStr == "invalid bytes after =" && badSoftRx.MatchString(s) {
 
142
                                return
 
143
                        }
 
144
                        t.Errorf("decode(%q) = %v", s, err)
 
145
                        return
 
146
                }
 
147
                if *useQprint {
 
148
                        cmd := exec.Command("qprint", "-d")
 
149
                        cmd.Stdin = strings.NewReader(s)
 
150
                        stderr, err := cmd.StderrPipe()
 
151
                        if err != nil {
 
152
                                panic(err)
 
153
                        }
 
154
                        qpres := make(chan interface{}, 2)
 
155
                        go func() {
 
156
                                br := bufio.NewReader(stderr)
 
157
                                s, _ := br.ReadString('\n')
 
158
                                if s != "" {
 
159
                                        qpres <- errors.New(s)
 
160
                                        if cmd.Process != nil {
 
161
                                                // It can get stuck on invalid input, like:
 
162
                                                // echo -n "0000= " | qprint -d
 
163
                                                cmd.Process.Kill()
 
164
                                        }
 
165
                                }
 
166
                        }()
 
167
                        go func() {
 
168
                                want, err := cmd.Output()
 
169
                                if err == nil {
 
170
                                        qpres <- want
 
171
                                }
 
172
                        }()
 
173
                        select {
 
174
                        case got := <-qpres:
 
175
                                if want, ok := got.([]byte); ok {
 
176
                                        if string(want) != buf.String() {
 
177
                                                t.Errorf("go decode(%q) = %q; qprint = %q", s, want, buf.String())
 
178
                                        }
 
179
                                } else {
 
180
                                        t.Logf("qprint -d(%q) = %v", s, got)
 
181
                                }
 
182
                        case <-time.After(5 * time.Second):
 
183
                                t.Logf("qprint timeout on %q", s)
 
184
                        }
 
185
                }
 
186
                res["OK"]++
 
187
        })
 
188
        var outcomes []string
 
189
        for k, v := range res {
 
190
                outcomes = append(outcomes, fmt.Sprintf("%v: %d", k, v))
 
191
        }
 
192
        sort.Strings(outcomes)
 
193
        got := strings.Join(outcomes, "\n")
 
194
        want := `OK: 21576
 
195
invalid bytes after =: 3397
 
196
multipart: invalid quoted-printable hex byte 0x0a: 1400
 
197
multipart: invalid quoted-printable hex byte 0x0d: 2700
 
198
multipart: invalid quoted-printable hex byte 0x20: 2490
 
199
multipart: invalid quoted-printable hex byte 0x3d: 440
 
200
unexpected EOF: 3122`
 
201
        if got != want {
 
202
                t.Errorf("Got:\n%s\nWant:\n%s", got, want)
 
203
        }
 
204
}