<commit_before>// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package client

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"github.com/gogf/gf/internal/intlog"
	"github.com/gogf/gf/internal/json"
	"github.com/gogf/gf/internal/utils"
	"github.com/gogf/gf/net/ghttp/internal/httputil"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gogf/gf/encoding/gparser"
	"github.com/gogf/gf/text/gregex"
	"github.com/gogf/gf/text/gstr"
	"github.com/gogf/gf/util/gconv"

	"github.com/gogf/gf/os/gfile"
)

// Get send GET request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Get(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("GET", url, data...)
}

// Put send PUT request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Put(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("PUT", url, data...)
}

// Post sends request using HTTP method POST and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Post(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("POST", url, data...)
}

// Delete send DELETE request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Delete(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("DELETE", url, data...)
}

// Head send HEAD request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Head(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("HEAD", url, data...)
}

// Patch send PATCH request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Patch(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("PATCH", url, data...)
}

// Connect send CONNECT request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Connect(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("CONNECT", url, data...)
}

// Options send OPTIONS request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Options(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("OPTIONS", url, data...)
}

// Trace send TRACE request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Trace(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("TRACE", url, data...)
}

// DoRequest sends request with given HTTP method and data and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
//
// Note that it uses "multipart/form-data" as its Content-Type if it contains file uploading,
// else it uses "application/x-www-form-urlencoded". It also automatically detects the post
// content for JSON format, and for that it automatically sets the Content-Type as
// "application/json".
func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Response, err error) {
	req, err := c.prepareRequest(method, url, data...)
	if err != nil {
		return nil, err
	}

	// Client middleware.
	if len(c.middlewareHandler) > 0 {
		mdlHandlers := make([]HandlerFunc, 0, len(c.middlewareHandler)+1)
		mdlHandlers = append(mdlHandlers, c.middlewareHandler...)
		mdlHandlers = append(mdlHandlers, func(cli *Client, r *http.Request) (*Response, error) {
			return cli.callRequest(r)
		})
		ctx := context.WithValue(req.Context(), clientMiddlewareKey, &clientMiddleware{
			client:       c,
			handlers:     mdlHandlers,
			handlerIndex: -1,
		})
		req = req.WithContext(ctx)
		resp, err = c.Next(req)
	} else {
		resp, err = c.callRequest(req)
	}

	// Auto saving cookie content.
	if c.browserMode && resp != nil {
		now := time.Now()
		for _, v := range resp.Response.Cookies() {
			if !v.Expires.IsZero() && v.Expires.UnixNano() < now.UnixNano() {
				delete(c.cookies, v.Name)
			} else {
				c.cookies[v.Name] = v.Value
			}
		}
	}
	return resp, err
}

// prepareRequest verifies request parameters, builds and returns http request.
func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *http.Request, err error) {
	method = strings.ToUpper(method)
	if len(c.prefix) > 0 {
		url = c.prefix + gstr.Trim(url)
	}
	param := ""
	if len(data) > 0 {
		switch c.header["Content-Type"] {
		case "application/json":
			switch data[0].(type) {
			case string, []byte:
				param = gconv.String(data[0])
			default:
				if b, err := json.Marshal(data[0]); err != nil {
					return nil, err
				} else {
					param = gconv.UnsafeBytesToStr(b)
				}
			}
		case "application/xml":
			switch data[0].(type) {
			case string, []byte:
				param = gconv.String(data[0])
			default:
				if b, err := gparser.VarToXml(data[0]); err != nil {
					return nil, err
				} else {
					param = gconv.UnsafeBytesToStr(b)
				}
			}
		default:
			param = httputil.BuildParams(data[0])
		}
	}
	if method == "GET" {
		// It appends the parameters to the url if http method is GET.
		if param != "" {
			if gstr.Contains(url, "?") {
				url = url + "&" + param
			} else {
				url = url + "?" + param
			}
		}
		if req, err = http.NewRequest(method, url, bytes.NewBuffer(nil)); err != nil {
			return nil, err
		}
	} else {
		if strings.Contains(param, "@file:") {
			// File uploading request.
			buffer := new(bytes.Buffer)
			writer := multipart.NewWriter(buffer)
			for _, item := range strings.Split(param, "&") {
				array := strings.Split(item, "=")
				if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 {
					path := array[1][6:]
					if !gfile.Exists(path) {
						return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path))
					}
					if file, err := writer.CreateFormFile(array[0], gfile.Basename(path)); err == nil {
						if f, err := os.Open(path); err == nil {
							if _, err = io.Copy(file, f); err != nil {
								if err := f.Close(); err != nil {
									intlog.Errorf(`%+v`, err)
								}
								return nil, err
							}
							if err := f.Close(); err != nil {
								intlog.Errorf(`%+v`, err)
							}
						} else {
							return nil, err
						}
					} else {
						return nil, err
					}
				} else {
					if err = writer.WriteField(array[0], array[1]); err != nil {
						return nil, err
					}
				}
			}
			// Close finishes the multipart message and writes the trailing
			// boundary end line to the output.
			if err = writer.Close(); err != nil {
				return nil, err
			}

			if req, err = http.NewRequest(method, url, buffer); err != nil {
				return nil, err
			} else {
				req.Header.Set("Content-Type", writer.FormDataContentType())
			}
		} else {
			// Normal request.
			paramBytes := []byte(param)
			if req, err = http.NewRequest(method, url, bytes.NewReader(paramBytes)); err != nil {
				return nil, err
			} else {
				if v, ok := c.header["Content-Type"]; ok {
					// Custom Content-Type.
					req.Header.Set("Content-Type", v)
				} else if len(paramBytes) > 0 {
					if (paramBytes[0] == '[' || paramBytes[0] == '{') && json.Valid(paramBytes) {
						// Auto detecting and setting the post content format: JSON.
						req.Header.Set("Content-Type", "application/json")
					} else if gregex.IsMatchString(`^[\w\[\]]+=.+`, param) {
						// If the parameters passed like "name=value", it then uses form type.
						req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
					}
				}
			}
		}
	}

	// Context.
	if c.ctx != nil {
		req = req.WithContext(c.ctx)
	} else {
		req = req.WithContext(context.Background())
	}
	// Custom header.
	if len(c.header) > 0 {
		for k, v := range c.header {
			req.Header.Set(k, v)
		}
	}
	// It's necessary set the req.Host if you want to custom the host value of the request.
	// It uses the "Host" value from header if it's not set in the request.
	if host := req.Header.Get("Host"); host != "" && req.Host == "" {
		req.Host = host
	}
	// Custom Cookie.
	if len(c.cookies) > 0 {
		headerCookie := ""
		for k, v := range c.cookies {
			if len(headerCookie) > 0 {
				headerCookie += ";"
			}
			headerCookie += k + "=" + v
		}
		if len(headerCookie) > 0 {
			req.Header.Set("Cookie", headerCookie)
		}
	}
	// HTTP basic authentication.
	if len(c.authUser) > 0 {
		req.SetBasicAuth(c.authUser, c.authPass)
	}
	// Client agent.
	if c.agent != "" {
		req.Header.Set("User-Agent", c.agent)
	}
	return req, nil
}

// callRequest sends request with give http.Request, and returns the responses object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) callRequest(req *http.Request) (resp *Response, err error) {
	resp = &Response{
		request: req,
	}
	// Dump feature.
	// The request body can be reused for dumping
	// raw HTTP request-response procedure.
	if c.dump {
		reqBodyContent, _ := ioutil.ReadAll(req.Body)
		resp.requestBody = reqBodyContent
		req.Body = utils.NewReadCloser(reqBodyContent, false)
	}
	for {
		if resp.Response, err = c.Do(req); err != nil {
			// The response might not be nil when err != nil.
			if resp.Response != nil {
				if err := resp.Response.Body.Close(); err != nil {
					intlog.Errorf(`%+v`, err)
				}
			}
			if c.retryCount > 0 {
				c.retryCount--
				time.Sleep(c.retryInterval)
			} else {
				//return resp, err
				break
			}
		} else {
			break
		}
	}
	return resp, err
}
<commit_msg>Update client_request.go<commit_after>// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package client

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"github.com/gogf/gf/internal/intlog"
	"github.com/gogf/gf/internal/json"
	"github.com/gogf/gf/internal/utils"
	"github.com/gogf/gf/net/ghttp/internal/httputil"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gogf/gf/encoding/gparser"
	"github.com/gogf/gf/text/gregex"
	"github.com/gogf/gf/text/gstr"
	"github.com/gogf/gf/util/gconv"

	"github.com/gogf/gf/os/gfile"
)

// Get send GET request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Get(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("GET", url, data...)
}

// Put send PUT request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Put(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("PUT", url, data...)
}

// Post sends request using HTTP method POST and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Post(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("POST", url, data...)
}

// Delete send DELETE request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Delete(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("DELETE", url, data...)
}

// Head send HEAD request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Head(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("HEAD", url, data...)
}

// Patch send PATCH request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Patch(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("PATCH", url, data...)
}

// Connect send CONNECT request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Connect(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("CONNECT", url, data...)
}

// Options send OPTIONS request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Options(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("OPTIONS", url, data...)
}

// Trace send TRACE request and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) Trace(url string, data ...interface{}) (*Response, error) {
	return c.DoRequest("TRACE", url, data...)
}

// DoRequest sends request with given HTTP method and data and returns the response object.
// Note that the response object MUST be closed if it'll be never used.
//
// Note that it uses "multipart/form-data" as its Content-Type if it contains file uploading,
// else it uses "application/x-www-form-urlencoded". It also automatically detects the post
// content for JSON format, and for that it automatically sets the Content-Type as
// "application/json".
func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Response, err error) {
	req, err := c.prepareRequest(method, url, data...)
	if err != nil {
		return nil, err
	}

	// Client middleware.
	if len(c.middlewareHandler) > 0 {
		mdlHandlers := make([]HandlerFunc, 0, len(c.middlewareHandler)+1)
		mdlHandlers = append(mdlHandlers, c.middlewareHandler...)
		mdlHandlers = append(mdlHandlers, func(cli *Client, r *http.Request) (*Response, error) {
			return cli.callRequest(r)
		})
		ctx := context.WithValue(req.Context(), clientMiddlewareKey, &clientMiddleware{
			client:       c,
			handlers:     mdlHandlers,
			handlerIndex: -1,
		})
		req = req.WithContext(ctx)
		resp, err = c.Next(req)
	} else {
		resp, err = c.callRequest(req)
	}

	// Auto saving cookie content.
	if c.browserMode && resp != nil {
		now := time.Now()
		for _, v := range resp.Response.Cookies() {
			if !v.Expires.IsZero() && v.Expires.UnixNano() < now.UnixNano() {
				delete(c.cookies, v.Name)
			} else {
				c.cookies[v.Name] = v.Value
			}
		}
	}
	return resp, err
}

// prepareRequest verifies request parameters, builds and returns http request.
func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *http.Request, err error) {
	method = strings.ToUpper(method)
	if len(c.prefix) > 0 {
		url = c.prefix + gstr.Trim(url)
	}
	param := ""
	if len(data) > 0 {
		switch c.header["Content-Type"] {
		case "application/json":
			switch data[0].(type) {
			case string, []byte:
				param = gconv.String(data[0])
			default:
				if b, err := json.Marshal(data[0]); err != nil {
					return nil, err
				} else {
					param = gconv.UnsafeBytesToStr(b)
				}
			}
		case "application/xml":
			switch data[0].(type) {
			case string, []byte:
				param = gconv.String(data[0])
			default:
				if b, err := gparser.VarToXml(data[0]); err != nil {
					return nil, err
				} else {
					param = gconv.UnsafeBytesToStr(b)
				}
			}
		default:
			param = httputil.BuildParams(data[0])
		}
	}
	if method == "GET" {
		// It appends the parameters to the url if http method is GET.
		if param != "" {
			if gstr.Contains(url, "?") {
				url = url + "&" + param
			} else {
				url = url + "?" + param
			}
		}
		if req, err = http.NewRequest(method, url, bytes.NewBuffer(nil)); err != nil {
			return nil, err
		}
	} else {
		if strings.Contains(param, "@file:") {
			// File uploading request.
			buffer := new(bytes.Buffer)
			writer := multipart.NewWriter(buffer)
			for _, item := range strings.Split(param, "&") {
				array := strings.Split(item, "=")
				if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 {
					path := array[1][6:]
					if !gfile.Exists(path) {
						return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path))
					}
					if file, err := writer.CreateFormFile(array[0], gfile.Basename(path)); err == nil {
						if f, err := os.Open(path); err == nil {
							if _, err = io.Copy(file, f); err != nil {
								if err := f.Close(); err != nil {
									intlog.Errorf(`%+v`, err)
								}
								return nil, err
							}
							if err := f.Close(); err != nil {
								intlog.Errorf(`%+v`, err)
							}
						} else {
							return nil, err
						}
					} else {
						return nil, err
					}
				} else {
					if err = writer.WriteField(array[0], array[1]); err != nil {
						return nil, err
					}
				}
			}
			// Close finishes the multipart message and writes the trailing
			// boundary end line to the output.
			if err = writer.Close(); err != nil {
				return nil, err
			}

			if req, err = http.NewRequest(method, url, buffer); err != nil {
				return nil, err
			} else {
				req.Header.Set("Content-Type", writer.FormDataContentType())
			}
		} else {
			// Normal request.
			paramBytes := []byte(param)
			if req, err = http.NewRequest(method, url, bytes.NewReader(paramBytes)); err != nil {
				return nil, err
			} else {
				if v, ok := c.header["Content-Type"]; ok {
					// Custom Content-Type.
					req.Header.Set("Content-Type", v)
				} else if len(paramBytes) > 0 {
					if (paramBytes[0] == '[' || paramBytes[0] == '{') && json.Valid(paramBytes) {
						// Auto detecting and setting the post content format: JSON.
						req.Header.Set("Content-Type", "application/json")
					} else if gregex.IsMatchString(`^[\w\[\]]+=.+`, param) {
						// If the parameters passed like "name=value", it then uses form type.
						req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
					}
				}
			}
		}
	}

	// Context.
	if c.ctx != nil {
		req = req.WithContext(c.ctx)
	} else {
		req = req.WithContext(context.Background())
	}
	// Client agent.
	if c.agent != "" {
		req.Header.Set("User-Agent", c.agent)
	}
	// Custom header.
	if len(c.header) > 0 {
		for k, v := range c.header {
			req.Header.Set(k, v)
		}
	}
	// It's necessary set the req.Host if you want to custom the host value of the request.
	// It uses the "Host" value from header if it's not set in the request.
	if host := req.Header.Get("Host"); host != "" && req.Host == "" {
		req.Host = host
	}
	// Custom Cookie.
	if len(c.cookies) > 0 {
		headerCookie := ""
		for k, v := range c.cookies {
			if len(headerCookie) > 0 {
				headerCookie += ";"
			}
			headerCookie += k + "=" + v
		}
		if len(headerCookie) > 0 {
			req.Header.Set("Cookie", headerCookie)
		}
	}
	// HTTP basic authentication.
	if len(c.authUser) > 0 {
		req.SetBasicAuth(c.authUser, c.authPass)
	}
	return req, nil
}

// callRequest sends request with give http.Request, and returns the responses object.
// Note that the response object MUST be closed if it'll be never used.
func (c *Client) callRequest(req *http.Request) (resp *Response, err error) {
	resp = &Response{
		request: req,
	}
	// Dump feature.
	// The request body can be reused for dumping
	// raw HTTP request-response procedure.
	if c.dump {
		reqBodyContent, _ := ioutil.ReadAll(req.Body)
		resp.requestBody = reqBodyContent
		req.Body = utils.NewReadCloser(reqBodyContent, false)
	}
	for {
		if resp.Response, err = c.Do(req); err != nil {
			// The response might not be nil when err != nil.
			if resp.Response != nil {
				if err := resp.Response.Body.Close(); err != nil {
					intlog.Errorf(`%+v`, err)
				}
			}
			if c.retryCount > 0 {
				c.retryCount--
				time.Sleep(c.retryInterval)
			} else {
				//return resp, err
				break
			}
		} else {
			break
		}
	}
	return resp, err
}
<|endoftext|>


<commit_before>package drop

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/vjeantet/bitfan/processors/doc"
	"github.com/vjeantet/bitfan/processors/testutils"
)

func TestNew(t *testing.T) {
	p := New()
	_, ok := p.(*processor)
	assert.Equal(t, ok, true, "New() should return a processor")
}
func TestDoc(t *testing.T) {
	assert.IsType(t, &doc.Processor{}, New().(*processor).Doc())
}
func getExampleConfiguration() map[string]interface{} {
	return map[string]interface{}{
		"add_field": map[string]interface{}{
			"test1": "myvalue",
		},
	}
}

func TestConfigureDefaults(t *testing.T) {
	p := New().(*processor)
	conf := map[string]interface{}{}
	ctx := testutils.NewProcessorContext()
	ret := p.Configure(ctx, conf)
	assert.Equal(t, nil, ret, "configuration is correct, it should return nil")
	assert.Equal(t, 100, p.opt.Percentage)
}

func TestConfigure(t *testing.T) {
	p := New().(*processor)
	conf := getExampleConfiguration()
	ctx := testutils.NewProcessorContext()
	ret := p.Configure(ctx, conf)
	assert.Equal(t, nil, ret, "configuration is correct, it should return nil")

	assert.Equal(t, len(p.opt.Add_field), 1)
}

func TestReceiveDropAll(t *testing.T) {
	p := New().(*processor)
	ctx := testutils.NewProcessorContext()
	p.Configure(ctx, getExampleConfiguration())
	for i := 1; i <= 1000; i++ {
		em := testutils.NewPacket("", nil)
		p.Receive(em)
	}

	assert.Equal(t, 0, ctx.BuiltPacketsCount(), "unexpected event was created by the processor")
	assert.Equal(t, 0, ctx.SentPacketsCount(0), "all events should be dropped")
}
func TestReceiveDrop99p(t *testing.T) {
	p := New().(*processor)
	ctx := testutils.NewProcessorContext()
	p.Configure(ctx, getExampleConfiguration())
	p.opt.Percentage = 99
	total := 1000
	for i := 1; i <= total; i++ {
		em := testutils.NewPacket("", nil)
		p.Receive(em)
	}

	expected := total / 100 * (100 - p.opt.Percentage)
	delta := total / 100
	assert.Equal(t, 0, ctx.BuiltPacketsCount(), "unexpected event was created by the processor")
	assert.InDelta(t, expected, ctx.SentPacketsCount(0), float64(delta))
	assert.Equal(t, "myvalue", ctx.SentPackets(0)[0].Fields().ValueOrEmptyForPathString("test1"), "a new should field be added")
}
func TestReceiveDrop80p(t *testing.T) {
	p := New().(*processor)
	ctx := testutils.NewProcessorContext()
	p.Configure(ctx, getExampleConfiguration())
	p.opt.Percentage = 80
	total := 10000
	for i := 1; i <= total; i++ {
		em := testutils.NewPacket("", nil)
		p.Receive(em)
	}

	expected := total / 100 * (100 - p.opt.Percentage)
	delta := total / 100
	assert.Equal(t, 0, ctx.BuiltPacketsCount(), "unexpected event was created by the processor")
	assert.InDelta(t, expected, ctx.SentPacketsCount(0), float64(delta))
	assert.Equal(t, "myvalue", ctx.SentPackets(0)[0].Fields().ValueOrEmptyForPathString("test1"), "a new should field be added")
}
<commit_msg>tests : fix filter-drop tests<commit_after>package drop

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/vjeantet/bitfan/processors/doc"
	"github.com/vjeantet/bitfan/processors/testutils"
)

func TestNew(t *testing.T) {
	p := New()
	_, ok := p.(*processor)
	assert.Equal(t, ok, true, "New() should return a processor")
}
func TestDoc(t *testing.T) {
	assert.IsType(t, &doc.Processor{}, New().(*processor).Doc())
}
func getExampleConfiguration() map[string]interface{} {
	return map[string]interface{}{
		"add_field": map[string]interface{}{
			"test1": "myvalue",
		},
	}
}

func TestConfigureDefaults(t *testing.T) {
	p := New().(*processor)
	conf := map[string]interface{}{}
	ctx := testutils.NewProcessorContext()
	ret := p.Configure(ctx, conf)
	assert.Equal(t, nil, ret, "configuration is correct, it should return nil")
	assert.Equal(t, 100, p.opt.Percentage)
}

func TestConfigure(t *testing.T) {
	p := New().(*processor)
	conf := getExampleConfiguration()
	ctx := testutils.NewProcessorContext()
	ret := p.Configure(ctx, conf)
	assert.Equal(t, nil, ret, "configuration is correct, it should return nil")

	assert.Equal(t, len(p.opt.AddField), 1)
}

func TestReceiveDropAll(t *testing.T) {
	p := New().(*processor)
	ctx := testutils.NewProcessorContext()
	p.Configure(ctx, getExampleConfiguration())
	for i := 1; i <= 1000; i++ {
		em := testutils.NewPacket("", nil)
		p.Receive(em)
	}

	assert.Equal(t, 0, ctx.BuiltPacketsCount(), "unexpected event was created by the processor")
	assert.Equal(t, 0, ctx.SentPacketsCount(0), "all events should be dropped")
}
func TestReceiveDrop99p(t *testing.T) {
	p := New().(*processor)
	ctx := testutils.NewProcessorContext()
	p.Configure(ctx, getExampleConfiguration())
	p.opt.Percentage = 99
	total := 1000
	for i := 1; i <= total; i++ {
		em := testutils.NewPacket("", nil)
		p.Receive(em)
	}

	expected := total / 100 * (100 - p.opt.Percentage)
	delta := total / 100
	assert.Equal(t, 0, ctx.BuiltPacketsCount(), "unexpected event was created by the processor")
	assert.InDelta(t, expected, ctx.SentPacketsCount(0), float64(delta))
	assert.Equal(t, "myvalue", ctx.SentPackets(0)[0].Fields().ValueOrEmptyForPathString("test1"), "a new should field be added")
}
func TestReceiveDrop80p(t *testing.T) {
	p := New().(*processor)
	ctx := testutils.NewProcessorContext()
	p.Configure(ctx, getExampleConfiguration())
	p.opt.Percentage = 80
	total := 10000
	for i := 1; i <= total; i++ {
		em := testutils.NewPacket("", nil)
		p.Receive(em)
	}

	expected := total / 100 * (100 - p.opt.Percentage)
	delta := total / 100
	assert.Equal(t, 0, ctx.BuiltPacketsCount(), "unexpected event was created by the processor")
	assert.InDelta(t, expected, ctx.SentPacketsCount(0), float64(delta))
	assert.Equal(t, "myvalue", ctx.SentPackets(0)[0].Fields().ValueOrEmptyForPathString("test1"), "a new should field be added")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/haklop/bazooka/commons/matrix"

	lib "github.com/haklop/bazooka/commons"
)

const (
	SourceFolder      = "/bazooka"
	OutputFolder      = "/bazooka-output"
	MetaFolder        = "/meta"
	BazookaConfigFile = ".bazooka.yml"
	TravisConfigFile  = ".travis.yml"
	MX_ENV_PREFIX     = "env::"
)

func main() {
	// Find either .travis.yml or .bazooka.yml file in the project
	configFile, err := lib.ResolveConfigFile(SourceFolder)
	if err != nil {
		log.Fatal(err)
	}

	// parse the configuration
	config := &lib.Config{}
	err = lib.Parse(configFile, config)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Parsed configuration: %+v\n", config)

	// resolve the docker image corresponding to this particular language parser
	image, err := resolveLanguageParser(config.Language)
	if err != nil {
		log.Fatal(err)
	}

	// run the parser image
	langParser := &LanguageParser{
		Image: image,
	}
	err = langParser.Parse()
	if err != nil {
		log.Fatal(err)
	}

	// if all went well, the parser should have generated one or more "sub" .bazooka.*.yml files
	// one for each compiler version for example
	//
	// they are also supposed to enrich it with a from attribute corresponding to a base docker image
	// to be used to run the build
	files, err := lib.ListFilesWithPrefix(OutputFolder, ".bazooka")
	if err != nil {
		log.Fatal(err)
	}

	// for each of those files (the "sub" .bazooka.*.yml)
	for _, file := range files {
		// parse the damned thing
		config := &lib.Config{}
		err = lib.Parse(file, config)
		if err != nil {
			log.Fatal(fmt.Errorf("Error while parsing config file %s: %v", file, err))
		}

		// create a matrix from the environment variables
		// a matrix has N variables (dimensions) where each variable has M values
		// config.Env is a list of key=value strings
		// explodeProps transforms that into a map[string][]string
		// for example ["A=1", "A=2", B="3"]
		// when exploded gets transformed into
		// {A: [1, 2], B: [3]}
		// this matches the matrix layout, so we could store them directly into the matrix
		// but since we would like to be able to extract them later, and to avoid mixing them with a language specific variables (like jdk, go, etc.)
		// explode prefixes the env variables names with a prefix defined in the constant MX_ENV_PREFIX
		// Hence, our matrix is more like: {"env::A": [1, 2], "env::B": [3]}
		mx := matrix.Matrix(explodeProps(config.Env, MX_ENV_PREFIX))

		// extract the "*" part from the .bazooka.*.yml file
		rootCounter := parseCounter(file)
		// for every .bazooka.*.yml file, the language parser is also supposed to have generated a meta/* file
		// which is a simple yml file containing the language specific  matrix variables
		// for example, if the original .bazooka.yml file defined 2 go versions:
		//
		// go:
		// - 1.2.2
		// - 1.3.1
		//
		// the language parser should generate 2 meta files, one for each go version in this format:
		//
		// go: 1.2.2
		//
		// and
		//
		// go: 1.3.1
		rootMetaFile := fmt.Sprintf("%s/%s", MetaFolder, rootCounter)
		// since we have no idea of the generated meta file structure, we'll parse it into a map[string]interface{}
		var langExtraVars map[string]interface{}
		err := lib.Parse(rootMetaFile, &langExtraVars)
		if err != nil {
			log.Fatal(err)
		}
		// and then add the new language specific variables parsed from the meta file to the build matrix (which already contains the env variables)
		feedMatrix(langExtraVars, &mx)

		// we're not done yet: we need to also handle the matrix exclusions
		// we parse them into a list of matrices
		exclusions, err := exclusionsMatrices(config.Matrix.Exclude)
		if err != nil {
			log.Fatal(err)
		}

		// and finally, we iterate over all the permutations of the build matrix
		// these permutations are the list of all the combinations of env variables and language specific variables, minux the exclusions
		mx.IterAll(func(permutation map[string]string, counter string) {
			// we get called for every non-excluded permutations with the different variables values for this permutations and a unique permutation counter
			// handlePermutation will start from the .bazooka.*.yml file, which should already contain a single language specific permutation
			// and enrich it with the env variables combination
			// the same goes for the meta file
			if err := handlePermutation(permutation, config, counter, rootCounter); err != nil {
				log.Fatal(fmt.Errorf("Error while generating the permutations: %v", err))
			}
		}, exclusions)

		// after we're done iterating over the .bazooka.*.yml, and since we generated a new set of build files
		// we can now safely remove them
		err = os.Remove(file)
		if err != nil {
			log.Fatal(fmt.Errorf("Error while removing file %s: %v", file, err))
		}

		// same for the meta files
		err = os.Remove(fmt.Sprintf("%s/%s", MetaFolder, rootCounter))
		if err != nil {
			log.Fatal(fmt.Errorf("Error while removing meta folders: %v", err))
		}
	}

	// Now we're left with the final build files
	files, err = lib.ListFilesWithPrefix(OutputFolder, ".bazooka")
	if err != nil {
		log.Fatal(fmt.Errorf("Error while listing .bazooka* files: %v", err))
	}

	for _, file := range files {
		config := &lib.Config{}
		err = lib.Parse(file, config)
		if err != nil {
			log.Fatal(fmt.Errorf("Error while parsing config file %s: %v", file, err))
		}

		// transform the .bazooka.x.yml file into a set of dockerfile + shell scripts who perform the actual build
		g := &Generator{
			Config:       config,
			OutputFolder: OutputFolder,
			Index:        parseCounter(file),
		}
		err = g.GenerateDockerfile()
		if err != nil {
			fmt.Errorf("Error while generating a dockerfile: %v", err)
		}
	}

}

func handlePermutation(permutation map[string]string, config *lib.Config, counter, rootCounter string) error {
	//Flush file
	// start from the language-spcecific permutation
	newConfig := *config

	// and replace its env variables with this unique permutation
	envMap := extractPrefixedKeysMap(permutation, MX_ENV_PREFIX)
	newConfig.Env = lib.FlattenEnvMap(envMap)
	if err := lib.Flush(newConfig, fmt.Sprintf("%s/.bazooka.%s.yml", OutputFolder, counter)); err != nil {
		return err
	}

	// do the same for the meta file
	// start from the language specific permutation meta file
	rootMetaFile := fmt.Sprintf("%s/%s", MetaFolder, rootCounter)
	// copy it to a global (lang specific+env vars) permutation meta file
	if err := lib.CopyFile(rootMetaFile, fmt.Sprintf("%s/%s", MetaFolder, counter)); err != nil {
		return err
	}
	// and add to it this unique permutation of env variables
	var buffer bytes.Buffer
	buffer.WriteString("env:\n")
	for _, env := range lib.FlattenEnvMap(envMap) {
		buffer.WriteString(fmt.Sprintf(" - %s\n", env))
	}
	// and write it to disk
	if err := lib.AppendToFile(fmt.Sprintf("%s/%s", MetaFolder, counter), buffer.String(), 0755); err != nil {
		return err
	}

	return nil
}

func feedMatrix(extra map[string]interface{}, mx *matrix.Matrix) error {
	for k, v := range extra {
		switch k {
		case "env":
			if vs, ok := v.([]interface{}); ok {
				envVars := []string{}
				for _, envVar := range vs {
					if strEnvVar, ok := envVar.(string); ok {
						envVars = append(envVars, strEnvVar)
					} else {
						return fmt.Errorf("Invalid config: env should contain a sequence of strings: found a non string value %v:%T", envVar, envVar)

					}
				}
				mx.Merge(explodeProps(envVars, MX_ENV_PREFIX))
			} else {
				return fmt.Errorf("Invalid config: env should contain a sequence of strings: %v:%T", v, v)
			}

		default:
			if s, ok := v.(string); ok {
				mx.AddVar(k, s)
			} else {
				return fmt.Errorf("Invalid config: Unsupported variable type (%v) for the key %v", k, v)
			}
		}
	}
	return nil
}

func exclusionsMatrices(xs []map[string]interface{}) ([]*matrix.Matrix, error) {
	res := make([]*matrix.Matrix, len(xs))
	for i, x := range xs {
		mx := matrix.Matrix{}
		if err := feedMatrix(x, &mx); err != nil {
			return nil, err
		}
		res[i] = &mx
	}
	return res, nil
}

// parseCounter extract the * part from a .bazooka.*.yml file name
func parseCounter(filePath string) string {
	splits := strings.Split(filePath, "/")
	file := splits[len(splits)-1]
	return strings.Split(file, ".")[2]
}

// explodeProps starts from a list of key=valye strings and stores them into a map
// it also handles repeated values, so ["A=1", "A=2", B="3"] gets transformed into {A: [1, 2], B: [3]}
func explodeProps(props []string, keyPrefix string) map[string][]string {
	envKeyMap := make(map[string][]string)
	for _, env := range props {
		envSplit := strings.Split(env, "=")
		envKeyMap[keyPrefix+envSplit[0]] = append(envKeyMap[keyPrefix+envSplit[0]], envSplit[1])
	}
	return envKeyMap
}

// prefixMapKeys returns a new map where all keys are prefixed with prefix
func prefixMapKeys(m map[string][]string, prefix string) map[string][]string {
	res := make(map[string][]string)
	for k, v := range m {
		res[prefix+k] = v
	}
	return res
}

// extractPrefixedKeysMap returns a new map containing only the values whose keys have the specified prefix, removing the latter in the process
// Given {xA: 1, B: 2, xC: 3}, it returns {A: 1, C: 3} if given a prefix x
func extractPrefixedKeysMap(m map[string]string, prefix string) map[string]string {
	res := make(map[string]string)
	for k, v := range m {
		if strings.HasPrefix(k, prefix) {
			res[k[len(prefix):]] = v
		}
	}
	return res
}

func resolveLanguageParser(language string) (string, error) {
	parserMap := map[string]string{
		"golang": "bazooka/parser-golang",
		"go":     "bazooka/parser-golang",
		"java":   "bazooka/parser-java",
	}
	if val, ok := parserMap[language]; ok {
		return val, nil
	}
	return "", fmt.Errorf("Unable to find Bazooka Docker Image for Language Parser %s\n", language)
}
<commit_msg>manage error case for feedMatrix<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/haklop/bazooka/commons/matrix"

	lib "github.com/haklop/bazooka/commons"
)

const (
	SourceFolder      = "/bazooka"
	OutputFolder      = "/bazooka-output"
	MetaFolder        = "/meta"
	BazookaConfigFile = ".bazooka.yml"
	TravisConfigFile  = ".travis.yml"
	MX_ENV_PREFIX     = "env::"
)

func main() {
	// Find either .travis.yml or .bazooka.yml file in the project
	configFile, err := lib.ResolveConfigFile(SourceFolder)
	if err != nil {
		log.Fatal(err)
	}

	// parse the configuration
	config := &lib.Config{}
	err = lib.Parse(configFile, config)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Parsed configuration: %+v\n", config)

	// resolve the docker image corresponding to this particular language parser
	image, err := resolveLanguageParser(config.Language)
	if err != nil {
		log.Fatal(err)
	}

	// run the parser image
	langParser := &LanguageParser{
		Image: image,
	}
	err = langParser.Parse()
	if err != nil {
		log.Fatal(err)
	}

	// if all went well, the parser should have generated one or more "sub" .bazooka.*.yml files
	// one for each compiler version for example
	//
	// they are also supposed to enrich it with a from attribute corresponding to a base docker image
	// to be used to run the build
	files, err := lib.ListFilesWithPrefix(OutputFolder, ".bazooka")
	if err != nil {
		log.Fatal(err)
	}

	// for each of those files (the "sub" .bazooka.*.yml)
	for _, file := range files {
		// parse the damned thing
		config := &lib.Config{}
		err = lib.Parse(file, config)
		if err != nil {
			log.Fatal(fmt.Errorf("Error while parsing config file %s: %v", file, err))
		}

		// create a matrix from the environment variables
		// a matrix has N variables (dimensions) where each variable has M values
		// config.Env is a list of key=value strings
		// explodeProps transforms that into a map[string][]string
		// for example ["A=1", "A=2", B="3"]
		// when exploded gets transformed into
		// {A: [1, 2], B: [3]}
		// this matches the matrix layout, so we could store them directly into the matrix
		// but since we would like to be able to extract them later, and to avoid mixing them with a language specific variables (like jdk, go, etc.)
		// explode prefixes the env variables names with a prefix defined in the constant MX_ENV_PREFIX
		// Hence, our matrix is more like: {"env::A": [1, 2], "env::B": [3]}
		mx := matrix.Matrix(explodeProps(config.Env, MX_ENV_PREFIX))

		// extract the "*" part from the .bazooka.*.yml file
		rootCounter := parseCounter(file)
		// for every .bazooka.*.yml file, the language parser is also supposed to have generated a meta/* file
		// which is a simple yml file containing the language specific  matrix variables
		// for example, if the original .bazooka.yml file defined 2 go versions:
		//
		// go:
		// - 1.2.2
		// - 1.3.1
		//
		// the language parser should generate 2 meta files, one for each go version in this format:
		//
		// go: 1.2.2
		//
		// and
		//
		// go: 1.3.1
		rootMetaFile := fmt.Sprintf("%s/%s", MetaFolder, rootCounter)
		// since we have no idea of the generated meta file structure, we'll parse it into a map[string]interface{}
		var langExtraVars map[string]interface{}
		err := lib.Parse(rootMetaFile, &langExtraVars)
		if err != nil {
			log.Fatal(err)
		}
		// and then add the new language specific variables parsed from the meta file to the build matrix (which already contains the env variables)
		err = feedMatrix(langExtraVars, &mx)
		if err != nil {
			log.Fatal(err)
		}

		// we're not done yet: we need to also handle the matrix exclusions
		// we parse them into a list of matrices
		exclusions, err := exclusionsMatrices(config.Matrix.Exclude)
		if err != nil {
			log.Fatal(err)
		}

		// and finally, we iterate over all the permutations of the build matrix
		// these permutations are the list of all the combinations of env variables and language specific variables, minux the exclusions
		mx.IterAll(func(permutation map[string]string, counter string) {
			// we get called for every non-excluded permutations with the different variables values for this permutations and a unique permutation counter
			// handlePermutation will start from the .bazooka.*.yml file, which should already contain a single language specific permutation
			// and enrich it with the env variables combination
			// the same goes for the meta file
			if err := handlePermutation(permutation, config, counter, rootCounter); err != nil {
				log.Fatal(fmt.Errorf("Error while generating the permutations: %v", err))
			}
		}, exclusions)

		// after we're done iterating over the .bazooka.*.yml, and since we generated a new set of build files
		// we can now safely remove them
		err = os.Remove(file)
		if err != nil {
			log.Fatal(fmt.Errorf("Error while removing file %s: %v", file, err))
		}

		// same for the meta files
		err = os.Remove(fmt.Sprintf("%s/%s", MetaFolder, rootCounter))
		if err != nil {
			log.Fatal(fmt.Errorf("Error while removing meta folders: %v", err))
		}
	}

	// Now we're left with the final build files
	files, err = lib.ListFilesWithPrefix(OutputFolder, ".bazooka")
	if err != nil {
		log.Fatal(fmt.Errorf("Error while listing .bazooka* files: %v", err))
	}

	for _, file := range files {
		config := &lib.Config{}
		err = lib.Parse(file, config)
		if err != nil {
			log.Fatal(fmt.Errorf("Error while parsing config file %s: %v", file, err))
		}

		// transform the .bazooka.x.yml file into a set of dockerfile + shell scripts who perform the actual build
		g := &Generator{
			Config:       config,
			OutputFolder: OutputFolder,
			Index:        parseCounter(file),
		}
		err = g.GenerateDockerfile()
		if err != nil {
			fmt.Errorf("Error while generating a dockerfile: %v", err)
		}
	}

}

func handlePermutation(permutation map[string]string, config *lib.Config, counter, rootCounter string) error {
	//Flush file
	// start from the language-spcecific permutation
	newConfig := *config

	// and replace its env variables with this unique permutation
	envMap := extractPrefixedKeysMap(permutation, MX_ENV_PREFIX)
	newConfig.Env = lib.FlattenEnvMap(envMap)
	if err := lib.Flush(newConfig, fmt.Sprintf("%s/.bazooka.%s.yml", OutputFolder, counter)); err != nil {
		return err
	}

	// do the same for the meta file
	// start from the language specific permutation meta file
	rootMetaFile := fmt.Sprintf("%s/%s", MetaFolder, rootCounter)
	// copy it to a global (lang specific+env vars) permutation meta file
	if err := lib.CopyFile(rootMetaFile, fmt.Sprintf("%s/%s", MetaFolder, counter)); err != nil {
		return err
	}
	// and add to it this unique permutation of env variables
	var buffer bytes.Buffer
	buffer.WriteString("env:\n")
	for _, env := range lib.FlattenEnvMap(envMap) {
		buffer.WriteString(fmt.Sprintf(" - %s\n", env))
	}
	// and write it to disk
	if err := lib.AppendToFile(fmt.Sprintf("%s/%s", MetaFolder, counter), buffer.String(), 0755); err != nil {
		return err
	}

	return nil
}

func feedMatrix(extra map[string]interface{}, mx *matrix.Matrix) error {
	for k, v := range extra {
		switch k {
		case "env":
			if vs, ok := v.([]interface{}); ok {
				envVars := []string{}
				for _, envVar := range vs {
					if strEnvVar, ok := envVar.(string); ok {
						envVars = append(envVars, strEnvVar)
					} else {
						return fmt.Errorf("Invalid config: env should contain a sequence of strings: found a non string value %v:%T", envVar, envVar)

					}
				}
				mx.Merge(explodeProps(envVars, MX_ENV_PREFIX))
			} else {
				return fmt.Errorf("Invalid config: env should contain a sequence of strings: %v:%T", v, v)
			}

		default:
			if s, ok := v.(string); ok {
				mx.AddVar(k, s)
			} else {
				return fmt.Errorf("Invalid config: Unsupported variable type (%v) for the key %v", k, v)
			}
		}
	}
	return nil
}

func exclusionsMatrices(xs []map[string]interface{}) ([]*matrix.Matrix, error) {
	res := make([]*matrix.Matrix, len(xs))
	for i, x := range xs {
		mx := matrix.Matrix{}
		if err := feedMatrix(x, &mx); err != nil {
			return nil, err
		}
		res[i] = &mx
	}
	return res, nil
}

// parseCounter extract the * part from a .bazooka.*.yml file name
func parseCounter(filePath string) string {
	splits := strings.Split(filePath, "/")
	file := splits[len(splits)-1]
	return strings.Split(file, ".")[2]
}

// explodeProps starts from a list of key=valye strings and stores them into a map
// it also handles repeated values, so ["A=1", "A=2", B="3"] gets transformed into {A: [1, 2], B: [3]}
func explodeProps(props []string, keyPrefix string) map[string][]string {
	envKeyMap := make(map[string][]string)
	for _, env := range props {
		envSplit := strings.Split(env, "=")
		envKeyMap[keyPrefix+envSplit[0]] = append(envKeyMap[keyPrefix+envSplit[0]], envSplit[1])
	}
	return envKeyMap
}

// prefixMapKeys returns a new map where all keys are prefixed with prefix
func prefixMapKeys(m map[string][]string, prefix string) map[string][]string {
	res := make(map[string][]string)
	for k, v := range m {
		res[prefix+k] = v
	}
	return res
}

// extractPrefixedKeysMap returns a new map containing only the values whose keys have the specified prefix, removing the latter in the process
// Given {xA: 1, B: 2, xC: 3}, it returns {A: 1, C: 3} if given a prefix x
func extractPrefixedKeysMap(m map[string]string, prefix string) map[string]string {
	res := make(map[string]string)
	for k, v := range m {
		if strings.HasPrefix(k, prefix) {
			res[k[len(prefix):]] = v
		}
	}
	return res
}

func resolveLanguageParser(language string) (string, error) {
	parserMap := map[string]string{
		"golang": "bazooka/parser-golang",
		"go":     "bazooka/parser-golang",
		"java":   "bazooka/parser-java",
	}
	if val, ok := parserMap[language]; ok {
		return val, nil
	}
	return "", fmt.Errorf("Unable to find Bazooka Docker Image for Language Parser %s\n", language)
}
<|endoftext|>


<commit_before>package mandira

import (
	"testing"
)

func TestTokenizer(t *testing.T) {
	type MS map[string][]string
	tests := []MS{
		MS{"bare": []string{"bare"}},
		MS{" bare": []string{"bare"}},
		MS{"bare ": []string{"bare"}},
		MS{" bare ": []string{"bare"}},
		MS{">bare": []string{">", "bare"}},
		MS{"<bare": []string{"<", "bare"}},
		MS{">=bare": []string{">=", "bare"}},
		MS{"<=bare": []string{"<=", "bare"}},
		MS{"!=bare": []string{"!=", "bare"}},
		MS{"==bare": []string{"==", "bare"}},
		MS{"bare >= bare": []string{"bare", ">=", "bare"}},
		MS{"bare>=bare": []string{"bare", ">=", "bare"}},
		MS{"bare>= bare": []string{"bare", ">=", "bare"}},
		MS{"bare >=bare": []string{"bare", ">=", "bare"}},
		MS{"i>a": []string{"i", ">", "a"}},
		MS{`bare|func("foo", bar, 1.5) >= 9`: []string{"bare", "|", "func", "(", `"foo"`, ",", "bar", ",", "1.5", ")", ">=", "9"}},
		MS{`b|func("foo bar, 今日は世界")`: []string{"b", "|", "func", "(", `"foo bar, 今日は世界"`, ")"}},
	}
	errs := []string{
		"a = b", // single = is an invalid token
		"!a",    // single ! is an invalid token
	}

	for _, test := range tests {
		for k, v := range test {
			tokens, err := tokenize(k)
			if err != nil {
				t.Errorf("Got an error tokenizing: %v\n", err)
				continue
			}
			if len(tokens) != len(v) {
				t.Errorf("Wrong number of tokens: %v vs %v in \"%v\"\n", tokens, v, k)
				continue
			}
			for i, tok := range tokens {
				if tok != v[i] {
					t.Errorf("Expected %v, got %v in \"%v\"\n", v[i], tok, k)
				}
			}
		}
	}
	for _, e := range errs {
		_, err := tokenize(e)
		if err == nil {
			t.Errorf("Expected tokenizer error on \"%v\"\n", e)
		}
	}
}

func tErr(t *testing.T, err error) {
	if err != nil {
		t.Errorf("Unexpected error: %v\n", err)
	}
}

// shortcut for a new token list
func ntl(s string) *tokenList {
	tl, _ = tokenize(s)
	return &tokenList{tl, 0, 0}
}

func TestCondParser(t *testing.T) {
	expr, err := parseCondExpression(ntl("foo"))
	tErr(err)
	expr, err = parseCondExpression(ntl("not foo"))
	tErr(err)
	expr, err = parseCondExpression(ntl("foo > bar"))
	tErr(err)
	expr, err = parseCondExpression(ntl("foo != bar"))
	tErr(err)
	expr, err = parseCondExpression(ntl("foo or bar"))
	tErr(err)
	expr, err = parseCondExpression(ntl("not foo or not bar"))
	tErr(err)
}

func TestVarParser(t *testing.T) {
	// naked test
	expr, err := parseVarExpression(&tokenList{[]string{"hello"}, 0, 0})
	tErr(t, err)
	if len(expr.exprs) != 1 {
		t.Errorf("Expected a single lookup expression, got %v\n", expr.exprs)
	}
	lu, ok := expr.exprs[0].(*lookupExpr)
	if !ok {
		t.Errorf("Expected a lookup expression, got %v\n", expr.exprs)
	}
	if lu.name != "hello" {
		t.Errorf("Expected varname to be \"hello\", got %s\n", lu.name)
	}

	// naked with filter
	expr, err = parseVarExpression(&tokenList{[]string{"hello", "|", "upper"}, 0, 0})
	tErr(t, err)
	if len(expr.exprs) != 2 {
		t.Fatalf("Expected 2 expressions, got %v\n", len(expr.exprs))
	}
	lu, ok = expr.exprs[0].(*lookupExpr)
	if !ok {
		t.Errorf("Expected a lookup expression, got %v\n", expr.exprs)
	}
	if lu.name != "hello" {
		t.Errorf("Expected varname to be \"hello\", got %s\n", lu.name)
	}
	fu, ok := expr.exprs[1].(*funcExpr)
	if !ok {
		t.Errorf("Expected a func expression, got %v\n", expr.exprs)
	}
	if fu.name != "upper" {
		t.Errorf("Expected funcname to be \"upper\", got %s\n", fu.name)
	}
	if len(fu.arguments) != 0 {
		t.Errorf("Got unexpected arguments (%v)\n", fu.arguments)
	}

	// filter chain with arguments
	toks, _ := tokenize(`hello|upper|join(", ", 3.5, someVar)|fake("hi")`)
	if len(toks) != 17 {
		t.Fatalf("Unexpected tokenization results for test %v\n", toks)
	}
	expr, err = parseVarExpression(&tokenList{toks, 0, 0})

	if len(expr.exprs) != 4 {
		t.Fatalf("Expected 4 expressions, got %d (%v)\n", len(expr.exprs), expr.exprs)
	}
	lu, ok = expr.exprs[0].(*lookupExpr)
	if !ok {
		t.Errorf("Expected a lookup expression, got %v\n", expr.exprs)
	}
	if lu.name != "hello" {
		t.Errorf("Expected varname to be \"hello\", got %s\n", lu.name)
	}
	fu, ok = expr.exprs[1].(*funcExpr)
	if !ok {
		t.Errorf("Expected a func expression, got %v\n", expr.exprs)
	}
	if fu.name != "upper" {
		t.Errorf("Expected funcname to be \"upper\", got %s\n", fu.name)
	}
	if len(fu.arguments) != 0 {
		t.Errorf("Got unexpected arguments (%v)\n", fu.arguments)
	}
	fu, ok = expr.exprs[2].(*funcExpr)
	if !ok {
		t.Errorf("Expected a func expression, got %v\n", expr.exprs)
	}
	if fu.name != "join" {
		t.Errorf("Expected funcname to be \"join\", got %s\n", fu.name)
	}
	if len(fu.arguments) != 3 {
		t.Errorf("Got unexpected number of arguments, expected 3 (%v)\n", fu.arguments)
	}
	s, ok := fu.arguments[0].(string)
	if !ok {
		t.Errorf("Expecting string as first arg\n")
	}
	if s != ", " {
		t.Errorf(`Expecting ", ", got %s`+"\n", s)
	}
	f, ok := fu.arguments[1].(float64)
	if !ok {
		t.Errorf("Expecting float as second arg\n")
	}
	if f != 3.5 {
		t.Errorf("Expecting 3.5, got %v\n", f)
	}
	lu, ok = fu.arguments[2].(*lookupExpr)
	if !ok {
		t.Errorf("Expecting lookupExpr as third arg\n")
	}
	if lu.name != "someVar" {
		t.Errorf("Expecting name \"someVar\", got %s\n", lu.name)
	}
	fu, ok = expr.exprs[3].(*funcExpr)
	if !ok {
		t.Errorf("Expected a func expression, got %v\n", expr.exprs)
	}
	if fu.name != "fake" {
		t.Errorf("Expected funcname to be \"fake\", got %s\n", fu.name)
	}
	if len(fu.arguments) != 1 {
		t.Errorf("Got unexpected number of arguments, expected 3 (%v)\n", fu.arguments)
	}
	s, ok = fu.arguments[0].(string)
	if !ok {
		t.Errorf("Expecting string as first arg\n")
	}
	if s != "hi" {
		t.Errorf(`Expecting "hi", got %s`+"\n", s)
	}

}
<commit_msg>gr another mistake commit, add some rudimentary 'dont explode' tests for parsing conditionals<commit_after>package mandira

import (
	"fmt"
	"testing"
)

func TestTokenizer(t *testing.T) {
	type MS map[string][]string
	tests := []MS{
		MS{"bare": []string{"bare"}},
		MS{" bare": []string{"bare"}},
		MS{"bare ": []string{"bare"}},
		MS{" bare ": []string{"bare"}},
		MS{">bare": []string{">", "bare"}},
		MS{"<bare": []string{"<", "bare"}},
		MS{">=bare": []string{">=", "bare"}},
		MS{"<=bare": []string{"<=", "bare"}},
		MS{"!=bare": []string{"!=", "bare"}},
		MS{"==bare": []string{"==", "bare"}},
		MS{"bare >= bare": []string{"bare", ">=", "bare"}},
		MS{"bare>=bare": []string{"bare", ">=", "bare"}},
		MS{"bare>= bare": []string{"bare", ">=", "bare"}},
		MS{"bare >=bare": []string{"bare", ">=", "bare"}},
		MS{"i>a": []string{"i", ">", "a"}},
		MS{`bare|func("foo", bar, 1.5) >= 9`: []string{"bare", "|", "func", "(", `"foo"`, ",", "bar", ",", "1.5", ")", ">=", "9"}},
		MS{`b|func("foo bar, 今日は世界")`: []string{"b", "|", "func", "(", `"foo bar, 今日は世界"`, ")"}},
	}
	errs := []string{
		"a = b", // single = is an invalid token
		"!a",    // single ! is an invalid token
	}

	for _, test := range tests {
		for k, v := range test {
			tokens, err := tokenize(k)
			if err != nil {
				t.Errorf("Got an error tokenizing: %v\n", err)
				continue
			}
			if len(tokens) != len(v) {
				t.Errorf("Wrong number of tokens: %v vs %v in \"%v\"\n", tokens, v, k)
				continue
			}
			for i, tok := range tokens {
				if tok != v[i] {
					t.Errorf("Expected %v, got %v in \"%v\"\n", v[i], tok, k)
				}
			}
		}
	}
	for _, e := range errs {
		_, err := tokenize(e)
		if err == nil {
			t.Errorf("Expected tokenizer error on \"%v\"\n", e)
		}
	}
}

func tErr(t *testing.T, err error) {
	if err != nil {
		t.Errorf("Unexpected error: %v\n", err)
	}
}

// shortcut for a new token list
func ntl(s string) *tokenList {
	tl, _ := tokenize(s)
	return &tokenList{tl, 0, 0}
}

func TestCondParser(t *testing.T) {
	expr, err := parseCondExpression(ntl("foo"))
	tErr(t, err)
	expr, err = parseCondExpression(ntl("not foo"))
	tErr(t, err)
	expr, err = parseCondExpression(ntl("foo > bar"))
	tErr(t, err)
	expr, err = parseCondExpression(ntl("foo != bar"))
	tErr(t, err)
	expr, err = parseCondExpression(ntl("foo or bar"))
	tErr(t, err)
	expr, err = parseCondExpression(ntl("not foo or not bar"))
	tErr(t, err)
	fmt.Println(expr)
}

func TestVarParser(t *testing.T) {
	// naked test
	expr, err := parseVarExpression(&tokenList{[]string{"hello"}, 0, 0})
	tErr(t, err)
	if len(expr.exprs) != 1 {
		t.Errorf("Expected a single lookup expression, got %v\n", expr.exprs)
	}
	lu, ok := expr.exprs[0].(*lookupExpr)
	if !ok {
		t.Errorf("Expected a lookup expression, got %v\n", expr.exprs)
	}
	if lu.name != "hello" {
		t.Errorf("Expected varname to be \"hello\", got %s\n", lu.name)
	}

	// naked with filter
	expr, err = parseVarExpression(&tokenList{[]string{"hello", "|", "upper"}, 0, 0})
	tErr(t, err)
	if len(expr.exprs) != 2 {
		t.Fatalf("Expected 2 expressions, got %v\n", len(expr.exprs))
	}
	lu, ok = expr.exprs[0].(*lookupExpr)
	if !ok {
		t.Errorf("Expected a lookup expression, got %v\n", expr.exprs)
	}
	if lu.name != "hello" {
		t.Errorf("Expected varname to be \"hello\", got %s\n", lu.name)
	}
	fu, ok := expr.exprs[1].(*funcExpr)
	if !ok {
		t.Errorf("Expected a func expression, got %v\n", expr.exprs)
	}
	if fu.name != "upper" {
		t.Errorf("Expected funcname to be \"upper\", got %s\n", fu.name)
	}
	if len(fu.arguments) != 0 {
		t.Errorf("Got unexpected arguments (%v)\n", fu.arguments)
	}

	// filter chain with arguments
	toks, _ := tokenize(`hello|upper|join(", ", 3.5, someVar)|fake("hi")`)
	if len(toks) != 17 {
		t.Fatalf("Unexpected tokenization results for test %v\n", toks)
	}
	expr, err = parseVarExpression(&tokenList{toks, 0, 0})

	if len(expr.exprs) != 4 {
		t.Fatalf("Expected 4 expressions, got %d (%v)\n", len(expr.exprs), expr.exprs)
	}
	lu, ok = expr.exprs[0].(*lookupExpr)
	if !ok {
		t.Errorf("Expected a lookup expression, got %v\n", expr.exprs)
	}
	if lu.name != "hello" {
		t.Errorf("Expected varname to be \"hello\", got %s\n", lu.name)
	}
	fu, ok = expr.exprs[1].(*funcExpr)
	if !ok {
		t.Errorf("Expected a func expression, got %v\n", expr.exprs)
	}
	if fu.name != "upper" {
		t.Errorf("Expected funcname to be \"upper\", got %s\n", fu.name)
	}
	if len(fu.arguments) != 0 {
		t.Errorf("Got unexpected arguments (%v)\n", fu.arguments)
	}
	fu, ok = expr.exprs[2].(*funcExpr)
	if !ok {
		t.Errorf("Expected a func expression, got %v\n", expr.exprs)
	}
	if fu.name != "join" {
		t.Errorf("Expected funcname to be \"join\", got %s\n", fu.name)
	}
	if len(fu.arguments) != 3 {
		t.Errorf("Got unexpected number of arguments, expected 3 (%v)\n", fu.arguments)
	}
	s, ok := fu.arguments[0].(string)
	if !ok {
		t.Errorf("Expecting string as first arg\n")
	}
	if s != ", " {
		t.Errorf(`Expecting ", ", got %s`+"\n", s)
	}
	f, ok := fu.arguments[1].(float64)
	if !ok {
		t.Errorf("Expecting float as second arg\n")
	}
	if f != 3.5 {
		t.Errorf("Expecting 3.5, got %v\n", f)
	}
	lu, ok = fu.arguments[2].(*lookupExpr)
	if !ok {
		t.Errorf("Expecting lookupExpr as third arg\n")
	}
	if lu.name != "someVar" {
		t.Errorf("Expecting name \"someVar\", got %s\n", lu.name)
	}
	fu, ok = expr.exprs[3].(*funcExpr)
	if !ok {
		t.Errorf("Expected a func expression, got %v\n", expr.exprs)
	}
	if fu.name != "fake" {
		t.Errorf("Expected funcname to be \"fake\", got %s\n", fu.name)
	}
	if len(fu.arguments) != 1 {
		t.Errorf("Got unexpected number of arguments, expected 3 (%v)\n", fu.arguments)
	}
	s, ok = fu.arguments[0].(string)
	if !ok {
		t.Errorf("Expecting string as first arg\n")
	}
	if s != "hi" {
		t.Errorf(`Expecting "hi", got %s`+"\n", s)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"github.com/sperre/astar"
)

func FindPath(z *Zone, startX, startY, endX, endY uint8, allowDiagonal bool) [][2]uint8 {
	spaceBlocked := false
	mapData := make(astar.MapData, 256)
	mapData_ := make([]int, 256*256)
	for i := range mapData {
		mapData[i] = mapData_[:256]
		mapData_ = mapData_[256:]
	}
	for x := range mapData {
		for y := range mapData[x] {
			if z.Blocked(uint8(x), uint8(y)) {
				if uint8(x) == endX && uint8(y) == endY {
					spaceBlocked = true
					mapData[x][y] = astar.LAND
				} else {
					mapData[x][y] = astar.WALL
				}
			} else {
				mapData[x][y] = astar.LAND
			}
		}
	}
	nodes := astar.Astar(mapData, int(startX), int(startY), int(endX), int(endY), allowDiagonal)
	path := make([][2]uint8, len(nodes))
	for i, n := range nodes {
		path[i][0] = uint8(n.X)
		path[i][1] = uint8(n.Y)
	}
	if spaceBlocked && len(path) > 0 {
		path = path[:len(path)-1]
	}
	return path
}
<commit_msg>allow pathfinding when the player is on a blocked space<commit_after>package main

import (
	"github.com/sperre/astar"
)

func FindPath(z *Zone, startX, startY, endX, endY uint8, allowDiagonal bool) [][2]uint8 {
	spaceBlocked := false
	mapData := make(astar.MapData, 256)
	mapData_ := make([]int, 256*256)
	for i := range mapData {
		mapData[i] = mapData_[:256]
		mapData_ = mapData_[256:]
	}
	for x := range mapData {
		for y := range mapData[x] {
			if z.Blocked(uint8(x), uint8(y)) {
				if uint8(x) == endX && uint8(y) == endY {
					spaceBlocked = true
					mapData[x][y] = astar.LAND
				} else if uint8(x) == startX && uint8(y) == startY {
					mapData[x][y] = astar.LAND
				} else {
					mapData[x][y] = astar.WALL
				}
			} else {
				mapData[x][y] = astar.LAND
			}
		}
	}
	nodes := astar.Astar(mapData, int(startX), int(startY), int(endX), int(endY), allowDiagonal)
	path := make([][2]uint8, len(nodes))
	for i, n := range nodes {
		path[i][0] = uint8(n.X)
		path[i][1] = uint8(n.Y)
	}
	if len(path) > 1 && path[0][0] == startX && path[0][1] == startY {
		path = path[1:]
	}
	if spaceBlocked && len(path) > 0 {
		path = path[:len(path)-1]
	}
	return path
}
<|endoftext|>


<commit_before>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.

package parse

import (
	"code.google.com/p/go.text/unicode/norm"
	"fmt"
	"github.com/demizer/go-elog"
	"github.com/demizer/go-spew/spew"
	"reflect"
)

var spd = spew.ConfigState{Indent: "\t", DisableMethods: true}

type systemMessageLevel int

const (
	levelInfo systemMessageLevel = iota
	levelWarning
	levelError
	levelSevere
)

var systemMessageLevels = [...]string{
	"INFO",
	"WARNING",
	"ERROR",
	"SEVERE",
}

func (s systemMessageLevel) String() string {
	return systemMessageLevels[s]
}

type parserMessage int

const (
	warningShortUnderline parserMessage = iota
	errorUnexpectedSectionTitle
	errorUnexpectedSectionTitleOrTransition
)

var parserErrors = [...]string{
	"warningShortUnderline",
	"errorUnexpectedSectionTitle",
	"errorUnexpectedSectionTitleOrTransition",
}

func (p parserMessage) String() string {
	return parserErrors[p]
}

func (p parserMessage) Message() (s string) {
	switch p {
	case warningShortUnderline:
		s = "Title underline too short."
	case errorUnexpectedSectionTitle:
		s = "Unexpected section title."
	case errorUnexpectedSectionTitleOrTransition:
		s = "Unexpected section title or transition."
	}
	return
}

func (p parserMessage) Level() (s systemMessageLevel) {
	switch p {
	case warningShortUnderline:
		s = levelWarning
	case errorUnexpectedSectionTitle:
		s = levelSevere
	case errorUnexpectedSectionTitleOrTransition:
		s = levelSevere
	}
	return
}

type sectionLevels []*SectionNode

func (s *sectionLevels) String() string {
	var out string
	for _, sec := range *s {
		out += fmt.Sprintf("level: %d, rune: %q, overline: %t, length: %d\n",
			sec.Level, sec.UnderLine.Rune, sec.OverLine != nil, sec.Length)
	}
	return out
}

// Returns nil if not found
func (s *sectionLevels) FindByRune(adornChar rune) *SectionNode {
	for _, sec := range *s {
		if sec.UnderLine.Rune == adornChar {
			return sec
		}
	}
	return nil
}

// If exists == true, a section node with the same text and underline has been found in
// sectionLevels, sec is the matching SectionNode. If exists == false, then the sec return value is
// the similarly leveled SectionNode. If exists == false and sec == nil, then the SectionNode added
// to sectionLevels is a new Node.
func (s *sectionLevels) Add(section *SectionNode) (exists bool, sec *SectionNode) {
	sec = s.FindByRune(section.UnderLine.Rune)
	if sec != nil {
		if sec.Text == section.Text {
			return true, sec
		} else if sec.Text != section.Text {
			section.Level = sec.Level
		}
	} else {
		section.Level = len(*s) + 1
	}
	exists = false
	*s = append(*s, section)
	return
}

func (s *sectionLevels) Level() int {
	return len(*s)
}

// Parse is the entry point for the reStructuredText parser.
func Parse(name, text string) (t *Tree, errors []error) {
	t = New(name)
	if !norm.NFC.IsNormalString(text) {
		text = norm.NFC.String(text)
	}
	t.text = text
	_, errors = t.Parse(text, t)
	return
}

func New(name string) *Tree {
	return &Tree{
		Name:          name,
		Nodes:         newList(),
		nodeTarget:    newList(),
		sectionLevels: new(sectionLevels),
		indentWidth:   indentWidth,
	}
}

const (
	zed         = 3
	indentWidth = 4 // Default indent width
)

type Tree struct {
	Name             string
	Nodes            *NodeList // The root node list
	nodeTarget       *NodeList // Used by the parser to add nodes to a target NodeList
	Errors           []error
	text             string
	lex              *lexer
	tokenBackupCount int
	peekCount        int
	token            [7]*item
	sectionLevels    *sectionLevels // Encountered section levels
	id               int            // The unique id of the node in the tree
	indentWidth      int
	indentLevel      int
}

// startParse initializes the parser, using the lexer.
func (t *Tree) startParse(lex *lexer) {
	t.lex = lex
}

func (t *Tree) Parse(text string, treeSet *Tree) (tree *Tree, errors []error) {
	log.Debugln("Start")
	t.startParse(lex(t.Name, text))
	t.text = text
	t.parse(treeSet)
	log.Debugln("End")
	return t, t.Errors
}

func (t *Tree) parse(tree *Tree) {
	log.Debugln("Start")

	t.nodeTarget = t.Nodes

	for t.peek(1).Type != itemEOF {
		var n Node

		token := t.next()
		log.Infof("\nParser got token: %#+v\n\n", token)

		switch token.Type {
		case itemSectionAdornment:
			n = t.section(token)
		case itemParagraph:
			n = newParagraph(token, &t.id)
		case itemSpace:
			n = t.indent(token)
			if n == nil {
				continue
			}
		case itemEOF:
			goto exit
		case itemTitle, itemBlankLine:
			// itemTitle is consumed when evaluating itemSectionAdornment
			continue
		}

		t.nodeTarget.append(n)
		switch n.NodeType() {
		case NodeSection, NodeBlockQuote:
			// Set the loop to append items to the NodeList of the new section
			t.nodeTarget = reflect.ValueOf(n).Elem().FieldByName("NodeList").Addr().Interface().(*NodeList)
		}
	}

exit:
	log.Debugln("End")
}

func (t *Tree) peekBack(pos int) *item {
	return t.token[zed-pos]
}

func (t *Tree) peek(pos int) *item {
	// log.Debugln("\n", "Pos:", pos)
	// log.Debugf("##### peek() before #####\n")
	// spd.Dump(t.token)
	nItem := t.token[zed]
	for i := 1; i <= pos; i++ {
		if t.token[zed+i] != nil {
			nItem = t.token[zed+i]
			log.Debugf("Using %#+v\n", nItem)
			continue
		} else {
			log.Debugln("Getting next item")
			t.token[zed+i] = t.lex.nextItem()
			nItem = t.token[zed+i]
		}
	}
	// log.Debugf("\n##### peek() aftermath #####\n")
	// spd.Dump(t.token)
	// log.Debugf("Returning: %#+v\n", nItem)
	return nItem
}

func (t *Tree) peekSkip(iSkip itemElement) *item {
	var nItem *item
	var count int = 1
	for {
		nItem = t.peek(count)
		if nItem.Type != iSkip {
			 break
		}
		count++
	}
	return nItem
}

func (t *Tree) next() *item {
	// log.Debugln("t.peekCount:", t.peekCount)
	// skip shifts the pointers left in t.token, pos is the amount to shift
	skip := func(num int) {
		for i := num; i > 0; i-- {
			for x := 0; x < len(t.token)-1; x++ {
				t.token[x] = t.token[x+1]
				t.token[x+1] = nil
			}
		}
	}
	if t.peekCount > 0 {
		skip(t.peekCount)
	} else {
		skip(1)
		t.token[zed] = t.lex.nextItem()
	}
	t.tokenBackupCount, t.peekCount = 0, 0
	// log.Debugf("\n##### next() aftermath #####\n\n")
	// spd.Dump(t.token)
	return t.token[zed]
}

func (t *Tree) section(i *item) Node {
	log.Debugln("Start")
	var overAdorn, title, underAdorn *item
	var sysMessage Node

	peekForward := t.peekSkip(1, itemSpace)
	if peekForward != nil && peekForward.Type == itemTitle {
		log.Debugln("FOUND SECTION WITH OVERLINE")
		overAdorn = i
		t.next()
	loop:
		for {
			switch tTok := t.token[zed]; tTok.Type {
			case itemTitle:
				title = tTok
				t.next()
				cur := t.token[zed]
				if cur != nil && cur.Type == itemSectionAdornment {
					continue
				}
			case itemSectionAdornment:
				underAdorn = tTok
				break loop
			}
		}
	} else {
		if peekBack := t.peekBack(1); peekBack != nil && peekBack.Type == itemSpace {
			// Looking back past the white space
			if t.peekBack(2).Type == itemTitle {
				return t.systemMessage(errorUnexpectedSectionTitle)
			}
			return t.systemMessage(errorUnexpectedSectionTitleOrTransition)
		}
		title = t.peekBack(1)
		underAdorn = i
	}

	sec := newSection(title, overAdorn, underAdorn, indent, &t.id)
	exists, eSec := t.sectionLevels.Add(sec)
	if !exists && eSec != nil {
		// There is a matching level in sectionLevels
		t.nodeTarget = &(*t.sectionLevels)[sec.Level-2].NodeList
	}

	// System messages have to be applied after the section is created in order to preserve
	// a consecutive id number.
	if title.Length != underAdorn.Length {
		sysMessage = t.systemMessage(warningShortUnderline)
		sec.NodeList = append(sec.NodeList, sysMessage)
	}

	log.Debugln("End")
	return sec
}

func (t *Tree) systemMessage(err parserMessage) Node {
	var lbText string
	var lbTextLen int
	var backToken int

	s := newSystemMessage(&item{
		Type: itemSystemMessage,
		Line: t.token[zed].Line,
	},
		err.Level(), &t.id)

	msg := newParagraph(&item{
		Text:   err.Message(),
		Length: len(err.Message()),
	}, &t.id)

	log.Debugln("FOUND", err)

	switch err {
	case warningShortUnderline, errorUnexpectedSectionTitle:
		backToken = zed - 1
		if t.peekBack(1).Type == itemSpace {
			backToken = zed - 2
		}
		lbText = t.token[backToken].Text.(string) + "\n" + t.token[zed].Text.(string)
		lbTextLen = len(lbText) + 1
	case errorUnexpectedSectionTitleOrTransition:
		lbText = t.token[zed].Text.(string)
		lbTextLen = len(lbText)
	}

	lb := newLiteralBlock(&item{
		Type:   itemLiteralBlock,
		Text:   lbText,
		Length: lbTextLen, // Add one to account for the backslash
	}, &t.id)

	s.NodeList = append(s.NodeList, msg, lb)
	return s
}

func (t *Tree) indent(i *item) Node {
	level := i.Length / t.indentWidth
	if t.peekBack(1).Type == itemBlankLine {
		if t.indentLevel == level {
			// Append to the current blockquote NodeList
			return nil
		}
		t.indentLevel = level
		return newBlockQuote(&item{Type: itemBlockquote, Line: i.Line}, level, &t.id)
	}
	return nil
}
<commit_msg>parse.go: Remove peekCount from Tree.next()<commit_after>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.

package parse

import (
	"code.google.com/p/go.text/unicode/norm"
	"fmt"
	"github.com/demizer/go-elog"
	"github.com/demizer/go-spew/spew"
	"reflect"
)

var spd = spew.ConfigState{Indent: "\t", DisableMethods: true}

type systemMessageLevel int

const (
	levelInfo systemMessageLevel = iota
	levelWarning
	levelError
	levelSevere
)

var systemMessageLevels = [...]string{
	"INFO",
	"WARNING",
	"ERROR",
	"SEVERE",
}

func (s systemMessageLevel) String() string {
	return systemMessageLevels[s]
}

type parserMessage int

const (
	warningShortUnderline parserMessage = iota
	errorUnexpectedSectionTitle
	errorUnexpectedSectionTitleOrTransition
)

var parserErrors = [...]string{
	"warningShortUnderline",
	"errorUnexpectedSectionTitle",
	"errorUnexpectedSectionTitleOrTransition",
}

func (p parserMessage) String() string {
	return parserErrors[p]
}

func (p parserMessage) Message() (s string) {
	switch p {
	case warningShortUnderline:
		s = "Title underline too short."
	case errorUnexpectedSectionTitle:
		s = "Unexpected section title."
	case errorUnexpectedSectionTitleOrTransition:
		s = "Unexpected section title or transition."
	}
	return
}

func (p parserMessage) Level() (s systemMessageLevel) {
	switch p {
	case warningShortUnderline:
		s = levelWarning
	case errorUnexpectedSectionTitle:
		s = levelSevere
	case errorUnexpectedSectionTitleOrTransition:
		s = levelSevere
	}
	return
}

type sectionLevels []*SectionNode

func (s *sectionLevels) String() string {
	var out string
	for _, sec := range *s {
		out += fmt.Sprintf("level: %d, rune: %q, overline: %t, length: %d\n",
			sec.Level, sec.UnderLine.Rune, sec.OverLine != nil, sec.Length)
	}
	return out
}

// Returns nil if not found
func (s *sectionLevels) FindByRune(adornChar rune) *SectionNode {
	for _, sec := range *s {
		if sec.UnderLine.Rune == adornChar {
			return sec
		}
	}
	return nil
}

// If exists == true, a section node with the same text and underline has been found in
// sectionLevels, sec is the matching SectionNode. If exists == false, then the sec return value is
// the similarly leveled SectionNode. If exists == false and sec == nil, then the SectionNode added
// to sectionLevels is a new Node.
func (s *sectionLevels) Add(section *SectionNode) (exists bool, sec *SectionNode) {
	sec = s.FindByRune(section.UnderLine.Rune)
	if sec != nil {
		if sec.Text == section.Text {
			return true, sec
		} else if sec.Text != section.Text {
			section.Level = sec.Level
		}
	} else {
		section.Level = len(*s) + 1
	}
	exists = false
	*s = append(*s, section)
	return
}

func (s *sectionLevels) Level() int {
	return len(*s)
}

// Parse is the entry point for the reStructuredText parser.
func Parse(name, text string) (t *Tree, errors []error) {
	t = New(name)
	if !norm.NFC.IsNormalString(text) {
		text = norm.NFC.String(text)
	}
	t.text = text
	_, errors = t.Parse(text, t)
	return
}

func New(name string) *Tree {
	return &Tree{
		Name:          name,
		Nodes:         newList(),
		nodeTarget:    newList(),
		sectionLevels: new(sectionLevels),
		indentWidth:   indentWidth,
	}
}

const (
	zed         = 3
	indentWidth = 4 // Default indent width
)

type Tree struct {
	Name             string
	Nodes            *NodeList // The root node list
	nodeTarget       *NodeList // Used by the parser to add nodes to a target NodeList
	Errors           []error
	text             string
	lex              *lexer
	tokenBackupCount int
	peekCount        int
	token            [7]*item
	sectionLevels    *sectionLevels // Encountered section levels
	id               int            // The unique id of the node in the tree
	indentWidth      int
	indentLevel      int
}

// startParse initializes the parser, using the lexer.
func (t *Tree) startParse(lex *lexer) {
	t.lex = lex
}

func (t *Tree) Parse(text string, treeSet *Tree) (tree *Tree, errors []error) {
	log.Debugln("Start")
	t.startParse(lex(t.Name, text))
	t.text = text
	t.parse(treeSet)
	log.Debugln("End")
	return t, t.Errors
}

func (t *Tree) parse(tree *Tree) {
	log.Debugln("Start")

	t.nodeTarget = t.Nodes

	for t.peek(1).Type != itemEOF {
		var n Node

		token := t.next()
		log.Infof("\nParser got token: %#+v\n\n", token)

		switch token.Type {
		case itemSectionAdornment:
			n = t.section(token)
		case itemParagraph:
			n = newParagraph(token, &t.id)
		case itemSpace:
			n = t.indent(token)
			if n == nil {
				continue
			}
		case itemEOF:
			goto exit
		case itemTitle, itemBlankLine:
			// itemTitle is consumed when evaluating itemSectionAdornment
			continue
		}

		t.nodeTarget.append(n)
		switch n.NodeType() {
		case NodeSection, NodeBlockQuote:
			// Set the loop to append items to the NodeList of the new section
			t.nodeTarget = reflect.ValueOf(n).Elem().FieldByName("NodeList").Addr().Interface().(*NodeList)
		}
	}

exit:
	log.Debugln("End")
}

func (t *Tree) peekBack(pos int) *item {
	return t.token[zed-pos]
}

func (t *Tree) peek(pos int) *item {
	// log.Debugln("\n", "Pos:", pos)
	// log.Debugf("##### peek() before #####\n")
	// spd.Dump(t.token)
	nItem := t.token[zed]
	for i := 1; i <= pos; i++ {
		if t.token[zed+i] != nil {
			nItem = t.token[zed+i]
			log.Debugf("Using %#+v\n", nItem)
			continue
		} else {
			log.Debugln("Getting next item")
			t.token[zed+i] = t.lex.nextItem()
			nItem = t.token[zed+i]
		}
	}
	// log.Debugf("\n##### peek() aftermath #####\n")
	// spd.Dump(t.token)
	// log.Debugf("Returning: %#+v\n", nItem)
	return nItem
}

func (t *Tree) peekSkip(iSkip itemElement) *item {
	var nItem *item
	var count int = 1
	for {
		nItem = t.peek(count)
		if nItem.Type != iSkip {
			 break
		}
		count++
	}
	return nItem
}

func (t *Tree) next() *item {
	// log.Debugf("\n##### next() before #####\n")
	// spd.Dump(t.token)
	for x := 0; x < len(t.token)-1; x++ {
		t.token[x] = t.token[x+1]
		t.token[x+1] = nil
	}
	if t.token[zed] == nil {
		t.token[zed] = t.lex.nextItem()
	}
	// log.Debugf("\n##### next() aftermath #####\n\n")
	// spd.Dump(t.token)
	return t.token[zed]
}

func (t *Tree) section(i *item) Node {
	log.Debugln("Start")
	var overAdorn, title, underAdorn *item
	var sysMessage Node

	peekForward := t.peekSkip(1, itemSpace)
	if peekForward != nil && peekForward.Type == itemTitle {
		log.Debugln("FOUND SECTION WITH OVERLINE")
		overAdorn = i
		t.next()
	loop:
		for {
			switch tTok := t.token[zed]; tTok.Type {
			case itemTitle:
				title = tTok
				t.next()
				cur := t.token[zed]
				if cur != nil && cur.Type == itemSectionAdornment {
					continue
				}
			case itemSectionAdornment:
				underAdorn = tTok
				break loop
			}
		}
	} else {
		if peekBack := t.peekBack(1); peekBack != nil && peekBack.Type == itemSpace {
			// Looking back past the white space
			if t.peekBack(2).Type == itemTitle {
				return t.systemMessage(errorUnexpectedSectionTitle)
			}
			return t.systemMessage(errorUnexpectedSectionTitleOrTransition)
		}
		title = t.peekBack(1)
		underAdorn = i
	}

	sec := newSection(title, overAdorn, underAdorn, indent, &t.id)
	exists, eSec := t.sectionLevels.Add(sec)
	if !exists && eSec != nil {
		// There is a matching level in sectionLevels
		t.nodeTarget = &(*t.sectionLevels)[sec.Level-2].NodeList
	}

	// System messages have to be applied after the section is created in order to preserve
	// a consecutive id number.
	if title.Length != underAdorn.Length {
		sysMessage = t.systemMessage(warningShortUnderline)
		sec.NodeList = append(sec.NodeList, sysMessage)
	}

	log.Debugln("End")
	return sec
}

func (t *Tree) systemMessage(err parserMessage) Node {
	var lbText string
	var lbTextLen int
	var backToken int

	s := newSystemMessage(&item{
		Type: itemSystemMessage,
		Line: t.token[zed].Line,
	},
		err.Level(), &t.id)

	msg := newParagraph(&item{
		Text:   err.Message(),
		Length: len(err.Message()),
	}, &t.id)

	log.Debugln("FOUND", err)

	switch err {
	case warningShortUnderline, errorUnexpectedSectionTitle:
		backToken = zed - 1
		if t.peekBack(1).Type == itemSpace {
			backToken = zed - 2
		}
		lbText = t.token[backToken].Text.(string) + "\n" + t.token[zed].Text.(string)
		lbTextLen = len(lbText) + 1
	case errorUnexpectedSectionTitleOrTransition:
		lbText = t.token[zed].Text.(string)
		lbTextLen = len(lbText)
	}

	lb := newLiteralBlock(&item{
		Type:   itemLiteralBlock,
		Text:   lbText,
		Length: lbTextLen, // Add one to account for the backslash
	}, &t.id)

	s.NodeList = append(s.NodeList, msg, lb)
	return s
}

func (t *Tree) indent(i *item) Node {
	level := i.Length / t.indentWidth
	if t.peekBack(1).Type == itemBlankLine {
		if t.indentLevel == level {
			// Append to the current blockquote NodeList
			return nil
		}
		t.indentLevel = level
		return newBlockQuote(&item{Type: itemBlockquote, Line: i.Line}, level, &t.id)
	}
	return nil
}
<|endoftext|>


<commit_before>package jsh

import (
	"encoding/json"
	"net/http"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func TestParsing(t *testing.T) {

	Convey("Parse Tests", t, func() {

		Convey("->validateHeaders()", func() {
			req, reqErr := http.NewRequest("GET", "", nil)
			So(reqErr, ShouldBeNil)
			req.Header.Set("Content-Type", "jpeg")

			err := validateHeaders(req.Header)
			So(err, ShouldNotBeNil)
			So(err.Status, ShouldEqual, http.StatusNotAcceptable)
		})

		Convey("->ParseObject()", func() {

			Convey("should parse a valid object", func() {

				objectJSON := `{
					"data": {
						"type": "user",
						"id": "sweetID123",
						"attributes": {"ID":"123"},
						"relationships": {
							"company": {
								"data": { "type": "company", "id": "companyID123" }
							},
							"comments": {
								"data": [
									{ "type": "comments", "id": "commentID123" },
									{ "type": "comments", "id": "commentID456" }
								]
							}
						}
					}
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				object, err := ParseObject(req)

				So(err, ShouldBeNil)
				So(object, ShouldNotBeEmpty)
				So(object.Type, ShouldEqual, "user")
				So(object.ID, ShouldEqual, "sweetID123")
				So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"123"}`))
				So(object.Relationships["company"], ShouldResemble, &Relationship{Data: IDList{&IDObject{Type: "company", ID: "companyID123"}}})
				So(object.Relationships["comments"], ShouldResemble, &Relationship{Data: IDList{{Type: "comments", ID: "commentID123"}, {Type: "comments", ID: "commentID456"}}})
			})

			Convey("should reject an object with missing attributes", func() {
				objectJSON := `{"data": {"id": "sweetID123", "attributes": {"ID":"123"}}}`

				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				_, err := ParseObject(req)
				So(err, ShouldNotBeNil)
				So(err.Status, ShouldEqual, 422)
				So(err.Source, ShouldNotBeNil)
				So(err.Source.Pointer, ShouldEqual, "/data/attributes/type")
			})

			Convey("should accept empty ID only for POST", func() {
				objectJSON := `{"data": {"id": "", "type":"test", "attributes": {"ID":"123"}}}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				Convey("POST test", func() {
					req.Method = "POST"
					_, err := ParseObject(req)
					So(err, ShouldBeNil)
				})

				Convey("PATCH test", func() {
					req.Method = "PATCH"
					_, err := ParseObject(req)
					So(err, ShouldNotBeNil)
				})
			})
		})

		Convey("->ParseList()", func() {

			Convey("should parse a valid list", func() {

				listJSON :=
					`{"data": [
		{"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}},
		{"type": "user", "id": "sweetID456", "attributes": {"ID":"456"}}
		]}`
				req, reqErr := testRequest([]byte(listJSON))
				So(reqErr, ShouldBeNil)

				list, err := ParseList(req)
				So(err, ShouldBeNil)
				So(len(list), ShouldEqual, 2)

				object := list[1]
				So(object.Type, ShouldEqual, "user")
				So(object.ID, ShouldEqual, "sweetID456")
				So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"456"}`))
			})

			Convey("should error for an invalid list", func() {
				listJSON :=
					`{"data": [
		{"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}},
		{"type": "user", "attributes": {"ID":"456"}}
		]}`

				req, reqErr := testRequest([]byte(listJSON))
				So(reqErr, ShouldBeNil)

				_, err := ParseList(req)
				So(err, ShouldNotBeNil)
				So(err.Status, ShouldEqual, 422)
				So(err.Source, ShouldNotBeNil)
				So(err.Source.Pointer, ShouldEqual, "/data/attributes/id")
			})
		})

		Convey("->ParseRelationship()", func() {

			Convey("should parse a valid ID object", func() {
				objectJSON := `{
					"data": {
						"type": "user",
						"id": "sweetID123"
					}
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				idObject, err := ParseRelationship(req)
				So(err, ShouldBeNil)
				So(idObject, ShouldNotBeEmpty)
				So(idObject.Type, ShouldEqual, "user")
				So(idObject.ID, ShouldEqual, "sweetID123")
			})

			Convey("should parse a null ID object", func() {
				objectJSON := `{
					"data": null
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				idObject, err := ParseRelationship(req)
				So(err, ShouldBeNil)
				So(idObject, ShouldBeNil)
			})

			Convey("should error for an invalid ID object", func() {
				objectJSON := `{
					"data": {
						"type": "user"
					}
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				_, err := ParseRelationship(req)
				So(err, ShouldNotBeNil)
				So(err.Status, ShouldEqual, 422)
				So(err.Source, ShouldNotBeNil)
				So(err.Source.Pointer, ShouldEqual, "/data/attributes/id")
			})
		})

		Convey("->ParseRelationshipList()", func() {

			Convey("should parse a valid ID list", func() {
				objectJSON := `{
					"data": [
						{
							"type": "user",
							"id": "sweetID123"
						},
						{
							"type": "user",
							"id": "sweetID456"
						}
					]
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				idList, err := ParseRelationshipList(req)
				So(err, ShouldBeNil)
				So(idList, ShouldHaveLength, 2)

				object := idList[1]
				So(object.Type, ShouldEqual, "user")
				So(object.ID, ShouldEqual, "sweetID456")
			})

			Convey("should error for an invalid ID list", func() {
				objectJSON := `{
					"data": [
						{
							"id": "sweetID123"
						},
						{
							"type": "user",
							"id": "sweetID456"
						}
					]
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				_, err := ParseRelationshipList(req)
				So(err, ShouldNotBeNil)
				So(err.Status, ShouldEqual, 422)
				So(err.Source, ShouldNotBeNil)
				So(err.Source.Pointer, ShouldEqual, "/data/attributes/type")
			})
		})
	})
}
<commit_msg>#10 Reformat parser_test struct literals<commit_after>package jsh

import (
	"encoding/json"
	"net/http"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func TestParsing(t *testing.T) {

	Convey("Parse Tests", t, func() {

		Convey("->validateHeaders()", func() {
			req, reqErr := http.NewRequest("GET", "", nil)
			So(reqErr, ShouldBeNil)
			req.Header.Set("Content-Type", "jpeg")

			err := validateHeaders(req.Header)
			So(err, ShouldNotBeNil)
			So(err.Status, ShouldEqual, http.StatusNotAcceptable)
		})

		Convey("->ParseObject()", func() {

			Convey("should parse a valid object", func() {

				objectJSON := `{
					"data": {
						"type": "user",
						"id": "sweetID123",
						"attributes": {"ID":"123"},
						"relationships": {
							"company": {
								"data": { "type": "company", "id": "companyID123" }
							},
							"comments": {
								"data": [
									{ "type": "comments", "id": "commentID123" },
									{ "type": "comments", "id": "commentID456" }
								]
							}
						}
					}
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				object, err := ParseObject(req)

				So(err, ShouldBeNil)
				So(object, ShouldNotBeEmpty)
				So(object.Type, ShouldEqual, "user")
				So(object.ID, ShouldEqual, "sweetID123")
				So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"123"}`))
				So(object.Relationships["company"], ShouldResemble, &Relationship{
					Data: IDList{
						{Type: "company", ID: "companyID123"},
					},
				})
				So(object.Relationships["comments"], ShouldResemble, &Relationship{
					Data: IDList{
						{Type: "comments", ID: "commentID123"},
						{Type: "comments", ID: "commentID456"},
					},
				})
			})

			Convey("should reject an object with missing attributes", func() {
				objectJSON := `{"data": {"id": "sweetID123", "attributes": {"ID":"123"}}}`

				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				_, err := ParseObject(req)
				So(err, ShouldNotBeNil)
				So(err.Status, ShouldEqual, 422)
				So(err.Source, ShouldNotBeNil)
				So(err.Source.Pointer, ShouldEqual, "/data/attributes/type")
			})

			Convey("should accept empty ID only for POST", func() {
				objectJSON := `{"data": {"id": "", "type":"test", "attributes": {"ID":"123"}}}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				Convey("POST test", func() {
					req.Method = "POST"
					_, err := ParseObject(req)
					So(err, ShouldBeNil)
				})

				Convey("PATCH test", func() {
					req.Method = "PATCH"
					_, err := ParseObject(req)
					So(err, ShouldNotBeNil)
				})
			})
		})

		Convey("->ParseList()", func() {

			Convey("should parse a valid list", func() {

				listJSON :=
					`{"data": [
		{"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}},
		{"type": "user", "id": "sweetID456", "attributes": {"ID":"456"}}
		]}`
				req, reqErr := testRequest([]byte(listJSON))
				So(reqErr, ShouldBeNil)

				list, err := ParseList(req)
				So(err, ShouldBeNil)
				So(len(list), ShouldEqual, 2)

				object := list[1]
				So(object.Type, ShouldEqual, "user")
				So(object.ID, ShouldEqual, "sweetID456")
				So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"456"}`))
			})

			Convey("should error for an invalid list", func() {
				listJSON :=
					`{"data": [
		{"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}},
		{"type": "user", "attributes": {"ID":"456"}}
		]}`

				req, reqErr := testRequest([]byte(listJSON))
				So(reqErr, ShouldBeNil)

				_, err := ParseList(req)
				So(err, ShouldNotBeNil)
				So(err.Status, ShouldEqual, 422)
				So(err.Source, ShouldNotBeNil)
				So(err.Source.Pointer, ShouldEqual, "/data/attributes/id")
			})
		})

		Convey("->ParseRelationship()", func() {

			Convey("should parse a valid ID object", func() {
				objectJSON := `{
					"data": {
						"type": "user",
						"id": "sweetID123"
					}
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				idObject, err := ParseRelationship(req)
				So(err, ShouldBeNil)
				So(idObject, ShouldNotBeEmpty)
				So(idObject.Type, ShouldEqual, "user")
				So(idObject.ID, ShouldEqual, "sweetID123")
			})

			Convey("should parse a null ID object", func() {
				objectJSON := `{
					"data": null
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				idObject, err := ParseRelationship(req)
				So(err, ShouldBeNil)
				So(idObject, ShouldBeNil)
			})

			Convey("should error for an invalid ID object", func() {
				objectJSON := `{
					"data": {
						"type": "user"
					}
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				_, err := ParseRelationship(req)
				So(err, ShouldNotBeNil)
				So(err.Status, ShouldEqual, 422)
				So(err.Source, ShouldNotBeNil)
				So(err.Source.Pointer, ShouldEqual, "/data/attributes/id")
			})
		})

		Convey("->ParseRelationshipList()", func() {

			Convey("should parse a valid ID list", func() {
				objectJSON := `{
					"data": [
						{
							"type": "user",
							"id": "sweetID123"
						},
						{
							"type": "user",
							"id": "sweetID456"
						}
					]
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				idList, err := ParseRelationshipList(req)
				So(err, ShouldBeNil)
				So(idList, ShouldHaveLength, 2)

				object := idList[1]
				So(object.Type, ShouldEqual, "user")
				So(object.ID, ShouldEqual, "sweetID456")
			})

			Convey("should error for an invalid ID list", func() {
				objectJSON := `{
					"data": [
						{
							"id": "sweetID123"
						},
						{
							"type": "user",
							"id": "sweetID456"
						}
					]
				}`
				req, reqErr := testRequest([]byte(objectJSON))
				So(reqErr, ShouldBeNil)

				_, err := ParseRelationshipList(req)
				So(err, ShouldNotBeNil)
				So(err.Status, ShouldEqual, 422)
				So(err.Source, ShouldNotBeNil)
				So(err.Source.Pointer, ShouldEqual, "/data/attributes/type")
			})
		})
	})
}
<|endoftext|>


<commit_before>package cajun

import "testing"

type parserTest struct {
	name   string
	input  string
	output string
}

var parserTests = []parserTest{
	{"empty", "", ""},
	{"spaces", "   ", "   "},
	{"heading1", "= Level 1 =", "
 Level 1
"},
	{"should not be heading1. no space", "=Level 1 =", "
=Level 1 =
"},
	{"heading2", "== Level 2 ==", "
 Level 2
"},
	{"heading3", "=== Level 3 ===", "
 Level 3
"},
	{"heading4", "==== Level 4 ====", "
 Level 4
"},
	{"heading5", "===== Level 5 =====", "
 Level 5
"},
	{"heading6", "====== Level 6 ======", "
 Level 6
"},
	{"heading6", "====== Level 6 ========", "
 Level 6
"},
	{"heading: should close h1 as h3", "=== Level =", "
 Level
"},
	{"hr", "----", "
"},
	{"hr preceeded by space", "  ----", "
"},
	{"hr preceeded by space, break, then text", "  ----  \n more", "
   more"},
	{"hr followed by space", "----  ", "
  "},
	{"hr too many dashes", "-----", "
-----
"},
	{"text", `now is the time`, "
now is the time
"},
	{"text with bold", "hello-~**blah**-world", "
hello-**blah-world
"},
	{"text with bold", "hello-**blah**-world", "
hello-blah-world
"},
	{"text with italics", "hello-//blah//-world", "
hello-blah-world
"},
	{"text with bad order", "hello-**//blah**//-world", "
hello-blah-world
"},
	{"text with bad order, twice", "hello-**//blah**//-world**//blah**// this is a **test**", "
hello-blah-worldblah this is a test
"},
	{"text with bad order, twice", "hello-**//blah**//**//blah**//", "
hello-blahblah
"},
	{"test closing bold accross line breaks", "close this ** testing a \n\n\n\n\n\n bold... more stuff here", "
close this  testing a

 bold... more stuff here
"},

	{"line break", "line \\\\break", "
line
break
"},
	{"unordered list simple", "* list item\n** child item", "

 list item

 child item

"},
	{"unordered list - one child, in first parent", "* list item\n** child item\n* list item", "

 list item

 child item

 list item

"},
	{"unordered list - two child in first parent", "* item1\n** item1.1\n** item1.2\n* item2", "

 item1

 item1.1

 item1.2

 item2

"},
	{"unordered list - 3 first level, 2 child", "* item1\n** item1.1\n** item1.2\n* item2\n* item3", "

 item1

 item1.1

 item1.2

 item2

 item3

"},
	{"unordered list, ", "* item1\n** item1.1\n** item1.2", "

 item1

 item1.1

 item1.2

"},
	{"3 children", "* item1\n** item1.1\n** item1.2\n** item1.3", "

 item1

 item1.1

 item1.2

 item1.3

"},
	{"unordered list - long", "* item1\n** item1.1\n** item1.2\n* item2 \n** item2.1\n** item2.2\n*** item2.2.1", "

 item1

 item1.1

 item1.2

 item2

 item2.1

 item2.2

 item2.2.1

"},
	{"5 levels", "* 1\n** 2\n*** 3\n**** 4\n***** 5", "

 1

 2

 3

 4

 5

"},
	{"multiline list items", "* 1\n test\n* 2\n test", "

 1 test

 2 test

"},

	{"ordered list simple", "# list item\n## child item", "

 list item

 child item

"},
	{"ordered list - one child, in first parent", "# list item\n## child item\n# list item", "

 list item

 child item

 list item

"},
	{"ordered list - two child in first parent", "# item1\n## item1.1\n## item1.2\n# item2", "

 item1

 item1.1

 item1.2

 item2

"},
	{"ordered list - 3 first level, 2 child", "# item1\n## item1.1\n## item1.2\n# item2\n# item3", "

 item1

 item1.1

 item1.2

 item2

 item3

"},
	{"ordered list, ", "# item1\n## item1.1\n## item1.2", "

 item1

 item1.1

 item1.2

"},
	{"ordered 3 children", "# item1\n## item1.1\n## item1.2\n## item1.3", "

 item1

 item1.1

 item1.2

 item1.3

"},
	{"ordered list - long", "# item1\n## item1.1\n## item1.2\n# item2 \n## item2.1\n## item2.2\n### item2.2.1", "

 item1

 item1.1

 item1.2

 item2

 item2.1

 item2.2

 item2.2.1

"},
	{"ordered 5 levels", "# 1\n## 2\n### 3\n#### 4\n##### 5", "

 1

 2

 3

 4

 5

"},
	{"multiline ordered list items", "# 1\n test\n# 2\n test", "

 1 test

 2 test

"},
	{"image simple", "{{Red-Flower.jpg|here is a red flower}}", ""},
	{"image simple no alt", "{{Red-Flower.jpg}}", ""},
	{"link simple", "[[http://www.wikicreole.org|external links]]", "external links"},
	{"link simple", "[[http://www.wikicreole.org]]", "http://www.wikicreole.org"},
	{"free link simple", "this text has a link http://www.wikicreole.org to wiki creole", "
this text has a link http://www.wikicreole.org to wiki creole
"},
}

func TestParser(t *testing.T) {
	for _, test := range parserTests {
		output, _ := Transform(test.input)
		if test.output != output {
			t.Errorf("%s: got\n\t%+v\nexpected\n\t%v", test.name, output, test.output)
		}

	}
}

//func TestParserLarge(t *testing.T) {
//	dat, _ := ioutil.ReadFile("./creole1.0test.txt")
//	//	fmt.Print(string(dat))
//	output, _ := Transform(string(dat))
//	fmt.Print("-------------\n", output)
//}
<commit_msg>fix test desc<commit_after>package cajun

import "testing"

type parserTest struct {
	name   string
	input  string
	output string
}

var parserTests = []parserTest{
	{"empty", "", ""},
	{"spaces", "   ", "   "},
	{"heading1", "= Level 1 =", "
 Level 1
"},
	{"should not be heading1. no space", "=Level 1 =", "
=Level 1 =
"},
	{"heading2", "== Level 2 ==", "
 Level 2
"},
	{"heading3", "=== Level 3 ===", "
 Level 3
"},
	{"heading4", "==== Level 4 ====", "
 Level 4
"},
	{"heading5", "===== Level 5 =====", "
 Level 5
"},
	{"heading6", "====== Level 6 ======", "
 Level 6
"},
	{"heading6", "====== Level 6 ========", "
 Level 6
"},
	{"heading: should close h1 as h3", "=== Level =", "
 Level
"},
	{"hr", "----", "
"},
	{"hr preceeded by space", "  ----", "
"},
	{"hr preceeded by space, break, then text", "  ----  \n more", "
   more"},
	{"hr followed by space", "----  ", "
  "},
	{"hr too many dashes", "-----", "
-----
"},
	{"text", `now is the time`, "
now is the time
"},
	{"text with escaped bold", "hello-~**blah**-world", "
hello-**blah-world
"},
	{"text with bold", "hello-**blah**-world", "
hello-blah-world
"},
	{"text with italics", "hello-//blah//-world", "
hello-blah-world
"},
	{"text with bad order", "hello-**//blah**//-world", "
hello-blah-world
"},
	{"text with bad order, twice", "hello-**//blah**//-world**//blah**// this is a **test**", "
hello-blah-worldblah this is a test
"},
	{"text with bad order, twice", "hello-**//blah**//**//blah**//", "
hello-blahblah
"},
	{"test closing bold accross line breaks", "close this ** testing a \n\n\n\n\n\n bold... more stuff here", "
close this  testing a

 bold... more stuff here
"},

	{"line break", "line \\\\break", "
line
break
"},
	{"unordered list simple", "* list item\n** child item", "

 list item

 child item

"},
	{"unordered list - one child, in first parent", "* list item\n** child item\n* list item", "

 list item

 child item

 list item

"},
	{"unordered list - two child in first parent", "* item1\n** item1.1\n** item1.2\n* item2", "

 item1

 item1.1

 item1.2

 item2

"},
	{"unordered list - 3 first level, 2 child", "* item1\n** item1.1\n** item1.2\n* item2\n* item3", "

 item1

 item1.1

 item1.2

 item2

 item3

"},
	{"unordered list, ", "* item1\n** item1.1\n** item1.2", "

 item1

 item1.1

 item1.2

"},
	{"3 children", "* item1\n** item1.1\n** item1.2\n** item1.3", "

 item1

 item1.1

 item1.2

 item1.3

"},
	{"unordered list - long", "* item1\n** item1.1\n** item1.2\n* item2 \n** item2.1\n** item2.2\n*** item2.2.1", "

 item1

 item1.1

 item1.2

 item2

 item2.1

 item2.2

 item2.2.1

"},
	{"5 levels", "* 1\n** 2\n*** 3\n**** 4\n***** 5", "

 1

 2

 3

 4

 5

"},
	{"multiline list items", "* 1\n test\n* 2\n test", "

 1 test

 2 test

"},

	{"ordered list simple", "# list item\n## child item", "

 list item

 child item

"},
	{"ordered list - one child, in first parent", "# list item\n## child item\n# list item", "

 list item

 child item

 list item

"},
	{"ordered list - two child in first parent", "# item1\n## item1.1\n## item1.2\n# item2", "

 item1

 item1.1

 item1.2

 item2

"},
	{"ordered list - 3 first level, 2 child", "# item1\n## item1.1\n## item1.2\n# item2\n# item3", "

 item1

 item1.1

 item1.2

 item2

 item3

"},
	{"ordered list, ", "# item1\n## item1.1\n## item1.2", "

 item1

 item1.1

 item1.2

"},
	{"ordered 3 children", "# item1\n## item1.1\n## item1.2\n## item1.3", "

 item1

 item1.1

 item1.2

 item1.3

"},
	{"ordered list - long", "# item1\n## item1.1\n## item1.2\n# item2 \n## item2.1\n## item2.2\n### item2.2.1", "

 item1

 item1.1

 item1.2

 item2

 item2.1

 item2.2

 item2.2.1

"},
	{"ordered 5 levels", "# 1\n## 2\n### 3\n#### 4\n##### 5", "

 1

 2

 3

 4

 5

"},
	{"multiline ordered list items", "# 1\n test\n# 2\n test", "

 1 test

 2 test

"},
	{"image simple", "{{Red-Flower.jpg|here is a red flower}}", ""},
	{"image simple no alt", "{{Red-Flower.jpg}}", ""},
	{"link simple", "[[http://www.wikicreole.org|external links]]", "external links"},
	{"link simple", "[[http://www.wikicreole.org]]", "http://www.wikicreole.org"},
	{"free link simple", "this text has a link http://www.wikicreole.org to wiki creole", "
this text has a link http://www.wikicreole.org to wiki creole
"},
}

func TestParser(t *testing.T) {
	for _, test := range parserTests {
		output, _ := Transform(test.input)
		if test.output != output {
			t.Errorf("%s: got\n\t%+v\nexpected\n\t%v", test.name, output, test.output)
		}

	}
}

//func TestParserLarge(t *testing.T) {
//	dat, _ := ioutil.ReadFile("./creole1.0test.txt")
//	//	fmt.Print(string(dat))
//	output, _ := Transform(string(dat))
//	fmt.Print("-------------\n", output)
//}
<|endoftext|>