Merge branch 'comment_branch'
diff --git a/README.md b/README.md
index f7ebfe5..462004f 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,9 @@
 Filenames can also contain environment variables like in
 `/home/${USER}/myapp.properties`.
 
+Comments and the order of keys are preserved. Comments can be modified
+and can be written to the output.
+
 The properties library supports both ISO-8859-1 and UTF-8 encoded data.
 
 Starting from version 1.3.0 the behavior of the MustXXX() functions is
@@ -41,9 +44,23 @@
 $ go get -u github.com/magiconair/properties
 ```
 
+For testing and debugging you need the [go-check](https://github.com/go-check/check) library
+
+```
+$ go get -u gopkg.in/check.v1
+```
+
 History
 -------
 
+v1.5.0, 18 Nov 2014
+-------------------
+ * Added support for single and multi-line comments (reading, writing and updating)
+ * The order of keys is now preserved
+ * Calling Set() with an empty key now silently ignores the call and does not create a new entry
+ * Added a MustSet() method
+ * Migrated test library from launchpad.net/gocheck to gopkg.in/check.v1
+
 v1.4.2, 15 Nov 2014
 -------------------
  * Issue #2: Fixed goroutine leak in parser which created two lexers but cleaned up only one
diff --git a/lex.go b/lex.go
index f6c0c23..1d6b532 100644
--- a/lex.go
+++ b/lex.go
@@ -44,8 +44,9 @@
 const (
 	itemError itemType = iota // error occurred; value is text of error
 	itemEOF
-	itemKey   // a key
-	itemValue // a value
+	itemKey     // a key
+	itemValue   // a value
+	itemComment // a comment
 )
 
 // defines a constant for EOF
@@ -207,6 +208,8 @@
 
 // lexComment scans a comment line. The comment character has already been scanned.
 func lexComment(l *lexer) stateFn {
+	l.acceptRun(whitespace)
+	l.ignore()
 	for {
 		switch r := l.next(); {
 		case isEOF(r):
@@ -214,8 +217,10 @@
 			l.emit(itemEOF)
 			return nil
 		case isEOL(r):
-			l.ignore()
+			l.emit(itemComment)
 			return lexBeforeKey
+		default:
+			l.appendRune(r)
 		}
 	}
 }
diff --git a/load_test.go b/load_test.go
index 66f9b7c..9d1e3b6 100644
--- a/load_test.go
+++ b/load_test.go
@@ -10,7 +10,7 @@
 	"os"
 	"strings"
 
-	. "launchpad.net/gocheck"
+	. "gopkg.in/check.v1"
 )
 
 type LoadSuite struct {
diff --git a/parser.go b/parser.go
index d996d00..bb71fb9 100644
--- a/parser.go
+++ b/parser.go
@@ -18,22 +18,39 @@
 	defer p.recover(&err)
 
 	properties = NewProperties()
+	key := ""
+	comments := []string{}
 
 	for {
-		token := p.expectOneOf(itemKey, itemEOF)
-		if token.typ == itemEOF {
-			break
+		token := p.expectOneOf(itemComment, itemKey, itemEOF)
+		switch token.typ {
+		case itemEOF:
+			goto done
+		case itemComment:
+			comments = append(comments, token.val)
+			continue
+		case itemKey:
+			key = token.val
+			if _, ok := properties.m[key]; !ok {
+				properties.k = append(properties.k, key)
+			}
 		}
-		key := token.val
 
 		token = p.expectOneOf(itemValue, itemEOF)
-		if token.typ == itemEOF {
-			properties.m[key] = ""
-			break
+		if len(comments) > 0 {
+			properties.c[key] = comments
+			comments = []string{}
 		}
-		properties.m[key] = token.val
+		switch token.typ {
+		case itemEOF:
+			properties.m[key] = ""
+			goto done
+		case itemValue:
+			properties.m[key] = token.val
+		}
 	}
 
+done:
 	return properties, nil
 }
 
@@ -50,12 +67,15 @@
 	return token
 }
 
-func (p *parser) expectOneOf(expected1, expected2 itemType) (token item) {
+func (p *parser) expectOneOf(expected ...itemType) (token item) {
 	token = p.lex.nextItem()
-	if token.typ != expected1 && token.typ != expected2 {
-		p.unexpected(token)
+	for _, v := range expected {
+		if token.typ == v {
+			return token
+		}
 	}
-	return token
+	p.unexpected(token)
+	panic("unexpected token")
 }
 
 func (p *parser) unexpected(token item) {
diff --git a/properties.go b/properties.go
index cde68fe..e9aca7a 100644
--- a/properties.go
+++ b/properties.go
@@ -45,7 +45,14 @@
 	Prefix  string
 	Postfix string
 
+	// Stores the key/value pairs
 	m map[string]string
+
+	// Stores the comments per key.
+	c map[string][]string
+
+	// Stores the keys in order of appearance.
+	k []string
 }
 
 // NewProperties creates a new Properties struct with the default
@@ -54,7 +61,9 @@
 	return &Properties{
 		Prefix:  "${",
 		Postfix: "}",
-		m:       make(map[string]string),
+		m:       map[string]string{},
+		c:       map[string][]string{},
+		k:       []string{},
 	}
 }
 
@@ -90,6 +99,53 @@
 
 // ----------------------------------------------------------------------------
 
+// ClearComments removes the comments for all keys.
+func (p *Properties) ClearComments() {
+	p.c = map[string][]string{}
+}
+
+// ----------------------------------------------------------------------------
+
+// GetComment returns the last comment before the given key or an empty string.
+func (p *Properties) GetComment(key string) string {
+	comments, ok := p.c[key]
+	if !ok || len(comments) == 0 {
+		return ""
+	}
+	return comments[len(comments)-1]
+}
+
+// ----------------------------------------------------------------------------
+
+// GetComments returns all comments that appeared before the given key or nil.
+func (p *Properties) GetComments(key string) []string {
+	if comments, ok := p.c[key]; ok {
+		return comments
+	}
+	return nil
+}
+
+// ----------------------------------------------------------------------------
+
+// SetComment sets the comment for the key.
+func (p *Properties) SetComment(key, comment string) {
+	p.c[key] = []string{comment}
+}
+
+// ----------------------------------------------------------------------------
+
+// SetComments sets the comments for the key. If the comments are nil then
+// all comments for this key are deleted.
+func (p *Properties) SetComments(key string, comments []string) {
+	if comments == nil {
+		delete(p.c, key)
+		return
+	}
+	p.c[key] = comments
+}
+
+// ----------------------------------------------------------------------------
+
 // GetBool checks if the expanded value is one of '1', 'yes',
 // 'true' or 'on' if the key exists. The comparison is case-insensitive.
 // If the key does not exist the default value is returned.
@@ -360,11 +416,11 @@
 	return len(p.m)
 }
 
-// Keys returns all keys.
+// Keys returns all keys in the same order as in the input.
 func (p *Properties) Keys() []string {
-	keys := make([]string, 0, len(p.m))
-	for k, _ := range p.m {
-		keys = append(keys, k)
+	keys := make([]string, len(p.k))
+	for i, k := range p.k {
+		keys[i] = k
 	}
 	return keys
 }
@@ -374,21 +430,55 @@
 // contains the previous value. If the value contains a
 // circular reference or a malformed expression then
 // an error is returned.
+// An empty key is silently ignored.
 func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
+	if key == "" {
+		return "", false, nil
+	}
+
+	// to check for a circular reference we temporarily need
+	// to set the new value. If there is an error then revert
+	// to the previous state. Only if all tests are successful
+	// then we add the key to the p.k list.
+	prev, ok = p.Get(key)
+	p.m[key] = value
+
+	// now check for a circular reference
 	_, err = p.expand(value)
 	if err != nil {
+
+		// revert to the previous state
+		if ok {
+			p.m[key] = prev
+		} else {
+			delete(p.m, key)
+		}
+
 		return "", false, err
 	}
 
-	v, ok := p.Get(key)
-	p.m[key] = value
-	return v, ok, nil
+	if !ok {
+		p.k = append(p.k, key)
+	}
+
+	return prev, ok, nil
+}
+
+// MustSet sets the property key to the corresponding value.
+// If a value for key existed before then ok is true and prev
+// contains the previous value. An empty key is silently ignored.
+func (p *Properties) MustSet(key, value string) (prev string, ok bool) {
+	prev, ok, err := p.Set(key, value)
+	if err != nil {
+		ErrorHandler(err)
+	}
+	return prev, ok
 }
 
 // String returns a string of all expanded 'key = value' pairs.
 func (p *Properties) String() string {
 	var s string
-	for key, _ := range p.m {
+	for _, key := range p.k {
 		value, _ := p.Get(key)
 		s = fmt.Sprintf("%s%s = %s\n", s, key, value)
 	}
@@ -396,17 +486,51 @@
 }
 
 // Write writes all unexpanded 'key = value' pairs to the given writer.
-func (p *Properties) Write(w io.Writer, enc Encoding) (int, error) {
-	total := 0
-	for key, value := range p.m {
-		s := fmt.Sprintf("%s = %s\n", encode(key, " :", enc), encode(value, "", enc))
-		n, err := w.Write([]byte(s))
-		if err != nil {
-			return total, err
+// Write returns the number of bytes written and any write error encountered.
+func (p *Properties) Write(w io.Writer, enc Encoding) (n int, err error) {
+	return p.WriteComment(w, "", enc)
+}
+
+// WriteComment writes all unexpanced 'key = value' pairs to the given writer.
+// If prefix is not empty then comments are written with a blank line and the
+// given prefix. The prefix should be either "# " or "! " to be compatible with
+// the properties file format. Otherwise, the properties parser will not be
+// able to read the file back in. It returns the number of bytes written and
+// any write error encountered.
+func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n int, err error) {
+	var x int
+
+	for _, key := range p.k {
+		value := p.m[key]
+
+		if prefix != "" {
+			if comments, ok := p.c[key]; ok {
+				// add a blank line between entries but not at the top
+				if len(comments) > 0 && n > 0 {
+					x, err = fmt.Fprintln(w)
+					if err != nil {
+						return
+					}
+					n += x
+				}
+
+				for _, c := range comments {
+					x, err = fmt.Fprintf(w, "%s%s\n", prefix, encode(c, "", enc))
+					if err != nil {
+						return
+					}
+					n += x
+				}
+			}
 		}
-		total += n
+
+		x, err = fmt.Fprintf(w, "%s = %s\n", encode(key, " :", enc), encode(value, "", enc))
+		if err != nil {
+			return
+		}
+		n += x
 	}
-	return total, nil
+	return
 }
 
 // ----------------------------------------------------------------------------
diff --git a/properties_test.go b/properties_test.go
index 4abe20b..fda5b15 100644
--- a/properties_test.go
+++ b/properties_test.go
@@ -14,7 +14,7 @@
 	"testing"
 	"time"
 
-	. "launchpad.net/gocheck"
+	. "gopkg.in/check.v1"
 )
 
 func Test(t *testing.T) { TestingT(t) }
@@ -109,9 +109,32 @@
 	{"key=${USER}\nUSER=value", "key", "value", "USER", "value"},
 }
 
-// define error test cases in the form of
-// {"input", "expected error message"}
-var errorTests = [][]string{
+// ----------------------------------------------------------------------------
+
+var commentTests = []struct {
+	input, key, value string
+	comments          []string
+}{
+	{"key=value", "key", "value", nil},
+	{"#comment\nkey=value", "key", "value", []string{"comment"}},
+	{"# comment\nkey=value", "key", "value", []string{"comment"}},
+	{"#  comment\nkey=value", "key", "value", []string{"comment"}},
+	{"# comment\n\nkey=value", "key", "value", []string{"comment"}},
+	{"# comment1\n# comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}},
+	{"# comment1\n\n# comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}},
+	{"!comment\nkey=value", "key", "value", []string{"comment"}},
+	{"! comment\nkey=value", "key", "value", []string{"comment"}},
+	{"!  comment\nkey=value", "key", "value", []string{"comment"}},
+	{"! comment\n\nkey=value", "key", "value", []string{"comment"}},
+	{"! comment1\n! comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}},
+	{"! comment1\n\n! comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}},
+}
+
+// ----------------------------------------------------------------------------
+
+var errorTests = []struct {
+	input, msg string
+}{
 	// unicode literals
 	{"key\\u1 = value", "Invalid unicode literal"},
 	{"key\\u12 = value", "Invalid unicode literal"},
@@ -128,9 +151,11 @@
 	{"key=valu${ke", "Malformed expression"},
 }
 
-// define write encoding test cases in the form of
-// {"input", "expected output after write", ["UTF-8", "ISO-8859-1"]}
-var writeTests = [][]string{
+// ----------------------------------------------------------------------------
+
+var writeTests = []struct {
+	input, output, encoding string
+}{
 	// ISO-8859-1 tests
 	{"key = value", "key = value\n", "ISO-8859-1"},
 	{"key = value \\\n   continued", "key = value continued\n", "ISO-8859-1"},
@@ -146,202 +171,224 @@
 
 // ----------------------------------------------------------------------------
 
-type boolTest struct {
+var writeCommentTests = []struct {
+	input, output, encoding string
+}{
+	// ISO-8859-1 tests
+	{"key = value", "key = value\n", "ISO-8859-1"},
+	{"# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
+	{"\n# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
+	{"# comment\n\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
+	{"# comment1\n# comment2\nkey = value", "# comment1\n# comment2\nkey = value\n", "ISO-8859-1"},
+	{"#comment1\nkey1 = value1\n#comment2\nkey2 = value2", "# comment1\nkey1 = value1\n\n# comment2\nkey2 = value2\n", "ISO-8859-1"},
+
+	// UTF-8 tests
+	{"key = value", "key = value\n", "UTF-8"},
+	{"# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
+	{"\n# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
+	{"# comment⌘\n\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
+	{"# comment1⌘\n# comment2⌘\nkey = value⌘", "# comment1⌘\n# comment2⌘\nkey = value⌘\n", "UTF-8"},
+	{"#comment1⌘\nkey1 = value1⌘\n#comment2⌘\nkey2 = value2⌘", "# comment1⌘\nkey1 = value1⌘\n\n# comment2⌘\nkey2 = value2⌘\n", "UTF-8"},
+}
+
+// ----------------------------------------------------------------------------
+
+var boolTests = []struct {
 	input, key string
 	def, value bool
-}
-
-var boolTests = []*boolTest{
+}{
 	// valid values for TRUE
-	&boolTest{"key = 1", "key", false, true},
-	&boolTest{"key = on", "key", false, true},
-	&boolTest{"key = On", "key", false, true},
-	&boolTest{"key = ON", "key", false, true},
-	&boolTest{"key = true", "key", false, true},
-	&boolTest{"key = True", "key", false, true},
-	&boolTest{"key = TRUE", "key", false, true},
-	&boolTest{"key = yes", "key", false, true},
-	&boolTest{"key = Yes", "key", false, true},
-	&boolTest{"key = YES", "key", false, true},
+	{"key = 1", "key", false, true},
+	{"key = on", "key", false, true},
+	{"key = On", "key", false, true},
+	{"key = ON", "key", false, true},
+	{"key = true", "key", false, true},
+	{"key = True", "key", false, true},
+	{"key = TRUE", "key", false, true},
+	{"key = yes", "key", false, true},
+	{"key = Yes", "key", false, true},
+	{"key = YES", "key", false, true},
 
 	// valid values for FALSE (all other)
-	&boolTest{"key = 0", "key", true, false},
-	&boolTest{"key = off", "key", true, false},
-	&boolTest{"key = false", "key", true, false},
-	&boolTest{"key = no", "key", true, false},
+	{"key = 0", "key", true, false},
+	{"key = off", "key", true, false},
+	{"key = false", "key", true, false},
+	{"key = no", "key", true, false},
 
 	// non existent key
-	&boolTest{"key = true", "key2", false, false},
+	{"key = true", "key2", false, false},
 }
 
 // ----------------------------------------------------------------------------
 
-type durationTest struct {
+var durationTests = []struct {
 	input, key string
 	def, value time.Duration
-}
-
-var durationTests = []*durationTest{
+}{
 	// valid values
-	&durationTest{"key = 1", "key", 999, 1},
-	&durationTest{"key = 0", "key", 999, 0},
-	&durationTest{"key = -1", "key", 999, -1},
-	&durationTest{"key = 0123", "key", 999, 123},
+	{"key = 1", "key", 999, 1},
+	{"key = 0", "key", 999, 0},
+	{"key = -1", "key", 999, -1},
+	{"key = 0123", "key", 999, 123},
 
 	// invalid values
-	&durationTest{"key = 0xff", "key", 999, 999},
-	&durationTest{"key = 1.0", "key", 999, 999},
-	&durationTest{"key = a", "key", 999, 999},
+	{"key = 0xff", "key", 999, 999},
+	{"key = 1.0", "key", 999, 999},
+	{"key = a", "key", 999, 999},
 
 	// non existent key
-	&durationTest{"key = 1", "key2", 999, 999},
+	{"key = 1", "key2", 999, 999},
 }
 
 // ----------------------------------------------------------------------------
 
-type floatTest struct {
+var floatTests = []struct {
 	input, key string
 	def, value float64
-}
-
-var floatTests = []*floatTest{
+}{
 	// valid values
-	&floatTest{"key = 1.0", "key", 999, 1.0},
-	&floatTest{"key = 0.0", "key", 999, 0.0},
-	&floatTest{"key = -1.0", "key", 999, -1.0},
-	&floatTest{"key = 1", "key", 999, 1},
-	&floatTest{"key = 0", "key", 999, 0},
-	&floatTest{"key = -1", "key", 999, -1},
-	&floatTest{"key = 0123", "key", 999, 123},
+	{"key = 1.0", "key", 999, 1.0},
+	{"key = 0.0", "key", 999, 0.0},
+	{"key = -1.0", "key", 999, -1.0},
+	{"key = 1", "key", 999, 1},
+	{"key = 0", "key", 999, 0},
+	{"key = -1", "key", 999, -1},
+	{"key = 0123", "key", 999, 123},
 
 	// invalid values
-	&floatTest{"key = 0xff", "key", 999, 999},
-	&floatTest{"key = a", "key", 999, 999},
+	{"key = 0xff", "key", 999, 999},
+	{"key = a", "key", 999, 999},
 
 	// non existent key
-	&floatTest{"key = 1", "key2", 999, 999},
+	{"key = 1", "key2", 999, 999},
 }
 
 // ----------------------------------------------------------------------------
 
-type int64Test struct {
+var int64Tests = []struct {
 	input, key string
 	def, value int64
-}
-
-var int64Tests = []*int64Test{
+}{
 	// valid values
-	&int64Test{"key = 1", "key", 999, 1},
-	&int64Test{"key = 0", "key", 999, 0},
-	&int64Test{"key = -1", "key", 999, -1},
-	&int64Test{"key = 0123", "key", 999, 123},
+	{"key = 1", "key", 999, 1},
+	{"key = 0", "key", 999, 0},
+	{"key = -1", "key", 999, -1},
+	{"key = 0123", "key", 999, 123},
 
 	// invalid values
-	&int64Test{"key = 0xff", "key", 999, 999},
-	&int64Test{"key = 1.0", "key", 999, 999},
-	&int64Test{"key = a", "key", 999, 999},
+	{"key = 0xff", "key", 999, 999},
+	{"key = 1.0", "key", 999, 999},
+	{"key = a", "key", 999, 999},
 
 	// non existent key
-	&int64Test{"key = 1", "key2", 999, 999},
+	{"key = 1", "key2", 999, 999},
 }
 
 // ----------------------------------------------------------------------------
 
-type uint64Test struct {
+var uint64Tests = []struct {
 	input, key string
 	def, value uint64
-}
-
-var uint64Tests = []*uint64Test{
+}{
 	// valid values
-	&uint64Test{"key = 1", "key", 999, 1},
-	&uint64Test{"key = 0", "key", 999, 0},
-	&uint64Test{"key = 0123", "key", 999, 123},
+	{"key = 1", "key", 999, 1},
+	{"key = 0", "key", 999, 0},
+	{"key = 0123", "key", 999, 123},
 
 	// invalid values
-	&uint64Test{"key = -1", "key", 999, 999},
-	&uint64Test{"key = 0xff", "key", 999, 999},
-	&uint64Test{"key = 1.0", "key", 999, 999},
-	&uint64Test{"key = a", "key", 999, 999},
+	{"key = -1", "key", 999, 999},
+	{"key = 0xff", "key", 999, 999},
+	{"key = 1.0", "key", 999, 999},
+	{"key = a", "key", 999, 999},
 
 	// non existent key
-	&uint64Test{"key = 1", "key2", 999, 999},
+	{"key = 1", "key2", 999, 999},
 }
 
 // ----------------------------------------------------------------------------
 
-type stringTest struct {
+var stringTests = []struct {
 	input, key string
 	def, value string
-}
-
-var stringTests = []*stringTest{
+}{
 	// valid values
-	&stringTest{"key = abc", "key", "def", "abc"},
+	{"key = abc", "key", "def", "abc"},
 
 	// non existent key
-	&stringTest{"key = abc", "key2", "def", "def"},
+	{"key = abc", "key2", "def", "def"},
 }
 
 // ----------------------------------------------------------------------------
 
-type keysTest struct {
+var keysTests = []struct {
 	input string
 	keys  []string
-}
-
-var keysTests = []*keysTest{
-	&keysTest{"", []string{}},
-	&keysTest{"key = abc", []string{"key"}},
-	&keysTest{"key = abc\nkey2=def", []string{"key", "key2"}},
-	&keysTest{"key = abc\nkey=def", []string{"key"}},
+}{
+	{"", []string{}},
+	{"key = abc", []string{"key"}},
+	{"key = abc\nkey2=def", []string{"key", "key2"}},
+	{"key2 = abc\nkey=def", []string{"key2", "key"}},
+	{"key = abc\nkey=def", []string{"key"}},
 }
 
 // ----------------------------------------------------------------------------
 
-type filterTest struct {
+var filterTests = []struct {
 	input   string
 	pattern string
 	keys    []string
 	err     string
-}
-
-var filterTests = []*filterTest{
-	&filterTest{"", "", []string{}, ""},
-	&filterTest{"", "abc", []string{}, ""},
-	&filterTest{"key=value", "", []string{"key"}, ""},
-	&filterTest{"key=value", "key=", []string{}, ""},
-	&filterTest{"key=value\nfoo=bar", "", []string{"foo", "key"}, ""},
-	&filterTest{"key=value\nfoo=bar", "f", []string{"foo"}, ""},
-	&filterTest{"key=value\nfoo=bar", "fo", []string{"foo"}, ""},
-	&filterTest{"key=value\nfoo=bar", "foo", []string{"foo"}, ""},
-	&filterTest{"key=value\nfoo=bar", "fooo", []string{}, ""},
-	&filterTest{"key=value\nkey2=value2\nfoo=bar", "ey", []string{"key", "key2"}, ""},
-	&filterTest{"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}, ""},
-	&filterTest{"key=value\nkey2=value2\nfoo=bar", "^key", []string{"key", "key2"}, ""},
-	&filterTest{"key=value\nkey2=value2\nfoo=bar", "^(key|foo)", []string{"foo", "key", "key2"}, ""},
-	&filterTest{"key=value\nkey2=value2\nfoo=bar", "[ abc", nil, "error parsing regexp.*"},
+}{
+	{"", "", []string{}, ""},
+	{"", "abc", []string{}, ""},
+	{"key=value", "", []string{"key"}, ""},
+	{"key=value", "key=", []string{}, ""},
+	{"key=value\nfoo=bar", "", []string{"foo", "key"}, ""},
+	{"key=value\nfoo=bar", "f", []string{"foo"}, ""},
+	{"key=value\nfoo=bar", "fo", []string{"foo"}, ""},
+	{"key=value\nfoo=bar", "foo", []string{"foo"}, ""},
+	{"key=value\nfoo=bar", "fooo", []string{}, ""},
+	{"key=value\nkey2=value2\nfoo=bar", "ey", []string{"key", "key2"}, ""},
+	{"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}, ""},
+	{"key=value\nkey2=value2\nfoo=bar", "^key", []string{"key", "key2"}, ""},
+	{"key=value\nkey2=value2\nfoo=bar", "^(key|foo)", []string{"foo", "key", "key2"}, ""},
+	{"key=value\nkey2=value2\nfoo=bar", "[ abc", nil, "error parsing regexp.*"},
 }
 
 // ----------------------------------------------------------------------------
 
-type filterPrefixTest struct {
+var filterPrefixTests = []struct {
 	input  string
 	prefix string
 	keys   []string
+}{
+	{"", "", []string{}},
+	{"", "abc", []string{}},
+	{"key=value", "", []string{"key"}},
+	{"key=value", "key=", []string{}},
+	{"key=value\nfoo=bar", "", []string{"foo", "key"}},
+	{"key=value\nfoo=bar", "f", []string{"foo"}},
+	{"key=value\nfoo=bar", "fo", []string{"foo"}},
+	{"key=value\nfoo=bar", "foo", []string{"foo"}},
+	{"key=value\nfoo=bar", "fooo", []string{}},
+	{"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}},
 }
 
-var filterPrefixTests = []*filterPrefixTest{
-	&filterPrefixTest{"", "", []string{}},
-	&filterPrefixTest{"", "abc", []string{}},
-	&filterPrefixTest{"key=value", "", []string{"key"}},
-	&filterPrefixTest{"key=value", "key=", []string{}},
-	&filterPrefixTest{"key=value\nfoo=bar", "", []string{"foo", "key"}},
-	&filterPrefixTest{"key=value\nfoo=bar", "f", []string{"foo"}},
-	&filterPrefixTest{"key=value\nfoo=bar", "fo", []string{"foo"}},
-	&filterPrefixTest{"key=value\nfoo=bar", "foo", []string{"foo"}},
-	&filterPrefixTest{"key=value\nfoo=bar", "fooo", []string{}},
-	&filterPrefixTest{"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}},
+// ----------------------------------------------------------------------------
+
+var setTests = []struct {
+	input      string
+	key, value string
+	prev       string
+	ok         bool
+	err        string
+	keys       []string
+}{
+	{"", "", "", "", false, "", []string{}},
+	{"", "key", "value", "", false, "", []string{"key"}},
+	{"key=value", "key2", "value2", "", false, "", []string{"key", "key2"}},
+	{"key=value", "abc", "value3", "", false, "", []string{"key", "abc"}},
+	{"key=value", "key", "value3", "value", true, "", []string{"key"}},
 }
 
 // ----------------------------------------------------------------------------
@@ -362,8 +409,9 @@
 
 func (l *TestSuite) TestErrors(c *C) {
 	for _, test := range errorTests {
-		input, msg := test[0], test[1]
-		testError(c, input, msg)
+		_, err := Load([]byte(test.input), ISO_8859_1)
+		c.Assert(err, NotNil)
+		c.Assert(strings.Contains(err.Error(), test.msg), Equals, true, Commentf("Expected %q got %q", test.msg, err.Error()))
 	}
 }
 
@@ -517,6 +565,40 @@
 	c.Assert(func() { p.MustGetString("invalid") }, PanicMatches, "unknown property: invalid")
 }
 
+func (l *TestSuite) TestComment(c *C) {
+	for _, test := range commentTests {
+		p, err := parse(test.input)
+		c.Assert(err, IsNil)
+		c.Assert(p.MustGetString(test.key), Equals, test.value)
+		c.Assert(p.GetComments(test.key), DeepEquals, test.comments)
+		if test.comments != nil {
+			c.Assert(p.GetComment(test.key), Equals, test.comments[len(test.comments)-1])
+		} else {
+			c.Assert(p.GetComment(test.key), Equals, "")
+		}
+
+		// test setting comments
+		if len(test.comments) > 0 {
+			// set single comment
+			p.ClearComments()
+			c.Assert(len(p.c), Equals, 0)
+			p.SetComment(test.key, test.comments[0])
+			c.Assert(p.GetComment(test.key), Equals, test.comments[0])
+
+			// set multiple comments
+			p.ClearComments()
+			c.Assert(len(p.c), Equals, 0)
+			p.SetComments(test.key, test.comments)
+			c.Assert(p.GetComments(test.key), DeepEquals, test.comments)
+
+			// clear comments for a key
+			p.SetComments(test.key, nil)
+			c.Assert(p.GetComment(test.key), Equals, "")
+			c.Assert(p.GetComments(test.key), IsNil)
+		}
+	}
+}
+
 func (l *TestSuite) TestFilter(c *C) {
 	for _, test := range filterTests {
 		p, err := parse(test.input)
@@ -561,20 +643,43 @@
 		c.Assert(err, IsNil)
 		c.Assert(p.Len(), Equals, len(test.keys))
 		c.Assert(len(p.Keys()), Equals, len(test.keys))
-		for _, key := range test.keys {
-			_, ok := p.Get(key)
-			c.Assert(ok, Equals, true)
-		}
+		c.Assert(p.Keys(), DeepEquals, test.keys)
 	}
 }
+
+func (l *TestSuite) TestSet(c *C) {
+	for _, test := range setTests {
+		p, err := parse(test.input)
+		c.Assert(err, IsNil)
+		prev, ok, err := p.Set(test.key, test.value)
+		if test.err != "" {
+			c.Assert(err, ErrorMatches, test.err)
+			continue
+		}
+
+		c.Assert(err, IsNil)
+		c.Assert(ok, Equals, test.ok)
+		if ok {
+			c.Assert(prev, Equals, test.prev)
+		}
+		c.Assert(p.Keys(), DeepEquals, test.keys)
+	}
+}
+
+func (l *TestSuite) TestMustSet(c *C) {
+	input := "key=${key}"
+	p, err := parse(input)
+	c.Assert(err, IsNil)
+	c.Assert(func() { p.MustSet("key", "${key}") }, PanicMatches, "Circular reference .*")
+}
+
 func (l *TestSuite) TestWrite(c *C) {
 	for _, test := range writeTests {
-		input, output, enc := test[0], test[1], test[2]
-		p, err := parse(input)
+		p, err := parse(test.input)
 
 		buf := new(bytes.Buffer)
 		var n int
-		switch enc {
+		switch test.encoding {
 		case "UTF-8":
 			n, err = p.Write(buf, UTF8)
 		case "ISO-8859-1":
@@ -582,8 +687,27 @@
 		}
 		c.Assert(err, IsNil)
 		s := string(buf.Bytes())
-		c.Assert(n, Equals, len(output), Commentf("input=%q expected=%q obtained=%q", input, output, s))
-		c.Assert(s, Equals, output, Commentf("input=%q expected=%q obtained=%q", input, output, s))
+		c.Assert(n, Equals, len(test.output), Commentf("input=%q expected=%q obtained=%q", test.input, test.output, s))
+		c.Assert(s, Equals, test.output, Commentf("input=%q expected=%q obtained=%q", test.input, test.output, s))
+	}
+}
+
+func (l *TestSuite) TestWriteComment(c *C) {
+	for _, test := range writeCommentTests {
+		p, err := parse(test.input)
+
+		buf := new(bytes.Buffer)
+		var n int
+		switch test.encoding {
+		case "UTF-8":
+			n, err = p.WriteComment(buf, "# ", UTF8)
+		case "ISO-8859-1":
+			n, err = p.WriteComment(buf, "# ", ISO_8859_1)
+		}
+		c.Assert(err, IsNil)
+		s := string(buf.Bytes())
+		c.Assert(n, Equals, len(test.output), Commentf("input=%q expected=%q obtained=%q", test.input, test.output, s))
+		c.Assert(s, Equals, test.output, Commentf("input=%q expected=%q obtained=%q", test.input, test.output, s))
 	}
 }
 
@@ -655,15 +779,6 @@
 	assertKeyValues(c, input, p, keyvalues...)
 }
 
-// tests whether some input produces a given error message.
-func testError(c *C, input, msg string) {
-	printf("%q\n", input)
-
-	_, err := Load([]byte(input), ISO_8859_1)
-	c.Assert(err, NotNil)
-	c.Assert(strings.Contains(err.Error(), msg), Equals, true, Commentf("Expected %q got %q", msg, err.Error()))
-}
-
 // tests whether key/value pairs exist for a given input.
 // keyvalues is expected to be an even number of strings of "key", "value", ...
 func assertKeyValues(c *C, input string, p *Properties, keyvalues ...string) {