THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Browse Source

LDAP Public SSH Keys synchronization (#1844)

* Add LDAP Key Synchronization feature

Signed-off-by: Magnus Lindvall <magnus@dnmgns.com>

* Add migration: add login source id column for public_key table

* Only update keys if needed

* Add function to only list pubkey synchronized from ldap

* Only list pub ssh keys synchronized from ldap. Do not sort strings as ExistsInSlice does it.

* Only get keys belonging to current login source id

* Set default login source id to 0

* Some minor cleanup. Add integration tests (updete dep testify)
tags/v1.5.0-dev
Magnus Lindvall 1 year ago
parent
commit
cdb9478774

+ 4
- 2
Gopkg.lock View File

@@ -167,7 +167,8 @@
167 167
 [[projects]]
168 168
   name = "github.com/davecgh/go-spew"
169 169
   packages = ["spew"]
170
-  revision = "ecdeabc65495df2dec95d7c4a4c3e021903035e5"
170
+  revision = "346938d642f2ec3594ed81d874461961cd0faa76"
171
+  version = "v1.1.0"
171 172
 
172 173
 [[projects]]
173 174
   name = "github.com/denisenkom/go-mssqldb"
@@ -669,7 +670,8 @@
669 670
 [[projects]]
670 671
   name = "github.com/stretchr/testify"
671 672
   packages = ["assert"]
672
-  revision = "2aa2c176b9dab406a6970f6a55f513e8a8c8b18f"
673
+  revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
674
+  version = "v1.2.1"
673 675
 
674 676
 [[projects]]
675 677
   name = "github.com/syndtr/goleveldb"

+ 58
- 21
integrations/auth_ldap_test.go View File

@@ -40,7 +40,11 @@ var gitLDAPUsers = []ldapUser{
40 40
 		Password: "hermes",
41 41
 		FullName: "Conrad Hermes",
42 42
 		Email:    "hermes@planetexpress.com",
43
-		IsAdmin:  true,
43
+		SSHKeys: []string{
44
+			"SHA256:qLY06smKfHoW/92yXySpnxFR10QFrLdRjf/GNPvwcW8",
45
+			"SHA256:QlVTuM5OssDatqidn2ffY+Lc4YA5Fs78U+0KOHI51jQ",
46
+		},
47
+		IsAdmin: true,
44 48
 	},
45 49
 	{
46 50
 		UserName: "fry",
@@ -89,26 +93,27 @@ func getLDAPServerHost() string {
89 93
 	return host
90 94
 }
91 95
 
92
-func addAuthSourceLDAP(t *testing.T) {
96
+func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string) {
93 97
 	session := loginUser(t, "user1")
94 98
 	csrf := GetCSRF(t, session, "/admin/auths/new")
95 99
 	req := NewRequestWithValues(t, "POST", "/admin/auths/new", map[string]string{
96
-		"_csrf":              csrf,
97
-		"type":               "2",
98
-		"name":               "ldap",
99
-		"host":               getLDAPServerHost(),
100
-		"port":               "389",
101
-		"bind_dn":            "uid=gitea,ou=service,dc=planetexpress,dc=com",
102
-		"bind_password":      "password",
103
-		"user_base":          "ou=people,dc=planetexpress,dc=com",
104
-		"filter":             "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))",
105
-		"admin_filter":       "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)",
106
-		"attribute_username": "uid",
107
-		"attribute_name":     "givenName",
108
-		"attribute_surname":  "sn",
109
-		"attribute_mail":     "mail",
110
-		"is_sync_enabled":    "on",
111
-		"is_active":          "on",
100
+		"_csrf":                    csrf,
101
+		"type":                     "2",
102
+		"name":                     "ldap",
103
+		"host":                     getLDAPServerHost(),
104
+		"port":                     "389",
105
+		"bind_dn":                  "uid=gitea,ou=service,dc=planetexpress,dc=com",
106
+		"bind_password":            "password",
107
+		"user_base":                "ou=people,dc=planetexpress,dc=com",
108
+		"filter":                   "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))",
109
+		"admin_filter":             "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)",
110
+		"attribute_username":       "uid",
111
+		"attribute_name":           "givenName",
112
+		"attribute_surname":        "sn",
113
+		"attribute_mail":           "mail",
114
+		"attribute_ssh_public_key": sshKeyAttribute,
115
+		"is_sync_enabled":          "on",
116
+		"is_active":                "on",
112 117
 	})
113 118
 	session.MakeRequest(t, req, http.StatusFound)
114 119
 }
@@ -119,7 +124,7 @@ func TestLDAPUserSignin(t *testing.T) {
119 124
 		return
120 125
 	}
121 126
 	prepareTestEnv(t)
122
-	addAuthSourceLDAP(t)
127
+	addAuthSourceLDAP(t, "")
123 128
 
124 129
 	u := gitLDAPUsers[0]
125 130
 
@@ -140,7 +145,7 @@ func TestLDAPUserSync(t *testing.T) {
140 145
 		return
141 146
 	}
142 147
 	prepareTestEnv(t)
143
-	addAuthSourceLDAP(t)
148
+	addAuthSourceLDAP(t, "")
144 149
 	models.SyncExternalUsers()
145 150
 
146 151
 	session := loginUser(t, "user1")
@@ -186,9 +191,41 @@ func TestLDAPUserSigninFailed(t *testing.T) {
186 191
 		return
187 192
 	}
188 193
 	prepareTestEnv(t)
189
-	addAuthSourceLDAP(t)
194
+	addAuthSourceLDAP(t, "")
190 195
 
191 196
 	u := otherLDAPUsers[0]
192 197
 
193 198
 	testLoginFailed(t, u.UserName, u.Password, i18n.Tr("en", "form.username_password_incorrect"))
194 199
 }
200
+
201
+func TestLDAPUserSSHKeySync(t *testing.T) {
202
+	if skipLDAPTests() {
203
+		t.Skip()
204
+		return
205
+	}
206
+	prepareTestEnv(t)
207
+	addAuthSourceLDAP(t, "sshPublicKey")
208
+	models.SyncExternalUsers()
209
+
210
+	// Check if users has SSH keys synced
211
+	for _, u := range gitLDAPUsers {
212
+		if len(u.SSHKeys) == 0 {
213
+			continue
214
+		}
215
+		session := loginUserWithPassword(t, u.UserName, u.Password)
216
+
217
+		req := NewRequest(t, "GET", "/user/settings/keys")
218
+		resp := session.MakeRequest(t, req, http.StatusOK)
219
+
220
+		htmlDoc := NewHTMLParser(t, resp.Body)
221
+
222
+		divs := htmlDoc.doc.Find(".key.list .print.meta")
223
+
224
+		syncedKeys := make([]string, divs.Length())
225
+		for i := 0; i < divs.Length(); i++ {
226
+			syncedKeys[i] = strings.TrimSpace(divs.Eq(i).Text())
227
+		}
228
+
229
+		assert.ElementsMatch(t, u.SSHKeys, syncedKeys)
230
+	}
231
+}

+ 2
- 0
models/migrations/migrations.go View File

@@ -184,6 +184,8 @@ var migrations = []Migration{
184 184
 	NewMigration("add multiple assignees", addMultipleAssignees),
185 185
 	// v65 -> v66
186 186
 	NewMigration("add u2f", addU2FReg),
187
+	// v66 -> v67
188
+	NewMigration("add login source id column for public_key table", addLoginSourceIDToPublicKeyTable),
187 189
 }
188 190
 
189 191
 // Migrate database to current version

+ 22
- 0
models/migrations/v66.go View File

@@ -0,0 +1,22 @@
1
+// Copyright 2017 The Gitea Authors. All rights reserved.
2
+// Use of this source code is governed by a MIT-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package migrations
6
+
7
+import (
8
+	"fmt"
9
+
10
+	"github.com/go-xorm/xorm"
11
+)
12
+
13
+func addLoginSourceIDToPublicKeyTable(x *xorm.Engine) error {
14
+	type PublicKey struct {
15
+		LoginSourceID int64 `xorm:"NOT NULL DEFAULT 0"`
16
+	}
17
+
18
+	if err := x.Sync2(new(PublicKey)); err != nil {
19
+		return fmt.Errorf("Sync2: %v", err)
20
+	}
21
+	return nil
22
+}

+ 24
- 14
models/ssh_key.go View File

@@ -47,13 +47,14 @@ const (
47 47
 
48 48
 // PublicKey represents a user or deploy SSH public key.
49 49
 type PublicKey struct {
50
-	ID          int64      `xorm:"pk autoincr"`
51
-	OwnerID     int64      `xorm:"INDEX NOT NULL"`
52
-	Name        string     `xorm:"NOT NULL"`
53
-	Fingerprint string     `xorm:"NOT NULL"`
54
-	Content     string     `xorm:"TEXT NOT NULL"`
55
-	Mode        AccessMode `xorm:"NOT NULL DEFAULT 2"`
56
-	Type        KeyType    `xorm:"NOT NULL DEFAULT 1"`
50
+	ID            int64      `xorm:"pk autoincr"`
51
+	OwnerID       int64      `xorm:"INDEX NOT NULL"`
52
+	Name          string     `xorm:"NOT NULL"`
53
+	Fingerprint   string     `xorm:"NOT NULL"`
54
+	Content       string     `xorm:"TEXT NOT NULL"`
55
+	Mode          AccessMode `xorm:"NOT NULL DEFAULT 2"`
56
+	Type          KeyType    `xorm:"NOT NULL DEFAULT 1"`
57
+	LoginSourceID int64      `xorm:"NOT NULL DEFAULT 0"`
57 58
 
58 59
 	CreatedUnix       util.TimeStamp `xorm:"created"`
59 60
 	UpdatedUnix       util.TimeStamp `xorm:"updated"`
@@ -391,7 +392,7 @@ func addKey(e Engine, key *PublicKey) (err error) {
391 392
 }
392 393
 
393 394
 // AddPublicKey adds new public key to database and authorized_keys file.
394
-func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
395
+func AddPublicKey(ownerID int64, name, content string, LoginSourceID int64) (*PublicKey, error) {
395 396
 	log.Trace(content)
396 397
 
397 398
 	fingerprint, err := calcFingerprint(content)
@@ -420,12 +421,13 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
420 421
 	}
421 422
 
422 423
 	key := &PublicKey{
423
-		OwnerID:     ownerID,
424
-		Name:        name,
425
-		Fingerprint: fingerprint,
426
-		Content:     content,
427
-		Mode:        AccessModeWrite,
428
-		Type:        KeyTypeUser,
424
+		OwnerID:       ownerID,
425
+		Name:          name,
426
+		Fingerprint:   fingerprint,
427
+		Content:       content,
428
+		Mode:          AccessModeWrite,
429
+		Type:          KeyTypeUser,
430
+		LoginSourceID: LoginSourceID,
429 431
 	}
430 432
 	if err = addKey(sess, key); err != nil {
431 433
 		return nil, fmt.Errorf("addKey: %v", err)
@@ -471,6 +473,14 @@ func ListPublicKeys(uid int64) ([]*PublicKey, error) {
471 473
 		Find(&keys)
472 474
 }
473 475
 
476
+// ListPublicLdapSSHKeys returns a list of synchronized public ldap ssh keys belongs to given user and login source.
477
+func ListPublicLdapSSHKeys(uid int64, LoginSourceID int64) ([]*PublicKey, error) {
478
+	keys := make([]*PublicKey, 0, 5)
479
+	return keys, x.
480
+		Where("owner_id = ? AND login_source_id = ?", uid, LoginSourceID).
481
+		Find(&keys)
482
+}
483
+
474 484
 // UpdatePublicKeyUpdated updates public key use time.
475 485
 func UpdatePublicKeyUpdated(id int64) error {
476 486
 	// Check if key exists before update as affected rows count is unreliable

+ 133
- 1
models/user.go View File

@@ -1356,6 +1356,119 @@ func GetWatchedRepos(userID int64, private bool) ([]*Repository, error) {
1356 1356
 	return repos, nil
1357 1357
 }
1358 1358
 
1359
+// deleteKeysMarkedForDeletion returns true if ssh keys needs update
1360
+func deleteKeysMarkedForDeletion(keys []string) (bool, error) {
1361
+	// Start session
1362
+	sess := x.NewSession()
1363
+	defer sess.Close()
1364
+	if err := sess.Begin(); err != nil {
1365
+		return false, err
1366
+	}
1367
+
1368
+	// Delete keys marked for deletion
1369
+	var sshKeysNeedUpdate bool
1370
+	for _, KeyToDelete := range keys {
1371
+		key, err := SearchPublicKeyByContent(KeyToDelete)
1372
+		if err != nil {
1373
+			log.Error(4, "SearchPublicKeyByContent: %v", err)
1374
+			continue
1375
+		}
1376
+		if err = deletePublicKeys(sess, key.ID); err != nil {
1377
+			log.Error(4, "deletePublicKeys: %v", err)
1378
+			continue
1379
+		}
1380
+		sshKeysNeedUpdate = true
1381
+	}
1382
+
1383
+	if err := sess.Commit(); err != nil {
1384
+		return false, err
1385
+	}
1386
+
1387
+	return sshKeysNeedUpdate, nil
1388
+}
1389
+
1390
+func addLdapSSHPublicKeys(s *LoginSource, usr *User, SSHPublicKeys []string) bool {
1391
+	var sshKeysNeedUpdate bool
1392
+	for _, sshKey := range SSHPublicKeys {
1393
+		if strings.HasPrefix(strings.ToLower(sshKey), "ssh") {
1394
+			sshKeyName := fmt.Sprintf("%s-%s", s.Name, sshKey[0:40])
1395
+			if _, err := AddPublicKey(usr.ID, sshKeyName, sshKey, s.ID); err != nil {
1396
+				log.Error(4, "addLdapSSHPublicKeys[%s]: Error adding LDAP Public SSH Key for user %s: %v", s.Name, usr.Name, err)
1397
+			} else {
1398
+				log.Trace("addLdapSSHPublicKeys[%s]: Added LDAP Public SSH Key for user %s", s.Name, usr.Name)
1399
+				sshKeysNeedUpdate = true
1400
+			}
1401
+		} else {
1402
+			log.Warn("addLdapSSHPublicKeys[%s]: Skipping invalid LDAP Public SSH Key for user %s: %v", s.Name, usr.Name, sshKey)
1403
+		}
1404
+	}
1405
+	return sshKeysNeedUpdate
1406
+}
1407
+
1408
+func synchronizeLdapSSHPublicKeys(s *LoginSource, SSHPublicKeys []string, usr *User) bool {
1409
+	var sshKeysNeedUpdate bool
1410
+
1411
+	log.Trace("synchronizeLdapSSHPublicKeys[%s]: Handling LDAP Public SSH Key synchronization for user %s", s.Name, usr.Name)
1412
+
1413
+	// Get Public Keys from DB with current LDAP source
1414
+	var giteaKeys []string
1415
+	keys, err := ListPublicLdapSSHKeys(usr.ID, s.ID)
1416
+	if err != nil {
1417
+		log.Error(4, "synchronizeLdapSSHPublicKeys[%s]: Error listing LDAP Public SSH Keys for user %s: %v", s.Name, usr.Name, err)
1418
+	}
1419
+
1420
+	for _, v := range keys {
1421
+		giteaKeys = append(giteaKeys, v.OmitEmail())
1422
+	}
1423
+
1424
+	// Get Public Keys from LDAP and skip duplicate keys
1425
+	var ldapKeys []string
1426
+	for _, v := range SSHPublicKeys {
1427
+		ldapKey := strings.Join(strings.Split(v, " ")[:2], " ")
1428
+		if !util.ExistsInSlice(ldapKey, ldapKeys) {
1429
+			ldapKeys = append(ldapKeys, ldapKey)
1430
+		}
1431
+	}
1432
+
1433
+	// Check if Public Key sync is needed
1434
+	if util.IsEqualSlice(giteaKeys, ldapKeys) {
1435
+		log.Trace("synchronizeLdapSSHPublicKeys[%s]: LDAP Public Keys are already in sync for %s (LDAP:%v/DB:%v)", s.Name, usr.Name, len(ldapKeys), len(giteaKeys))
1436
+		return false
1437
+	}
1438
+	log.Trace("synchronizeLdapSSHPublicKeys[%s]: LDAP Public Key needs update for user %s (LDAP:%v/DB:%v)", s.Name, usr.Name, len(ldapKeys), len(giteaKeys))
1439
+
1440
+	// Add LDAP Public SSH Keys that doesn't already exist in DB
1441
+	var newLdapSSHKeys []string
1442
+	for _, LDAPPublicSSHKey := range ldapKeys {
1443
+		if !util.ExistsInSlice(LDAPPublicSSHKey, giteaKeys) {
1444
+			newLdapSSHKeys = append(newLdapSSHKeys, LDAPPublicSSHKey)
1445
+		}
1446
+	}
1447
+	if addLdapSSHPublicKeys(s, usr, newLdapSSHKeys) {
1448
+		sshKeysNeedUpdate = true
1449
+	}
1450
+
1451
+	// Mark LDAP keys from DB that doesn't exist in LDAP for deletion
1452
+	var giteaKeysToDelete []string
1453
+	for _, giteaKey := range giteaKeys {
1454
+		if !util.ExistsInSlice(giteaKey, ldapKeys) {
1455
+			log.Trace("synchronizeLdapSSHPublicKeys[%s]: Marking LDAP Public SSH Key for deletion for user %s: %v", s.Name, usr.Name, giteaKey)
1456
+			giteaKeysToDelete = append(giteaKeysToDelete, giteaKey)
1457
+		}
1458
+	}
1459
+
1460
+	// Delete LDAP keys from DB that doesn't exist in LDAP
1461
+	needUpd, err := deleteKeysMarkedForDeletion(giteaKeysToDelete)
1462
+	if err != nil {
1463
+		log.Error(4, "synchronizeLdapSSHPublicKeys[%s]: Error deleting LDAP Public SSH Keys marked for deletion for user %s: %v", s.Name, usr.Name, err)
1464
+	}
1465
+	if needUpd {
1466
+		sshKeysNeedUpdate = true
1467
+	}
1468
+
1469
+	return sshKeysNeedUpdate
1470
+}
1471
+
1359 1472
 // SyncExternalUsers is used to synchronize users with external authorization source
1360 1473
 func SyncExternalUsers() {
1361 1474
 	if !taskStatusTable.StartIfNotRunning(syncExternalUsers) {
@@ -1377,10 +1490,13 @@ func SyncExternalUsers() {
1377 1490
 		if !s.IsActived || !s.IsSyncEnabled {
1378 1491
 			continue
1379 1492
 		}
1493
+
1380 1494
 		if s.IsLDAP() {
1381 1495
 			log.Trace("Doing: SyncExternalUsers[%s]", s.Name)
1382 1496
 
1383 1497
 			var existingUsers []int64
1498
+			var isAttributeSSHPublicKeySet = len(strings.TrimSpace(s.LDAP().AttributeSSHPublicKey)) > 0
1499
+			var sshKeysNeedUpdate bool
1384 1500
 
1385 1501
 			// Find all users with this login type
1386 1502
 			var users []User
@@ -1389,7 +1505,6 @@ func SyncExternalUsers() {
1389 1505
 				Find(&users)
1390 1506
 
1391 1507
 			sr := s.LDAP().SearchEntries()
1392
-
1393 1508
 			for _, su := range sr {
1394 1509
 				if len(su.Username) == 0 {
1395 1510
 					continue
@@ -1426,11 +1541,23 @@ func SyncExternalUsers() {
1426 1541
 					}
1427 1542
 
1428 1543
 					err = CreateUser(usr)
1544
+
1429 1545
 					if err != nil {
1430 1546
 						log.Error(4, "SyncExternalUsers[%s]: Error creating user %s: %v", s.Name, su.Username, err)
1547
+					} else if isAttributeSSHPublicKeySet {
1548
+						log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", s.Name, usr.Name)
1549
+						if addLdapSSHPublicKeys(s, usr, su.SSHPublicKey) {
1550
+							sshKeysNeedUpdate = true
1551
+						}
1431 1552
 					}
1432 1553
 				} else if updateExisting {
1433 1554
 					existingUsers = append(existingUsers, usr.ID)
1555
+
1556
+					// Synchronize SSH Public Key if that attribute is set
1557
+					if isAttributeSSHPublicKeySet && synchronizeLdapSSHPublicKeys(s, su.SSHPublicKey, usr) {
1558
+						sshKeysNeedUpdate = true
1559
+					}
1560
+
1434 1561
 					// Check if user data has changed
1435 1562
 					if (len(s.LDAP().AdminFilter) > 0 && usr.IsAdmin != su.IsAdmin) ||
1436 1563
 						strings.ToLower(usr.Email) != strings.ToLower(su.Mail) ||
@@ -1455,6 +1582,11 @@ func SyncExternalUsers() {
1455 1582
 				}
1456 1583
 			}
1457 1584
 
1585
+			// Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed
1586
+			if sshKeysNeedUpdate {
1587
+				RewriteAllPublicKeys()
1588
+			}
1589
+
1458 1590
 			// Deactivate users not present in LDAP
1459 1591
 			if updateExisting {
1460 1592
 				for _, usr := range users {

+ 1
- 0
modules/auth/auth_form.go View File

@@ -24,6 +24,7 @@ type AuthenticationForm struct {
24 24
 	AttributeName                 string
25 25
 	AttributeSurname              string
26 26
 	AttributeMail                 string
27
+	AttributeSSHPublicKey         string
27 28
 	AttributesInBind              bool
28 29
 	UsePagedSearch                bool
29 30
 	SearchPageSize                int

+ 33
- 30
modules/auth/ldap/ldap.go View File

@@ -28,33 +28,35 @@ const (
28 28
 
29 29
 // Source Basic LDAP authentication service
30 30
 type Source struct {
31
-	Name              string // canonical name (ie. corporate.ad)
32
-	Host              string // LDAP host
33
-	Port              int    // port number
34
-	SecurityProtocol  SecurityProtocol
35
-	SkipVerify        bool
36
-	BindDN            string // DN to bind with
37
-	BindPassword      string // Bind DN password
38
-	UserBase          string // Base search path for users
39
-	UserDN            string // Template for the DN of the user for simple auth
40
-	AttributeUsername string // Username attribute
41
-	AttributeName     string // First name attribute
42
-	AttributeSurname  string // Surname attribute
43
-	AttributeMail     string // E-mail attribute
44
-	AttributesInBind  bool   // fetch attributes in bind context (not user)
45
-	SearchPageSize    uint32 // Search with paging page size
46
-	Filter            string // Query filter to validate entry
47
-	AdminFilter       string // Query filter to check if user is admin
48
-	Enabled           bool   // if this source is disabled
31
+	Name                  string // canonical name (ie. corporate.ad)
32
+	Host                  string // LDAP host
33
+	Port                  int    // port number
34
+	SecurityProtocol      SecurityProtocol
35
+	SkipVerify            bool
36
+	BindDN                string // DN to bind with
37
+	BindPassword          string // Bind DN password
38
+	UserBase              string // Base search path for users
39
+	UserDN                string // Template for the DN of the user for simple auth
40
+	AttributeUsername     string // Username attribute
41
+	AttributeName         string // First name attribute
42
+	AttributeSurname      string // Surname attribute
43
+	AttributeMail         string // E-mail attribute
44
+	AttributesInBind      bool   // fetch attributes in bind context (not user)
45
+	AttributeSSHPublicKey string // LDAP SSH Public Key attribute
46
+	SearchPageSize        uint32 // Search with paging page size
47
+	Filter                string // Query filter to validate entry
48
+	AdminFilter           string // Query filter to check if user is admin
49
+	Enabled               bool   // if this source is disabled
49 50
 }
50 51
 
51 52
 // SearchResult : user data
52 53
 type SearchResult struct {
53
-	Username string // Username
54
-	Name     string // Name
55
-	Surname  string // Surname
56
-	Mail     string // E-mail address
57
-	IsAdmin  bool   // if user is administrator
54
+	Username     string   // Username
55
+	Name         string   // Name
56
+	Surname      string   // Surname
57
+	Mail         string   // E-mail address
58
+	SSHPublicKey []string // SSH Public Key
59
+	IsAdmin      bool     // if user is administrator
58 60
 }
59 61
 
60 62
 func (ls *Source) sanitizedUserQuery(username string) (string, bool) {
@@ -298,10 +300,10 @@ func (ls *Source) SearchEntries() []*SearchResult {
298 300
 
299 301
 	userFilter := fmt.Sprintf(ls.Filter, "*")
300 302
 
301
-	log.Trace("Fetching attributes '%v', '%v', '%v', '%v' with filter %s and base %s", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, userFilter, ls.UserBase)
303
+	log.Trace("Fetching attributes '%v', '%v', '%v', '%v', '%v' with filter %s and base %s", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.AttributeSSHPublicKey, userFilter, ls.UserBase)
302 304
 	search := ldap.NewSearchRequest(
303 305
 		ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
304
-		[]string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail},
306
+		[]string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.AttributeSSHPublicKey},
305 307
 		nil)
306 308
 
307 309
 	var sr *ldap.SearchResult
@@ -319,11 +321,12 @@ func (ls *Source) SearchEntries() []*SearchResult {
319 321
 
320 322
 	for i, v := range sr.Entries {
321 323
 		result[i] = &SearchResult{
322
-			Username: v.GetAttributeValue(ls.AttributeUsername),
323
-			Name:     v.GetAttributeValue(ls.AttributeName),
324
-			Surname:  v.GetAttributeValue(ls.AttributeSurname),
325
-			Mail:     v.GetAttributeValue(ls.AttributeMail),
326
-			IsAdmin:  checkAdmin(l, ls, v.DN),
324
+			Username:     v.GetAttributeValue(ls.AttributeUsername),
325
+			Name:         v.GetAttributeValue(ls.AttributeName),
326
+			Surname:      v.GetAttributeValue(ls.AttributeSurname),
327
+			Mail:         v.GetAttributeValue(ls.AttributeMail),
328
+			SSHPublicKey: v.GetAttributeValues(ls.AttributeSSHPublicKey),
329
+			IsAdmin:      checkAdmin(l, ls, v.DN),
327 330
 		}
328 331
 	}
329 332
 

+ 29
- 0
modules/util/compare.go View File

@@ -27,3 +27,32 @@ func IsSliceInt64Eq(a, b []int64) bool {
27 27
 	}
28 28
 	return true
29 29
 }
30
+
31
+// ExistsInSlice returns true if string exists in slice.
32
+func ExistsInSlice(target string, slice []string) bool {
33
+	i := sort.Search(len(slice),
34
+		func(i int) bool { return slice[i] == target })
35
+	return i < len(slice)
36
+}
37
+
38
+// IsEqualSlice returns true if slices are equal.
39
+func IsEqualSlice(target []string, source []string) bool {
40
+	if len(target) != len(source) {
41
+		return false
42
+	}
43
+
44
+	if (target == nil) != (source == nil) {
45
+		return false
46
+	}
47
+
48
+	sort.Strings(target)
49
+	sort.Strings(source)
50
+
51
+	for i, v := range target {
52
+		if v != source[i] {
53
+			return false
54
+		}
55
+	}
56
+
57
+	return true
58
+}

+ 1
- 0
options/locale/locale_en-US.ini View File

@@ -1390,6 +1390,7 @@ auths.attribute_username_placeholder = Leave empty to use the username entered i
1390 1390
 auths.attribute_name = First Name Attribute
1391 1391
 auths.attribute_surname = Surname Attribute
1392 1392
 auths.attribute_mail = Email Attribute
1393
+auths.attribute_ssh_public_key = Public SSH Key Attribute
1393 1394
 auths.attributes_in_bind = Fetch Attributes in Bind DN Context
1394 1395
 auths.use_paged_search = Use Paged Search
1395 1396
 auths.search_page_size = Page Size

+ 19
- 18
routers/admin/auths.go View File

@@ -97,24 +97,25 @@ func parseLDAPConfig(form auth.AuthenticationForm) *models.LDAPConfig {
97 97
 	}
98 98
 	return &models.LDAPConfig{
99 99
 		Source: &ldap.Source{
100
-			Name:              form.Name,
101
-			Host:              form.Host,
102
-			Port:              form.Port,
103
-			SecurityProtocol:  ldap.SecurityProtocol(form.SecurityProtocol),
104
-			SkipVerify:        form.SkipVerify,
105
-			BindDN:            form.BindDN,
106
-			UserDN:            form.UserDN,
107
-			BindPassword:      form.BindPassword,
108
-			UserBase:          form.UserBase,
109
-			AttributeUsername: form.AttributeUsername,
110
-			AttributeName:     form.AttributeName,
111
-			AttributeSurname:  form.AttributeSurname,
112
-			AttributeMail:     form.AttributeMail,
113
-			AttributesInBind:  form.AttributesInBind,
114
-			SearchPageSize:    pageSize,
115
-			Filter:            form.Filter,
116
-			AdminFilter:       form.AdminFilter,
117
-			Enabled:           true,
100
+			Name:                  form.Name,
101
+			Host:                  form.Host,
102
+			Port:                  form.Port,
103
+			SecurityProtocol:      ldap.SecurityProtocol(form.SecurityProtocol),
104
+			SkipVerify:            form.SkipVerify,
105
+			BindDN:                form.BindDN,
106
+			UserDN:                form.UserDN,
107
+			BindPassword:          form.BindPassword,
108
+			UserBase:              form.UserBase,
109
+			AttributeUsername:     form.AttributeUsername,
110
+			AttributeName:         form.AttributeName,
111
+			AttributeSurname:      form.AttributeSurname,
112
+			AttributeMail:         form.AttributeMail,
113
+			AttributesInBind:      form.AttributesInBind,
114
+			AttributeSSHPublicKey: form.AttributeSSHPublicKey,
115
+			SearchPageSize:        pageSize,
116
+			Filter:                form.Filter,
117
+			AdminFilter:           form.AdminFilter,
118
+			Enabled:               true,
118 119
 		},
119 120
 	}
120 121
 }

+ 1
- 1
routers/api/v1/user/key.go View File

@@ -129,7 +129,7 @@ func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid
129 129
 		return
130 130
 	}
131 131
 
132
-	key, err := models.AddPublicKey(uid, form.Title, content)
132
+	key, err := models.AddPublicKey(uid, form.Title, content, 0)
133 133
 	if err != nil {
134 134
 		repo.HandleAddKeyError(ctx, err)
135 135
 		return

+ 1
- 1
routers/user/setting/keys.go View File

@@ -99,7 +99,7 @@ func KeysPost(ctx *context.Context, form auth.AddKeyForm) {
99 99
 			return
100 100
 		}
101 101
 
102
-		if _, err = models.AddPublicKey(ctx.User.ID, form.Title, content); err != nil {
102
+		if _, err = models.AddPublicKey(ctx.User.ID, form.Title, content, 0); err != nil {
103 103
 			ctx.Data["HasSSHError"] = true
104 104
 			switch {
105 105
 			case models.IsErrKeyAlreadyExist(err):

+ 4
- 0
templates/admin/auth/edit.tmpl View File

@@ -90,6 +90,10 @@
90 90
 						<label for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label>
91 91
 						<input id="attribute_mail" name="attribute_mail" value="{{$cfg.AttributeMail}}" placeholder="e.g. mail" required>
92 92
 					</div>
93
+					<div class="field">
94
+					    <label for="attribute_ssh_public_key">{{.i18n.Tr "admin.auths.attribute_ssh_public_key"}}</label>
95
+					    <input id="attribute_ssh_public_key" name="attribute_ssh_public_key" value="{{$cfg.AttributeSSHPublicKey}}" placeholder="e.g. SshPublicKey">
96
+					</div>
93 97
 					{{if .Source.IsLDAP}}
94 98
 						<div class="inline field">
95 99
 							<div class="ui checkbox">

+ 4
- 0
templates/admin/auth/source/ldap.tmpl View File

@@ -62,6 +62,10 @@
62 62
 		<label for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label>
63 63
 		<input id="attribute_mail" name="attribute_mail" value="{{.attribute_mail}}" placeholder="e.g. mail">
64 64
 	</div>
65
+	<div class="field">
66
+	    <label for="attribute_ssh_public_key">{{.i18n.Tr "admin.auths.attribute_ssh_public_key"}}</label>
67
+	    <input id="attribute_ssh_public_key" name="attribute_ssh_public_key" value="{{.attribute_ssh_public_key}}" placeholder="e.g. SshPublicKey">
68
+	</div>
65 69
 	<div class="ldap inline field {{if not (eq .type 2)}}hide{{end}}">
66 70
 		<div class="ui checkbox">
67 71
 			<label for="use_paged_search"><strong>{{.i18n.Tr "admin.auths.use_paged_search"}}</strong></label>

+ 1
- 1
vendor/github.com/davecgh/go-spew/LICENSE View File

@@ -2,7 +2,7 @@ ISC License
2 2
 
3 3
 Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
4 4
 
5
-Permission to use, copy, modify, and/or distribute this software for any
5
+Permission to use, copy, modify, and distribute this software for any
6 6
 purpose with or without fee is hereby granted, provided that the above
7 7
 copyright notice and this permission notice appear in all copies.
8 8
 

+ 3
- 3
vendor/github.com/davecgh/go-spew/spew/bypass.go View File

@@ -41,9 +41,9 @@ var (
41 41
 	// after commit 82f48826c6c7 which changed the format again to mirror
42 42
 	// the original format.  Code in the init function updates these offsets
43 43
 	// as necessary.
44
-	offsetPtr    = ptrSize
44
+	offsetPtr    = uintptr(ptrSize)
45 45
 	offsetScalar = uintptr(0)
46
-	offsetFlag   = ptrSize * 2
46
+	offsetFlag   = uintptr(ptrSize * 2)
47 47
 
48 48
 	// flagKindWidth and flagKindShift indicate various bits that the
49 49
 	// reflect package uses internally to track kind information.
@@ -58,7 +58,7 @@ var (
58 58
 	// changed their positions.  Code in the init function updates these
59 59
 	// flags as necessary.
60 60
 	flagKindWidth = uintptr(5)
61
-	flagKindShift = flagKindWidth - 1
61
+	flagKindShift = uintptr(flagKindWidth - 1)
62 62
 	flagRO        = uintptr(1 << 0)
63 63
 	flagIndir     = uintptr(1 << 1)
64 64
 )

+ 1
- 1
vendor/github.com/davecgh/go-spew/spew/common.go View File

@@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
180 180
 	w.Write(closeParenBytes)
181 181
 }
182 182
 
183
-// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
183
+// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
184 184
 // prefix to Writer w.
185 185
 func printHexPtr(w io.Writer, p uintptr) {
186 186
 	// Null pointer.

+ 5
- 5
vendor/github.com/davecgh/go-spew/spew/dump.go View File

@@ -35,16 +35,16 @@ var (
35 35
 
36 36
 	// cCharRE is a regular expression that matches a cgo char.
37 37
 	// It is used to detect character arrays to hexdump them.
38
-	cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
38
+	cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
39 39
 
40 40
 	// cUnsignedCharRE is a regular expression that matches a cgo unsigned
41 41
 	// char.  It is used to detect unsigned character arrays to hexdump
42 42
 	// them.
43
-	cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
43
+	cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
44 44
 
45 45
 	// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
46 46
 	// It is used to detect uint8_t arrays to hexdump them.
47
-	cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
47
+	cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
48 48
 )
49 49
 
50 50
 // dumpState contains information about the state of a dump operation.
@@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
143 143
 	// Display dereferenced value.
144 144
 	d.w.Write(openParenBytes)
145 145
 	switch {
146
-	case nilFound:
146
+	case nilFound == true:
147 147
 		d.w.Write(nilAngleBytes)
148 148
 
149
-	case cycleFound:
149
+	case cycleFound == true:
150 150
 		d.w.Write(circularBytes)
151 151
 
152 152
 	default:

+ 2
- 2
vendor/github.com/davecgh/go-spew/spew/format.go View File

@@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
182 182
 
183 183
 	// Display dereferenced value.
184 184
 	switch {
185
-	case nilFound:
185
+	case nilFound == true:
186 186
 		f.fs.Write(nilAngleBytes)
187 187
 
188
-	case cycleFound:
188
+	case cycleFound == true:
189 189
 		f.fs.Write(circularShortBytes)
190 190
 
191 191
 	default:

+ 0
- 22
vendor/github.com/stretchr/testify/LICENCE.txt View File

@@ -1,22 +0,0 @@
1
-Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
2
-
3
-Please consider promoting this project if you find it useful.
4
-
5
-Permission is hereby granted, free of charge, to any person 
6
-obtaining a copy of this software and associated documentation 
7
-files (the "Software"), to deal in the Software without restriction, 
8
-including without limitation the rights to use, copy, modify, merge, 
9
-publish, distribute, sublicense, and/or sell copies of the Software, 
10
-and to permit persons to whom the Software is furnished to do so, 
11
-subject to the following conditions:
12
-
13
-The above copyright notice and this permission notice shall be included
14
-in all copies or substantial portions of the Software.
15
-
16
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
17
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
18
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
19
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
20
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 
21
-OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
22
-OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 37
- 67
vendor/github.com/stretchr/testify/assert/assertion_format.go View File

@@ -22,18 +22,28 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo
22 22
 //    assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
23 23
 //    assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
24 24
 //    assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
25
-//
26
-// Returns whether the assertion was successful (true) or not (false).
27 25
 func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
28 26
 	return Contains(t, s, contains, append([]interface{}{msg}, args...)...)
29 27
 }
30 28
 
29
+// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
30
+func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
31
+	return DirExists(t, path, append([]interface{}{msg}, args...)...)
32
+}
33
+
34
+// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
35
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
36
+// the number of appearances of each of them in both lists should match.
37
+//
38
+// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
39
+func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
40
+	return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
41
+}
42
+
31 43
 // Emptyf asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
32 44
 // a slice or a channel with len == 0.
33 45
 //
34 46
 //  assert.Emptyf(t, obj, "error message %s", "formatted")
35
-//
36
-// Returns whether the assertion was successful (true) or not (false).
37 47
 func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
38 48
 	return Empty(t, object, append([]interface{}{msg}, args...)...)
39 49
 }
@@ -42,8 +52,6 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo
42 52
 //
43 53
 //    assert.Equalf(t, 123, 123, "error message %s", "formatted")
44 54
 //
45
-// Returns whether the assertion was successful (true) or not (false).
46
-//
47 55
 // Pointer variable equality is determined based on the equality of the
48 56
 // referenced values (as opposed to the memory addresses). Function equality
49 57
 // cannot be determined and will always fail.
@@ -56,8 +64,6 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar
56 64
 //
57 65
 //   actualObj, err := SomeFunction()
58 66
 //   assert.EqualErrorf(t, err,  expectedErrorString, "error message %s", "formatted")
59
-//
60
-// Returns whether the assertion was successful (true) or not (false).
61 67
 func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
62 68
 	return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
63 69
 }
@@ -66,8 +72,6 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
66 72
 // and equal.
67 73
 //
68 74
 //    assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
69
-//
70
-// Returns whether the assertion was successful (true) or not (false).
71 75
 func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
72 76
 	return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
73 77
 }
@@ -78,17 +82,13 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri
78 82
 //   if assert.Errorf(t, err, "error message %s", "formatted") {
79 83
 // 	   assert.Equal(t, expectedErrorf, err)
80 84
 //   }
81
-//
82
-// Returns whether the assertion was successful (true) or not (false).
83 85
 func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
84 86
 	return Error(t, err, append([]interface{}{msg}, args...)...)
85 87
 }
86 88
 
87
-// Exactlyf asserts that two objects are equal is value and type.
89
+// Exactlyf asserts that two objects are equal in value and type.
88 90
 //
89 91
 //    assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
90
-//
91
-// Returns whether the assertion was successful (true) or not (false).
92 92
 func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
93 93
 	return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...)
94 94
 }
@@ -106,20 +106,23 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}
106 106
 // Falsef asserts that the specified value is false.
107 107
 //
108 108
 //    assert.Falsef(t, myBool, "error message %s", "formatted")
109
-//
110
-// Returns whether the assertion was successful (true) or not (false).
111 109
 func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
112 110
 	return False(t, value, append([]interface{}{msg}, args...)...)
113 111
 }
114 112
 
113
+// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
114
+func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
115
+	return FileExists(t, path, append([]interface{}{msg}, args...)...)
116
+}
117
+
115 118
 // HTTPBodyContainsf asserts that a specified handler returns a
116 119
 // body that contains a string.
117 120
 //
118 121
 //  assert.HTTPBodyContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
119 122
 //
120 123
 // Returns whether the assertion was successful (true) or not (false).
121
-func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
122
-	return HTTPBodyContains(t, handler, method, url, values, str)
124
+func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
125
+	return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
123 126
 }
124 127
 
125 128
 // HTTPBodyNotContainsf asserts that a specified handler returns a
@@ -128,8 +131,8 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url
128 131
 //  assert.HTTPBodyNotContainsf(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
129 132
 //
130 133
 // Returns whether the assertion was successful (true) or not (false).
131
-func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
132
-	return HTTPBodyNotContains(t, handler, method, url, values, str)
134
+func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
135
+	return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
133 136
 }
134 137
 
135 138
 // HTTPErrorf asserts that a specified handler returns an error status code.
@@ -137,8 +140,8 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u
137 140
 //  assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
138 141
 //
139 142
 // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
140
-func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) bool {
141
-	return HTTPError(t, handler, method, url, values)
143
+func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
144
+	return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
142 145
 }
143 146
 
144 147
 // HTTPRedirectf asserts that a specified handler returns a redirect status code.
@@ -146,8 +149,8 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string,
146 149
 //  assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
147 150
 //
148 151
 // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
149
-func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) bool {
150
-	return HTTPRedirect(t, handler, method, url, values)
152
+func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
153
+	return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
151 154
 }
152 155
 
153 156
 // HTTPSuccessf asserts that a specified handler returns a success status code.
@@ -155,8 +158,8 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
155 158
 //  assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
156 159
 //
157 160
 // Returns whether the assertion was successful (true) or not (false).
158
-func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) bool {
159
-	return HTTPSuccess(t, handler, method, url, values)
161
+func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
162
+	return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
160 163
 }
161 164
 
162 165
 // Implementsf asserts that an object is implemented by the specified interface.
@@ -169,20 +172,21 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms
169 172
 // InDeltaf asserts that the two numerals are within delta of each other.
170 173
 //
171 174
 // 	 assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
172
-//
173
-// Returns whether the assertion was successful (true) or not (false).
174 175
 func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
175 176
 	return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
176 177
 }
177 178
 
179
+// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
180
+func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
181
+	return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
182
+}
183
+
178 184
 // InDeltaSlicef is the same as InDelta, except it compares two slices.
179 185
 func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
180 186
 	return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
181 187
 }
182 188
 
183 189
 // InEpsilonf asserts that expected and actual have a relative error less than epsilon
184
-//
185
-// Returns whether the assertion was successful (true) or not (false).
186 190
 func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
187 191
 	return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
188 192
 }
@@ -200,8 +204,6 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin
200 204
 // JSONEqf asserts that two JSON strings are equivalent.
201 205
 //
202 206
 //  assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
203
-//
204
-// Returns whether the assertion was successful (true) or not (false).
205 207
 func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
206 208
 	return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
207 209
 }
@@ -210,8 +212,6 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int
210 212
 // Lenf also fails if the object has a type that len() not accept.
211 213
 //
212 214
 //    assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
213
-//
214
-// Returns whether the assertion was successful (true) or not (false).
215 215
 func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
216 216
 	return Len(t, object, length, append([]interface{}{msg}, args...)...)
217 217
 }
@@ -219,8 +219,6 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf
219 219
 // Nilf asserts that the specified object is nil.
220 220
 //
221 221
 //    assert.Nilf(t, err, "error message %s", "formatted")
222
-//
223
-// Returns whether the assertion was successful (true) or not (false).
224 222
 func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
225 223
 	return Nil(t, object, append([]interface{}{msg}, args...)...)
226 224
 }
@@ -231,8 +229,6 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool
231 229
 //   if assert.NoErrorf(t, err, "error message %s", "formatted") {
232 230
 // 	   assert.Equal(t, expectedObj, actualObj)
233 231
 //   }
234
-//
235
-// Returns whether the assertion was successful (true) or not (false).
236 232
 func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
237 233
 	return NoError(t, err, append([]interface{}{msg}, args...)...)
238 234
 }
@@ -243,8 +239,6 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
243 239
 //    assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
244 240
 //    assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
245 241
 //    assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
246
-//
247
-// Returns whether the assertion was successful (true) or not (false).
248 242
 func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
249 243
 	return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
250 244
 }
@@ -255,8 +249,6 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a
255 249
 //  if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
256 250
 //    assert.Equal(t, "two", obj[1])
257 251
 //  }
258
-//
259
-// Returns whether the assertion was successful (true) or not (false).
260 252
 func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
261 253
 	return NotEmpty(t, object, append([]interface{}{msg}, args...)...)
262 254
 }
@@ -265,8 +257,6 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{})
265 257
 //
266 258
 //    assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
267 259
 //
268
-// Returns whether the assertion was successful (true) or not (false).
269
-//
270 260
 // Pointer variable equality is determined based on the equality of the
271 261
 // referenced values (as opposed to the memory addresses).
272 262
 func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
@@ -276,8 +266,6 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string,
276 266
 // NotNilf asserts that the specified object is not nil.
277 267
 //
278 268
 //    assert.NotNilf(t, err, "error message %s", "formatted")
279
-//
280
-// Returns whether the assertion was successful (true) or not (false).
281 269
 func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
282 270
 	return NotNil(t, object, append([]interface{}{msg}, args...)...)
283 271
 }
@@ -285,8 +273,6 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo
285 273
 // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
286 274
 //
287 275
 //   assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
288
-//
289
-// Returns whether the assertion was successful (true) or not (false).
290 276
 func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
291 277
 	return NotPanics(t, f, append([]interface{}{msg}, args...)...)
292 278
 }
@@ -295,8 +281,6 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo
295 281
 //
296 282
 //  assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
297 283
 //  assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
298
-//
299
-// Returns whether the assertion was successful (true) or not (false).
300 284
 func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
301 285
 	return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...)
302 286
 }
@@ -305,13 +289,11 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ..
305 289
 // elements given in the specified subset(array, slice...).
306 290
 //
307 291
 //    assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
308
-//
309
-// Returns whether the assertion was successful (true) or not (false).
310 292
 func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
311 293
 	return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...)
312 294
 }
313 295
 
314
-// NotZerof asserts that i is not the zero value for its type and returns the truth.
296
+// NotZerof asserts that i is not the zero value for its type.
315 297
 func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
316 298
 	return NotZero(t, i, append([]interface{}{msg}, args...)...)
317 299
 }
@@ -319,8 +301,6 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
319 301
 // Panicsf asserts that the code inside the specified PanicTestFunc panics.
320 302
 //
321 303
 //   assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
322
-//
323
-// Returns whether the assertion was successful (true) or not (false).
324 304
 func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
325 305
 	return Panics(t, f, append([]interface{}{msg}, args...)...)
326 306
 }
@@ -329,8 +309,6 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool
329 309
 // the recovered panic value equals the expected panic value.
330 310
 //
331 311
 //   assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
332
-//
333
-// Returns whether the assertion was successful (true) or not (false).
334 312
 func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
335 313
 	return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
336 314
 }
@@ -339,8 +317,6 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
339 317
 //
340 318
 //  assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
341 319
 //  assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
342
-//
343
-// Returns whether the assertion was successful (true) or not (false).
344 320
 func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
345 321
 	return Regexp(t, rx, str, append([]interface{}{msg}, args...)...)
346 322
 }
@@ -349,8 +325,6 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in
349 325
 // elements given in the specified subset(array, slice...).
350 326
 //
351 327
 //    assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
352
-//
353
-// Returns whether the assertion was successful (true) or not (false).
354 328
 func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
355 329
 	return Subset(t, list, subset, append([]interface{}{msg}, args...)...)
356 330
 }
@@ -358,8 +332,6 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args
358 332
 // Truef asserts that the specified value is true.
359 333
 //
360 334
 //    assert.Truef(t, myBool, "error message %s", "formatted")
361
-//
362
-// Returns whether the assertion was successful (true) or not (false).
363 335
 func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
364 336
 	return True(t, value, append([]interface{}{msg}, args...)...)
365 337
 }
@@ -367,13 +339,11 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
367 339
 // WithinDurationf asserts that the two times are within duration delta of each other.
368 340
 //
369 341
 //   assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
370
-//
371
-// Returns whether the assertion was successful (true) or not (false).
372 342
 func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
373 343
 	return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
374 344
 }
375 345
 
376
-// Zerof asserts that i is the zero value for its type and returns the truth.
346
+// Zerof asserts that i is the zero value for its type.
377 347
 func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
378 348
 	return Zero(t, i, append([]interface{}{msg}, args...)...)
379 349
 }

+ 74
- 134
vendor/github.com/stretchr/testify/assert/assertion_forward.go View File

@@ -27,8 +27,6 @@ func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}
27 27
 //    a.Contains("Hello World", "World")
28 28
 //    a.Contains(["Hello", "World"], "World")
29 29
 //    a.Contains({"Hello": "World"}, "Hello")
30
-//
31
-// Returns whether the assertion was successful (true) or not (false).
32 30
 func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
33 31
 	return Contains(a.t, s, contains, msgAndArgs...)
34 32
 }
@@ -39,18 +37,42 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ..
39 37
 //    a.Containsf("Hello World", "World", "error message %s", "formatted")
40 38
 //    a.Containsf(["Hello", "World"], "World", "error message %s", "formatted")
41 39
 //    a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted")
42
-//
43
-// Returns whether the assertion was successful (true) or not (false).
44 40
 func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
45 41
 	return Containsf(a.t, s, contains, msg, args...)
46 42
 }
47 43
 
44
+// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
45
+func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool {
46
+	return DirExists(a.t, path, msgAndArgs...)
47
+}
48
+
49
+// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
50
+func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool {
51
+	return DirExistsf(a.t, path, msg, args...)
52
+}
53
+
54
+// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
55
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
56
+// the number of appearances of each of them in both lists should match.
57
+//
58
+// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2])
59
+func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool {
60
+	return ElementsMatch(a.t, listA, listB, msgAndArgs...)
61
+}
62
+
63
+// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
64
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
65
+// the number of appearances of each of them in both lists should match.
66
+//
67
+// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
68
+func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
69
+	return ElementsMatchf(a.t, listA, listB, msg, args...)
70
+}
71
+
48 72
 // Empty asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
49 73
 // a slice or a channel with len == 0.
50 74
 //
51 75
 //  a.Empty(obj)
52
-//
53
-// Returns whether the assertion was successful (true) or not (false).
54 76
 func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
55 77
 	return Empty(a.t, object, msgAndArgs...)
56 78
 }
@@ -59,8 +81,6 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
59 81
 // a slice or a channel with len == 0.
60 82
 //
61 83
 //  a.Emptyf(obj, "error message %s", "formatted")
62
-//
63
-// Returns whether the assertion was successful (true) or not (false).
64 84
 func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool {
65 85
 	return Emptyf(a.t, object, msg, args...)
66 86
 }
@@ -69,8 +89,6 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{})
69 89
 //
70 90
 //    a.Equal(123, 123)
71 91
 //
72
-// Returns whether the assertion was successful (true) or not (false).
73
-//
74 92
 // Pointer variable equality is determined based on the equality of the
75 93
 // referenced values (as opposed to the memory addresses). Function equality
76 94
 // cannot be determined and will always fail.
@@ -83,8 +101,6 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs
83 101
 //
84 102
 //   actualObj, err := SomeFunction()
85 103
 //   a.EqualError(err,  expectedErrorString)
86
-//
87
-// Returns whether the assertion was successful (true) or not (false).
88 104
 func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
89 105
 	return EqualError(a.t, theError, errString, msgAndArgs...)
90 106
 }
@@ -94,8 +110,6 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...
94 110
 //
95 111
 //   actualObj, err := SomeFunction()
96 112
 //   a.EqualErrorf(err,  expectedErrorString, "error message %s", "formatted")
97
-//
98
-// Returns whether the assertion was successful (true) or not (false).
99 113
 func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool {
100 114
 	return EqualErrorf(a.t, theError, errString, msg, args...)
101 115
 }
@@ -104,8 +118,6 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a
104 118
 // and equal.
105 119
 //
106 120
 //    a.EqualValues(uint32(123), int32(123))
107
-//
108
-// Returns whether the assertion was successful (true) or not (false).
109 121
 func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
110 122
 	return EqualValues(a.t, expected, actual, msgAndArgs...)
111 123
 }
@@ -114,8 +126,6 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
114 126
 // and equal.
115 127
 //
116 128
 //    a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
117
-//
118
-// Returns whether the assertion was successful (true) or not (false).
119 129
 func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
120 130
 	return EqualValuesf(a.t, expected, actual, msg, args...)
121 131
 }
@@ -124,8 +134,6 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg
124 134
 //
125 135
 //    a.Equalf(123, 123, "error message %s", "formatted")
126 136
 //
127
-// Returns whether the assertion was successful (true) or not (false).
128
-//
129 137
 // Pointer variable equality is determined based on the equality of the
130 138
 // referenced values (as opposed to the memory addresses). Function equality
131 139
 // cannot be determined and will always fail.
@@ -139,8 +147,6 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string
139 147
 //   if a.Error(err) {
140 148
 // 	   assert.Equal(t, expectedError, err)
141 149
 //   }
142
-//
143
-// Returns whether the assertion was successful (true) or not (false).
144 150
 func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
145 151
 	return Error(a.t, err, msgAndArgs...)
146 152
 }
@@ -151,26 +157,20 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
151 157
 //   if a.Errorf(err, "error message %s", "formatted") {
152 158
 // 	   assert.Equal(t, expectedErrorf, err)
153 159
 //   }
154
-//
155
-// Returns whether the assertion was successful (true) or not (false).
156 160
 func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
157 161
 	return Errorf(a.t, err, msg, args...)
158 162
 }
159 163
 
160
-// Exactly asserts that two objects are equal is value and type.
164
+// Exactly asserts that two objects are equal in value and type.
161 165
 //
162 166
 //    a.Exactly(int32(123), int64(123))
163
-//
164
-// Returns whether the assertion was successful (true) or not (false).
165 167
 func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
166 168
 	return Exactly(a.t, expected, actual, msgAndArgs...)
167 169
 }
168 170
 
169
-// Exactlyf asserts that two objects are equal is value and type.
171
+// Exactlyf asserts that two objects are equal in value and type.
170 172
 //
171 173
 //    a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123))
172
-//
173
-// Returns whether the assertion was successful (true) or not (false).
174 174
 func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
175 175
 	return Exactlyf(a.t, expected, actual, msg, args...)
176 176
 }
@@ -198,8 +198,6 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{
198 198
 // False asserts that the specified value is false.
199 199
 //
200 200
 //    a.False(myBool)
201
-//
202
-// Returns whether the assertion was successful (true) or not (false).
203 201
 func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
204 202
 	return False(a.t, value, msgAndArgs...)
205 203
 }
@@ -207,20 +205,28 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
207 205
 // Falsef asserts that the specified value is false.
208 206
 //
209 207
 //    a.Falsef(myBool, "error message %s", "formatted")
210
-//
211
-// Returns whether the assertion was successful (true) or not (false).
212 208
 func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool {
213 209
 	return Falsef(a.t, value, msg, args...)
214 210
 }
215 211
 
212
+// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
213
+func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool {
214
+	return FileExists(a.t, path, msgAndArgs...)
215
+}
216
+
217
+// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
218
+func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool {
219
+	return FileExistsf(a.t, path, msg, args...)
220
+}
221
+
216 222
 // HTTPBodyContains asserts that a specified handler returns a
217 223
 // body that contains a string.
218 224
 //
219 225
 //  a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
220 226
 //
221 227
 // Returns whether the assertion was successful (true) or not (false).
222
-func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
223
-	return HTTPBodyContains(a.t, handler, method, url, values, str)
228
+func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
229
+	return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...)
224 230
 }
225 231
 
226 232
 // HTTPBodyContainsf asserts that a specified handler returns a
@@ -229,8 +235,8 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u
229 235
 //  a.HTTPBodyContainsf(myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
230 236
 //
231 237
 // Returns whether the assertion was successful (true) or not (false).
232
-func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
233
-	return HTTPBodyContainsf(a.t, handler, method, url, values, str)
238
+func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
239
+	return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...)
234 240
 }
235 241
 
236 242
 // HTTPBodyNotContains asserts that a specified handler returns a
@@ -239,8 +245,8 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string,
239 245
 //  a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
240 246
 //
241 247
 // Returns whether the assertion was successful (true) or not (false).
242
-func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
243
-	return HTTPBodyNotContains(a.t, handler, method, url, values, str)
248
+func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
249
+	return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...)
244 250
 }
245 251
 
246 252
 // HTTPBodyNotContainsf asserts that a specified handler returns a
@@ -249,8 +255,8 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string
249 255
 //  a.HTTPBodyNotContainsf(myHandler, "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
250 256
 //
251 257
 // Returns whether the assertion was successful (true) or not (false).
252
-func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
253
-	return HTTPBodyNotContainsf(a.t, handler, method, url, values, str)
258
+func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
259
+	return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...)
254 260
 }
255 261
 
256 262
 // HTTPError asserts that a specified handler returns an error status code.
@@ -258,8 +264,8 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin
258 264
 //  a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
259 265
 //
260 266
 // Returns whether the assertion was successful (true) or not (false).
261
-func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool {
262
-	return HTTPError(a.t, handler, method, url, values)
267
+func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
268
+	return HTTPError(a.t, handler, method, url, values, msgAndArgs...)
263 269
 }
264 270
 
265 271
 // HTTPErrorf asserts that a specified handler returns an error status code.
@@ -267,8 +273,8 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri
267 273
 //  a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
268 274
 //
269 275
 // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
270
-func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values) bool {
271
-	return HTTPErrorf(a.t, handler, method, url, values)
276
+func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
277
+	return HTTPErrorf(a.t, handler, method, url, values, msg, args...)
272 278
 }
273 279
 
274 280
 // HTTPRedirect asserts that a specified handler returns a redirect status code.
@@ -276,8 +282,8 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str
276 282
 //  a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
277 283
 //
278 284
 // Returns whether the assertion was successful (true) or not (false).
279
-func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool {
280
-	return HTTPRedirect(a.t, handler, method, url, values)
285
+func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
286
+	return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...)
281 287
 }
282 288
 
283 289
 // HTTPRedirectf asserts that a specified handler returns a redirect status code.
@@ -285,8 +291,8 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s
285 291
 //  a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
286 292
 //
287 293
 // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
288
-func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values) bool {
289
-	return HTTPRedirectf(a.t, handler, method, url, values)
294
+func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
295
+	return HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
290 296
 }
291 297
 
292 298
 // HTTPSuccess asserts that a specified handler returns a success status code.
@@ -294,8 +300,8 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url
294 300
 //  a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
295 301
 //
296 302
 // Returns whether the assertion was successful (true) or not (false).
297
-func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool {
298
-	return HTTPSuccess(a.t, handler, method, url, values)
303
+func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool {
304
+	return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...)
299 305
 }
300 306
 
301 307
 // HTTPSuccessf asserts that a specified handler returns a success status code.
@@ -303,8 +309,8 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st
303 309
 //  a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
304 310
 //
305 311
 // Returns whether the assertion was successful (true) or not (false).
306
-func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values) bool {
307
-	return HTTPSuccessf(a.t, handler, method, url, values)
312
+func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
313
+	return HTTPSuccessf(a.t, handler, method, url, values, msg, args...)
308 314
 }
309 315
 
310 316
 // Implements asserts that an object is implemented by the specified interface.
@@ -324,12 +330,20 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}
324 330
 // InDelta asserts that the two numerals are within delta of each other.
325 331
 //
326 332
 // 	 a.InDelta(math.Pi, (22 / 7.0), 0.01)
327
-//
328
-// Returns whether the assertion was successful (true) or not (false).
329 333
 func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
330 334
 	return InDelta(a.t, expected, actual, delta, msgAndArgs...)
331 335
 }
332 336
 
337
+// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
338
+func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
339
+	return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...)
340
+}
341
+
342
+// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
343
+func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
344
+	return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...)
345
+}
346
+
333 347
 // InDeltaSlice is the same as InDelta, except it compares two slices.
334 348
 func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
335 349
 	return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...)
@@ -343,15 +357,11 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del
343 357
 // InDeltaf asserts that the two numerals are within delta of each other.
344 358
 //
345 359
 // 	 a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
346
-//
347
-// Returns whether the assertion was successful (true) or not (false).
348 360
 func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
349 361
 	return InDeltaf(a.t, expected, actual, delta, msg, args...)
350 362
 }
351 363
 
352 364
 // InEpsilon asserts that expected and actual have a relative error less than epsilon
353
-//
354
-// Returns whether the assertion was successful (true) or not (false).
355 365
 func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
356 366
 	return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
357 367
 }
@@ -367,8 +377,6 @@ func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, e
367 377
 }
368 378
 
369 379
 // InEpsilonf asserts that expected and actual have a relative error less than epsilon
370
-//
371
-// Returns whether the assertion was successful (true) or not (false).
372 380
 func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
373 381
 	return InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
374 382
 }
@@ -386,8 +394,6 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s
386 394
 // JSONEq asserts that two JSON strings are equivalent.
387 395
 //
388 396
 //  a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
389
-//
390
-// Returns whether the assertion was successful (true) or not (false).
391 397
 func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
392 398
 	return JSONEq(a.t, expected, actual, msgAndArgs...)
393 399
 }
@@ -395,8 +401,6 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf
395 401
 // JSONEqf asserts that two JSON strings are equivalent.
396 402
 //
397 403
 //  a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
398
-//
399
-// Returns whether the assertion was successful (true) or not (false).
400 404
 func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool {
401 405
 	return JSONEqf(a.t, expected, actual, msg, args...)
402 406
 }
@@ -405,8 +409,6 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ..
405 409
 // Len also fails if the object has a type that len() not accept.
406 410
 //
407 411
 //    a.Len(mySlice, 3)
408
-//
409
-// Returns whether the assertion was successful (true) or not (false).
410 412
 func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
411 413
 	return Len(a.t, object, length, msgAndArgs...)
412 414
 }
@@ -415,8 +417,6 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface
415 417
 // Lenf also fails if the object has a type that len() not accept.
416 418
 //
417 419
 //    a.Lenf(mySlice, 3, "error message %s", "formatted")
418
-//
419
-// Returns whether the assertion was successful (true) or not (false).
420 420
 func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool {
421 421
 	return Lenf(a.t, object, length, msg, args...)
422 422
 }
@@ -424,8 +424,6 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in
424 424
 // Nil asserts that the specified object is nil.
425 425
 //
426 426
 //    a.Nil(err)
427
-//
428
-// Returns whether the assertion was successful (true) or not (false).
429 427
 func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
430 428
 	return Nil(a.t, object, msgAndArgs...)
431 429
 }
@@ -433,8 +431,6 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
433 431
 // Nilf asserts that the specified object is nil.
434 432
 //
435 433
 //    a.Nilf(err, "error message %s", "formatted")
436
-//
437
-// Returns whether the assertion was successful (true) or not (false).
438 434
 func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool {
439 435
 	return Nilf(a.t, object, msg, args...)
440 436
 }
@@ -445,8 +441,6 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) b
445 441
 //   if a.NoError(err) {
446 442
 // 	   assert.Equal(t, expectedObj, actualObj)
447 443
 //   }
448
-//
449
-// Returns whether the assertion was successful (true) or not (false).
450 444
 func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
451 445
 	return NoError(a.t, err, msgAndArgs...)
452 446
 }
@@ -457,8 +451,6 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
457 451
 //   if a.NoErrorf(err, "error message %s", "formatted") {
458 452
 // 	   assert.Equal(t, expectedObj, actualObj)
459 453
 //   }
460
-//
461
-// Returns whether the assertion was successful (true) or not (false).
462 454
 func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool {
463 455
 	return NoErrorf(a.t, err, msg, args...)
464 456
 }
@@ -469,8 +461,6 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool {
469 461
 //    a.NotContains("Hello World", "Earth")
470 462
 //    a.NotContains(["Hello", "World"], "Earth")
471 463
 //    a.NotContains({"Hello": "World"}, "Earth")
472
-//
473
-// Returns whether the assertion was successful (true) or not (false).
474 464
 func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
475 465
 	return NotContains(a.t, s, contains, msgAndArgs...)
476 466
 }
@@ -481,8 +471,6 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs
481 471
 //    a.NotContainsf("Hello World", "Earth", "error message %s", "formatted")
482 472
 //    a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted")
483 473
 //    a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted")
484
-//
485
-// Returns whether the assertion was successful (true) or not (false).
486 474
 func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool {
487 475
 	return NotContainsf(a.t, s, contains, msg, args...)
488 476
 }
@@ -493,8 +481,6 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin
493 481
 //  if a.NotEmpty(obj) {
494 482
 //    assert.Equal(t, "two", obj[1])
495 483
 //  }
496
-//
497
-// Returns whether the assertion was successful (true) or not (false).
498 484
 func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
499 485
 	return NotEmpty(a.t, object, msgAndArgs...)
500 486
 }
@@ -505,8 +491,6 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo
505 491
 //  if a.NotEmptyf(obj, "error message %s", "formatted") {
506 492
 //    assert.Equal(t, "two", obj[1])
507 493
 //  }
508
-//
509
-// Returns whether the assertion was successful (true) or not (false).
510 494
 func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool {
511 495
 	return NotEmptyf(a.t, object, msg, args...)
512 496
 }
@@ -515,8 +499,6 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface
515 499
 //
516 500
 //    a.NotEqual(obj1, obj2)
517 501
 //
518
-// Returns whether the assertion was successful (true) or not (false).
519
-//
520 502
 // Pointer variable equality is determined based on the equality of the
521 503
 // referenced values (as opposed to the memory addresses).
522 504
 func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
@@ -527,8 +509,6 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr
527 509
 //
528 510
 //    a.NotEqualf(obj1, obj2, "error message %s", "formatted")
529 511
 //
530
-// Returns whether the assertion was successful (true) or not (false).
531
-//
532 512
 // Pointer variable equality is determined based on the equality of the
533 513
 // referenced values (as opposed to the memory addresses).
534 514
 func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
@@ -538,8 +518,6 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
538 518
 // NotNil asserts that the specified object is not nil.
539 519
 //
540 520
 //    a.NotNil(err)
541
-//
542
-// Returns whether the assertion was successful (true) or not (false).
543 521
 func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
544 522
 	return NotNil(a.t, object, msgAndArgs...)
545 523
 }
@@ -547,8 +525,6 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool
547 525
 // NotNilf asserts that the specified object is not nil.
548 526
 //
549 527
 //    a.NotNilf(err, "error message %s", "formatted")
550
-//
551
-// Returns whether the assertion was successful (true) or not (false).
552 528
 func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool {
553 529
 	return NotNilf(a.t, object, msg, args...)
554 530
 }
@@ -556,8 +532,6 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}
556 532
 // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
557 533
 //
558 534
 //   a.NotPanics(func(){ RemainCalm() })
559
-//
560
-// Returns whether the assertion was successful (true) or not (false).
561 535
 func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
562 536
 	return NotPanics(a.t, f, msgAndArgs...)
563 537
 }
@@ -565,8 +539,6 @@ func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool
565 539
 // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
566 540
 //
567 541
 //   a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted")
568
-//
569
-// Returns whether the assertion was successful (true) or not (false).
570 542
 func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
571 543
 	return NotPanicsf(a.t, f, msg, args...)
572 544
 }
@@ -575,8 +547,6 @@ func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}
575 547
 //
576 548
 //  a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
577 549
 //  a.NotRegexp("^start", "it's not starting")
578
-//
579
-// Returns whether the assertion was successful (true) or not (false).
580 550
 func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
581 551
 	return NotRegexp(a.t, rx, str, msgAndArgs...)
582 552
 }
@@ -585,8 +555,6 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in
585 555
 //
586 556
 //  a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
587 557
 //  a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
588
-//
589
-// Returns whether the assertion was successful (true) or not (false).
590 558
 func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
591 559
 	return NotRegexpf(a.t, rx, str, msg, args...)
592 560
 }
@@ -595,8 +563,6 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg
595 563
 // elements given in the specified subset(array, slice...).
596 564
 //
597 565
 //    a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
598
-//
599
-// Returns whether the assertion was successful (true) or not (false).
600 566
 func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
601 567
 	return NotSubset(a.t, list, subset, msgAndArgs...)
602 568
 }
@@ -605,18 +571,16 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs
605 571
 // elements given in the specified subset(array, slice...).
606 572
 //
607 573
 //    a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
608
-//
609
-// Returns whether the assertion was successful (true) or not (false).
610 574
 func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
611 575
 	return NotSubsetf(a.t, list, subset, msg, args...)
612 576
 }
613 577
 
614
-// NotZero asserts that i is not the zero value for its type and returns the truth.
578
+// NotZero asserts that i is not the zero value for its type.
615 579
 func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
616 580
 	return NotZero(a.t, i, msgAndArgs...)
617 581
 }
618 582
 
619
-// NotZerof asserts that i is not the zero value for its type and returns the truth.
583
+// NotZerof asserts that i is not the zero value for its type.
620 584
 func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool {
621 585
 	return NotZerof(a.t, i, msg, args...)
622 586
 }
@@ -624,8 +588,6 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bo
624 588
 // Panics asserts that the code inside the specified PanicTestFunc panics.
625 589
 //
626 590
 //   a.Panics(func(){ GoCrazy() })
627
-//
628
-// Returns whether the assertion was successful (true) or not (false).
629 591
 func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
630 592
 	return Panics(a.t, f, msgAndArgs...)
631 593
 }
@@ -634,8 +596,6 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
634 596
 // the recovered panic value equals the expected panic value.
635 597
 //
636 598
 //   a.PanicsWithValue("crazy error", func(){ GoCrazy() })
637
-//
638
-// Returns whether the assertion was successful (true) or not (false).
639 599
 func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
640 600
 	return PanicsWithValue(a.t, expected, f, msgAndArgs...)
641 601
 }
@@ -644,8 +604,6 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgA
644 604
 // the recovered panic value equals the expected panic value.
645 605
 //
646 606
 //   a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
647
-//
648
-// Returns whether the assertion was successful (true) or not (false).
649 607
 func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
650 608
 	return PanicsWithValuef(a.t, expected, f, msg, args...)
651 609
 }
@@ -653,8 +611,6 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg
653 611
 // Panicsf asserts that the code inside the specified PanicTestFunc panics.
654 612
 //
655 613
 //   a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted")
656
-//
657
-// Returns whether the assertion was successful (true) or not (false).
658 614
 func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool {
659 615
 	return Panicsf(a.t, f, msg, args...)
660 616
 }
@@ -663,8 +619,6 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b
663 619
 //
664 620
 //  a.Regexp(regexp.MustCompile("start"), "it's starting")
665 621
 //  a.Regexp("start...$", "it's not starting")
666
-//
667
-// Returns whether the assertion was successful (true) or not (false).
668 622
 func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
669 623
 	return Regexp(a.t, rx, str, msgAndArgs...)
670 624
 }
@@ -673,8 +627,6 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
673 627
 //
674 628
 //  a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
675 629
 //  a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
676
-//
677
-// Returns whether the assertion was successful (true) or not (false).
678 630
 func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
679 631
 	return Regexpf(a.t, rx, str, msg, args...)
680 632
 }
@@ -683,8 +635,6 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args .
683 635
 // elements given in the specified subset(array, slice...).
684 636
 //
685 637
 //    a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
686
-//
687
-// Returns whether the assertion was successful (true) or not (false).
688 638
 func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
689 639
 	return Subset(a.t, list, subset, msgAndArgs...)
690 640
 }
@@ -693,8 +643,6 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...
693 643
 // elements given in the specified subset(array, slice...).
694 644
 //
695 645
 //    a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
696
-//
697
-// Returns whether the assertion was successful (true) or not (false).
698 646
 func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
699 647
 	return Subsetf(a.t, list, subset, msg, args...)
700 648
 }
@@ -702,8 +650,6 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a
702 650
 // True asserts that the specified value is true.
703 651
 //
704 652
 //    a.True(myBool)
705
-//
706
-// Returns whether the assertion was successful (true) or not (false).
707 653
 func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
708 654
 	return True(a.t, value, msgAndArgs...)
709 655
 }
@@ -711,8 +657,6 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
711 657
 // Truef asserts that the specified value is true.
712 658
 //
713 659
 //    a.Truef(myBool, "error message %s", "formatted")
714
-//
715
-// Returns whether the assertion was successful (true) or not (false).
716 660
 func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool {
717 661
 	return Truef(a.t, value, msg, args...)
718 662
 }
@@ -720,8 +664,6 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool {
720 664
 // WithinDuration asserts that the two times are within duration delta of each other.
721 665
 //
722 666
 //   a.WithinDuration(time.Now(), time.Now(), 10*time.Second)
723
-//
724
-// Returns whether the assertion was successful (true) or not (false).
725 667
 func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
726 668
 	return WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
727 669
 }
@@ -729,18 +671,16 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta
729 671
 // WithinDurationf asserts that the two times are within duration delta of each other.
730 672
 //
731 673
 //   a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
732
-//
733
-// Returns whether the assertion was successful (true) or not (false).
734 674
 func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
735 675
 	return WithinDurationf(a.t, expected, actual, delta, msg, args...)
736 676
 }
737 677
 
738
-// Zero asserts that i is the zero value for its type and returns the truth.
678
+// Zero asserts that i is the zero value for its type.
739 679
 func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
740 680
 	return Zero(a.t, i, msgAndArgs...)
741 681
 }
742 682
 
743
-// Zerof asserts that i is the zero value for its type and returns the truth.
683
+// Zerof asserts that i is the zero value for its type.
744 684
 func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool {
745 685
 	return Zerof(a.t, i, msg, args...)
746 686
 }

+ 156
- 108
vendor/github.com/stretchr/testify/assert/assertions.go View File

@@ -7,6 +7,7 @@ import (
7 7
 	"errors"
8 8
 	"fmt"
9 9
 	"math"
10
+	"os"
10 11
 	"reflect"
11 12
 	"regexp"
12 13
 	"runtime"
@@ -231,6 +232,13 @@ func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
231 232
 		{"Error", failureMessage},
232 233
 	}
233 234
 
235
+	// Add test name if the Go version supports it
236
+	if n, ok := t.(interface {
237
+		Name() string
238
+	}); ok {
239
+		content = append(content, labeledContent{"Test", n.Name()})
240
+	}
241
+
234 242
 	message := messageFromMsgAndArgs(msgAndArgs...)
235 243
 	if len(message) > 0 {
236 244
 		content = append(content, labeledContent{"Messages", message})
@@ -273,15 +281,16 @@ func labeledOutput(content ...labeledContent) string {
273 281
 //
274 282
 //    assert.Implements(t, (*MyInterface)(nil), new(MyObject))
275 283
 func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
276
-
277 284
 	interfaceType := reflect.TypeOf(interfaceObject).Elem()
278 285
 
286
+	if object == nil {
287
+		return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...)
288
+	}
279 289
 	if !reflect.TypeOf(object).Implements(interfaceType) {
280 290
 		return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...)
281 291
 	}
282 292
 
283 293
 	return true
284
-
285 294
 }
286 295
 
287 296
 // IsType asserts that the specified objects are of the same type.
@@ -298,8 +307,6 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs
298 307
 //
299 308
 //    assert.Equal(t, 123, 123)
300 309
 //
301
-// Returns whether the assertion was successful (true) or not (false).
302
-//
303 310
 // Pointer variable equality is determined based on the equality of the
304 311
 // referenced values (as opposed to the memory addresses). Function equality
305 312
 // cannot be determined and will always fail.
@@ -314,7 +321,7 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{})
314 321
 		expected, actual = formatUnequalValues(expected, actual)
315 322
 		return Fail(t, fmt.Sprintf("Not equal: \n"+
316 323
 			"expected: %s\n"+
317
-			"actual: %s%s", expected, actual, diff), msgAndArgs...)
324
+			"actual  : %s%s", expected, actual, diff), msgAndArgs...)
318 325
 	}
319 326
 
320 327
 	return true
@@ -341,8 +348,6 @@ func formatUnequalValues(expected, actual interface{}) (e string, a string) {
341 348
 // and equal.
342 349
 //
343 350
 //    assert.EqualValues(t, uint32(123), int32(123))
344
-//
345
-// Returns whether the assertion was successful (true) or not (false).
346 351
 func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
347 352
 
348 353
 	if !ObjectsAreEqualValues(expected, actual) {
@@ -350,18 +355,16 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
350 355
 		expected, actual = formatUnequalValues(expected, actual)
351 356
 		return Fail(t, fmt.Sprintf("Not equal: \n"+
352 357
 			"expected: %s\n"+
353
-			"actual: %s%s", expected, actual, diff), msgAndArgs...)
358
+			"actual  : %s%s", expected, actual, diff), msgAndArgs...)
354 359
 	}
355 360
 
356 361
 	return true
357 362
 
358 363
 }
359 364
 
360
-// Exactly asserts that two objects are equal is value and type.
365
+// Exactly asserts that two objects are equal in value and type.
361 366
 //
362 367
 //    assert.Exactly(t, int32(123), int64(123))
363
-//
364
-// Returns whether the assertion was successful (true) or not (false).
365 368
 func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
366 369
 
367 370
 	aType := reflect.TypeOf(expected)
@@ -378,8 +381,6 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
378 381
 // NotNil asserts that the specified object is not nil.
379 382
 //
380 383
 //    assert.NotNil(t, err)
381
-//
382
-// Returns whether the assertion was successful (true) or not (false).
383 384
 func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
384 385
 	if !isNil(object) {
385 386
 		return true
@@ -405,8 +406,6 @@ func isNil(object interface{}) bool {
405 406
 // Nil asserts that the specified object is nil.
406 407
 //
407 408
 //    assert.Nil(t, err)
408
-//
409
-// Returns whether the assertion was successful (true) or not (false).
410 409
 func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
411 410
 	if isNil(object) {
412 411
 		return true
@@ -414,72 +413,38 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
414 413
 	return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
415 414
 }
416 415
 
417
-var numericZeros = []interface{}{
418
-	int(0),
419
-	int8(0),
420
-	int16(0),
421
-	int32(0),
422
-	int64(0),
423
-	uint(0),
424
-	uint8(0),
425
-	uint16(0),
426
-	uint32(0),
427
-	uint64(0),
428
-	float32(0),
429
-	float64(0),
430
-}
431
-
432 416
 // isEmpty gets whether the specified object is considered empty or not.
433 417
 func isEmpty(object interface{}) bool {
434 418
 
419
+	// get nil case out of the way
435 420
 	if object == nil {
436 421
 		return true
437
-	} else if object == "" {
438
-		return true
439
-	} else if object == false {
440
-		return true
441
-	}
442
-
443
-	for _, v := range numericZeros {
444
-		if object == v {
445
-			return true
446
-		}
447 422
 	}
448 423
 
449 424
 	objValue := reflect.ValueOf(object)
450 425
 
451 426
 	switch objValue.Kind() {
452
-	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
453
-		{
454
-			return (objValue.Len() == 0)
455
-		}
456
-	case reflect.Struct:
457
-		switch object.(type) {
458
-		case time.Time:
459
-			return object.(time.Time).IsZero()
460
-		}
427
+	// collection types are empty when they have no element
428
+	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
429
+		return objValue.Len() == 0
430
+	// pointers are empty if nil or if the value they point to is empty
461 431
 	case reflect.Ptr:
462
-		{
463
-			if objValue.IsNil() {
464
-				return true
465
-			}
466
-			switch object.(type) {
467
-			case *time.Time:
468
-				return object.(*time.Time).IsZero()
469
-			default:
470
-				return false
471
-			}
432
+		if objValue.IsNil() {
433
+			return true
472 434
 		}
435
+		deref := objValue.Elem().Interface()
436
+		return isEmpty(deref)
437
+	// for all other types, compare against the zero value
438
+	default:
439
+		zero := reflect.Zero(objValue.Type())
440
+		return reflect.DeepEqual(object, zero.Interface())
473 441
 	}
474
-	return false
475 442
 }
476 443
 
477 444
 // Empty asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
478 445
 // a slice or a channel with len == 0.
479 446
 //
480 447
 //  assert.Empty(t, obj)
481
-//
482
-// Returns whether the assertion was successful (true) or not (false).
483 448
 func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
484 449
 
485 450
 	pass := isEmpty(object)
@@ -497,8 +462,6 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
497 462
 //  if assert.NotEmpty(t, obj) {
498 463
 //    assert.Equal(t, "two", obj[1])
499 464
 //  }
500
-//
501
-// Returns whether the assertion was successful (true) or not (false).
502 465
 func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
503 466
 
504 467
 	pass := !isEmpty(object)
@@ -526,8 +489,6 @@ func getLen(x interface{}) (ok bool, length int) {
526 489
 // Len also fails if the object has a type that len() not accept.
527 490
 //
528 491
 //    assert.Len(t, mySlice, 3)
529
-//
530
-// Returns whether the assertion was successful (true) or not (false).
531 492
 func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
532 493
 	ok, l := getLen(object)
533 494
 	if !ok {
@@ -543,8 +504,6 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
543 504
 // True asserts that the specified value is true.
544 505
 //
545 506
 //    assert.True(t, myBool)
546
-//
547
-// Returns whether the assertion was successful (true) or not (false).
548 507
 func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
549 508
 
550 509
 	if value != true {
@@ -558,8 +517,6 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
558 517
 // False asserts that the specified value is false.
559 518
 //
560 519
 //    assert.False(t, myBool)
561
-//
562
-// Returns whether the assertion was successful (true) or not (false).
563 520
 func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
564 521
 
565 522
 	if value != false {
@@ -574,8 +531,6 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
574 531
 //
575 532
 //    assert.NotEqual(t, obj1, obj2)
576 533
 //
577
-// Returns whether the assertion was successful (true) or not (false).
578
-//
579 534
 // Pointer variable equality is determined based on the equality of the
580 535
 // referenced values (as opposed to the memory addresses).
581 536
 func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
@@ -636,8 +591,6 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) {
636 591
 //    assert.Contains(t, "Hello World", "World")
637 592
 //    assert.Contains(t, ["Hello", "World"], "World")
638 593
 //    assert.Contains(t, {"Hello": "World"}, "Hello")
639
-//
640
-// Returns whether the assertion was successful (true) or not (false).
641 594
 func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
642 595
 
643 596
 	ok, found := includeElement(s, contains)
@@ -658,8 +611,6 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
658 611
 //    assert.NotContains(t, "Hello World", "Earth")
659 612
 //    assert.NotContains(t, ["Hello", "World"], "Earth")
660 613
 //    assert.NotContains(t, {"Hello": "World"}, "Earth")
661
-//
662
-// Returns whether the assertion was successful (true) or not (false).
663 614
 func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
664 615
 
665 616
 	ok, found := includeElement(s, contains)
@@ -678,8 +629,6 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
678 629
 // elements given in the specified subset(array, slice...).
679 630
 //
680 631
 //    assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
681
-//
682
-// Returns whether the assertion was successful (true) or not (false).
683 632
 func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
684 633
 	if subset == nil {
685 634
 		return true // we consider nil to be equal to the nil set
@@ -721,11 +670,9 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
721 670
 // elements given in the specified subset(array, slice...).
722 671
 //
723 672
 //    assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
724
-//
725
-// Returns whether the assertion was successful (true) or not (false).
726 673
 func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
727 674
 	if subset == nil {
728
-		return false // we consider nil to be equal to the nil set
675
+		return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...)
729 676
 	}
730 677
 
731 678
 	subsetValue := reflect.ValueOf(subset)
@@ -760,6 +707,60 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
760 707
 	return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
761 708
 }
762 709
 
710
+// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
711
+// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
712
+// the number of appearances of each of them in both lists should match.
713
+//
714
+// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])
715
+func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
716
+	if isEmpty(listA) && isEmpty(listB) {
717
+		return true
718
+	}
719
+
720
+	aKind := reflect.TypeOf(listA).Kind()
721
+	bKind := reflect.TypeOf(listB).Kind()
722
+
723
+	if aKind != reflect.Array && aKind != reflect.Slice {
724
+		return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...)
725
+	}
726
+
727
+	if bKind != reflect.Array && bKind != reflect.Slice {
728
+		return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...)
729
+	}
730
+
731
+	aValue := reflect.ValueOf(listA)
732
+	bValue := reflect.ValueOf(listB)
733
+
734
+	aLen := aValue.Len()
735
+	bLen := bValue.Len()
736
+
737
+	if aLen != bLen {
738
+		return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...)
739
+	}
740
+
741
+	// Mark indexes in bValue that we already used
742
+	visited := make([]bool, bLen)
743
+	for i := 0; i < aLen; i++ {
744
+		element := aValue.Index(i).Interface()
745
+		found := false
746
+		for j := 0; j < bLen; j++ {
747
+			if visited[j] {
748
+				continue
749
+			}
750
+			if ObjectsAreEqual(bValue.Index(j).Interface(), element) {
751
+				visited[j] = true
752
+				found = true
753
+				break
754
+			}
755
+		}
756
+		if !found {
757
+			return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...)
758
+		}
759
+	}
760
+
761
+	return true
762
+}
763
+
763 764
 // Condition uses a Comparison to assert a complex condition.
764 765
 func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
765 766
 	result := comp()
@@ -798,8 +799,6 @@ func didPanic(f PanicTestFunc) (bool, interface{}) {
798 799
 // Panics asserts that the code inside the specified PanicTestFunc panics.
799 800
 //
800 801
 //   assert.Panics(t, func(){ GoCrazy() })
801
-//
802
-// Returns whether the assertion was successful (true) or not (false).
803 802
 func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
804 803
 
805 804
 	if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {
@@ -813,8 +812,6 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
813 812
 // the recovered panic value equals the expected panic value.
814 813
 //
815 814
 //   assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
816
-//
817
-// Returns whether the assertion was successful (true) or not (false).
818 815
 func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
819 816
 
820 817
 	funcDidPanic, panicValue := didPanic(f)
@@ -831,8 +828,6 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr
831 828
 // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
832 829
 //
833 830
 //   assert.NotPanics(t, func(){ RemainCalm() })
834
-//
835
-// Returns whether the assertion was successful (true) or not (false).
836 831
 func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
837 832
 
838 833
 	if funcDidPanic, panicValue := didPanic(f); funcDidPanic {
@@ -845,8 +840,6 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
845 840
 // WithinDuration asserts that the two times are within duration delta of each other.
846 841
 //
847 842
 //   assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
848
-//
849
-// Returns whether the assertion was successful (true) or not (false).
850 843
 func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
851 844
 
852 845
 	dt := expected.Sub(actual)
@@ -896,8 +889,6 @@ func toFloat(x interface{}) (float64, bool) {
896 889
 // InDelta asserts that the two numerals are within delta of each other.
897 890
 //
898 891
 // 	 assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
899
-//
900
-// Returns whether the assertion was successful (true) or not (false).
901 892
 func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
902 893
 
903 894
 	af, aok := toFloat(expected)
@@ -944,6 +935,47 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
944 935
 	return true
945 936
 }
946 937
 
938
+// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
939
+func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
940
+	if expected == nil || actual == nil ||
941
+		reflect.TypeOf(actual).Kind() != reflect.Map ||
942
+		reflect.TypeOf(expected).Kind() != reflect.Map {
943
+		return Fail(t, "Arguments must be maps", msgAndArgs...)
944
+	}
945
+
946
+	expectedMap := reflect.ValueOf(expected)
947
+	actualMap := reflect.ValueOf(actual)
948
+
949
+	if expectedMap.Len() != actualMap.Len() {
950
+		return Fail(t, "Arguments must have the same number of keys", msgAndArgs...)
951
+	}
952
+
953
+	for _, k := range expectedMap.MapKeys() {
954
+		ev := expectedMap.MapIndex(k)
955
+		av := actualMap.MapIndex(k)
956
+
957
+		if !ev.IsValid() {
958
+			return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...)
959
+		}
960
+
961
+		if !av.IsValid() {
962
+			return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...)
963
+		}
964
+
965
+		if !InDelta(
966
+			t,
967
+			ev.Interface(),
968
+			av.Interface(),
969
+			delta,
970
+			msgAndArgs...,
971
+		) {
972
+			return false
973
+		}
974
+	}
975
+
976
+	return true
977
+}
978
+
947 979
 func calcRelativeError(expected, actual interface{}) (float64, error) {
948 980
 	af, aok := toFloat(expected)
949 981
 	if !aok {
@@ -961,8 +993,6 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
961 993
 }
962 994
 
963 995
 // InEpsilon asserts that expected and actual have a relative error less than epsilon
964
-//
965
-// Returns whether the assertion was successful (true) or not (false).
966 996
 func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
967 997
 	actualEpsilon, err := calcRelativeError(expected, actual)
968 998
 	if err != nil {
@@ -1007,8 +1037,6 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
1007 1037
 //   if assert.NoError(t, err) {
1008 1038
 //	   assert.Equal(t, expectedObj, actualObj)
1009 1039
 //   }
1010
-//
1011
-// Returns whether the assertion was successful (true) or not (false).
1012 1040
 func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
1013 1041
 	if err != nil {
1014 1042
 		return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
@@ -1023,8 +1051,6 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
1023 1051
 //   if assert.Error(t, err) {
1024 1052
 //	   assert.Equal(t, expectedError, err)
1025 1053
 //   }
1026
-//
1027
-// Returns whether the assertion was successful (true) or not (false).
1028 1054
 func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
1029 1055
 
1030 1056
 	if err == nil {
@@ -1039,8 +1065,6 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
1039 1065
 //
1040 1066
 //   actualObj, err := SomeFunction()
1041 1067
 //   assert.EqualError(t, err,  expectedErrorString)
1042
-//
1043
-// Returns whether the assertion was successful (true) or not (false).
1044 1068
 func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
1045 1069
 	if !Error(t, theError, msgAndArgs...) {
1046 1070
 		return false
@@ -1051,7 +1075,7 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte
1051 1075
 	if expected != actual {
1052 1076
 		return Fail(t, fmt.Sprintf("Error message not equal:\n"+
1053 1077
 			"expected: %q\n"+
1054
-			"actual: %q", expected, actual), msgAndArgs...)
1078
+			"actual  : %q", expected, actual), msgAndArgs...)
1055 1079
 	}
1056 1080
 	return true
1057 1081
 }
@@ -1074,8 +1098,6 @@ func matchRegexp(rx interface{}, str interface{}) bool {
1074 1098
 //
1075 1099
 //  assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
1076 1100
 //  assert.Regexp(t, "start...$", "it's not starting")
1077
-//
1078
-// Returns whether the assertion was successful (true) or not (false).
1079 1101
 func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
1080 1102
 
1081 1103
 	match := matchRegexp(rx, str)
@@ -1091,8 +1113,6 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface
1091 1113
 //
1092 1114
 //  assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
1093 1115
 //  assert.NotRegexp(t, "^start", "it's not starting")
1094
-//
1095
-// Returns whether the assertion was successful (true) or not (false).
1096 1116
 func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
1097 1117
 	match := matchRegexp(rx, str)
1098 1118
 
@@ -1104,7 +1124,7 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf
1104 1124
 
1105 1125
 }
1106 1126
 
1107
-// Zero asserts that i is the zero value for its type and returns the truth.
1127
+// Zero asserts that i is the zero value for its type.
1108 1128
 func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
1109 1129
 	if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
1110 1130
 		return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
@@ -1112,7 +1132,7 @@ func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
1112 1132
 	return true
1113 1133
 }
1114 1134
 
1115
-// NotZero asserts that i is not the zero value for its type and returns the truth.
1135
+// NotZero asserts that i is not the zero value for its type.
1116 1136
 func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
1117 1137
 	if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
1118 1138
 		return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...)
@@ -1120,11 +1140,39 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
1120 1140
 	return true
1121 1141
 }
1122 1142
 
1143
+// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
1144
+func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
1145
+	info, err := os.Lstat(path)
1146
+	if err != nil {
1147
+		if os.IsNotExist(err) {
1148
+			return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
1149
+		}
1150
+		return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
1151
+	}
1152
+	if info.IsDir() {
1153
+		return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...)
1154
+	}
1155
+	return true
1156
+}
1157
+
1158
+// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
1159
+func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
1160
+	info, err := os.Lstat(path)
1161
+	if err != nil {
1162
+		if os.IsNotExist(err) {
1163
+			return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
1164
+		}
1165
+		return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
1166
+	}
1167
+	if !info.IsDir() {
1168
+		return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...)
1169
+	}
1170
+	return true
1171
+}
1172
+
1123 1173
 // JSONEq asserts that two JSON strings are equivalent.
1124 1174
 //
1125 1175
 //  assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
1126
-//
1127
-// Returns whether the assertion was successful (true) or not (false).
1128 1176
 func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
1129 1177
 	var expectedJSONAsInterface, actualJSONAsInterface interface{}
1130 1178
 

+ 5
- 5
vendor/github.com/stretchr/testify/assert/http_assertions.go View File

@@ -25,7 +25,7 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (
25 25
 //  assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
26 26
 //
27 27
 // Returns whether the assertion was successful (true) or not (false).
28
-func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
28
+func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
29 29
 	code, err := httpCode(handler, method, url, values)
30 30
 	if err != nil {
31 31
 		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
@@ -45,7 +45,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
45 45
 //  assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
46 46
 //
47 47
 // Returns whether the assertion was successful (true) or not (false).
48
-func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
48
+func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
49 49
 	code, err := httpCode(handler, method, url, values)
50 50
 	if err != nil {
51 51
 		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
@@ -65,7 +65,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
65 65
 //  assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
66 66
 //
67 67
 // Returns whether the assertion was successful (true) or not (false).
68
-func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
68
+func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
69 69
 	code, err := httpCode(handler, method, url, values)
70 70
 	if err != nil {
71 71
 		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
@@ -98,7 +98,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s
98 98
 //  assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
99 99
 //
100 100
 // Returns whether the assertion was successful (true) or not (false).
101
-func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
101
+func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
102 102
 	body := HTTPBody(handler, method, url, values)
103 103
 
104 104
 	contains := strings.Contains(body, fmt.Sprint(str))
@@ -115,7 +115,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
115 115
 //  assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
116 116
 //
117 117
 // Returns whether the assertion was successful (true) or not (false).
118
-func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
118
+func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
119 119
 	body := HTTPBody(handler, method, url, values)
120 120
 
121 121
 	contains := strings.Contains(body, fmt.Sprint(str))

Loading…
Cancel
Save