|  | // Copyright 2021 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | //go:build ((darwin || dragonfly || freebsd || (js && wasm) || (!android && linux) || netbsd || openbsd || solaris) && (!cgo || osusergo)) || aix || illumos | 
|  |  | 
|  | package user | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "sort" | 
|  | "strings" | 
|  | "testing" | 
|  | ) | 
|  |  | 
|  | var testGroupFile = `# See the opendirectoryd(8) man page for additional | 
|  | # information about Open Directory. | 
|  | ## | 
|  | nobody:*:-2: | 
|  | nogroup:*:-1: | 
|  | wheel:*:0:root | 
|  | emptyid:*::root | 
|  | invalidgid:*:notanumber:root | 
|  | +plussign:*:20:root | 
|  | -minussign:*:21:root | 
|  | # Next line is invalid (empty group name) | 
|  | :*:22:root | 
|  |  | 
|  | daemon:*:1:root | 
|  | indented:*:7:root | 
|  | # comment:*:4:found | 
|  | # comment:*:4:found | 
|  | kmem:*:2:root | 
|  | manymembers:x:777:jill,jody,john,jack,jov,user777 | 
|  | ` + largeGroup() | 
|  |  | 
|  | func largeGroup() (res string) { | 
|  | var b strings.Builder | 
|  | b.WriteString("largegroup:x:1000:user1") | 
|  | for i := 2; i <= 7500; i++ { | 
|  | fmt.Fprintf(&b, ",user%d", i) | 
|  | } | 
|  | return b.String() | 
|  | } | 
|  |  | 
|  | var listGroupsTests = []struct { | 
|  | // input | 
|  | in   string | 
|  | user string | 
|  | gid  string | 
|  | // output | 
|  | gids []string | 
|  | err  bool | 
|  | }{ | 
|  | {in: testGroupFile, user: "root", gid: "0", gids: []string{"0", "1", "2", "7"}}, | 
|  | {in: testGroupFile, user: "jill", gid: "33", gids: []string{"33", "777"}}, | 
|  | {in: testGroupFile, user: "jody", gid: "34", gids: []string{"34", "777"}}, | 
|  | {in: testGroupFile, user: "john", gid: "35", gids: []string{"35", "777"}}, | 
|  | {in: testGroupFile, user: "jov", gid: "37", gids: []string{"37", "777"}}, | 
|  | {in: testGroupFile, user: "user777", gid: "7", gids: []string{"7", "777", "1000"}}, | 
|  | {in: testGroupFile, user: "user1111", gid: "1111", gids: []string{"1111", "1000"}}, | 
|  | {in: testGroupFile, user: "user1000", gid: "1000", gids: []string{"1000"}}, | 
|  | {in: testGroupFile, user: "user7500", gid: "7500", gids: []string{"1000", "7500"}}, | 
|  | {in: testGroupFile, user: "no-such-user", gid: "2345", gids: []string{"2345"}}, | 
|  | {in: "", user: "no-such-user", gid: "2345", gids: []string{"2345"}}, | 
|  | // Error cases. | 
|  | {in: "", user: "", gid: "2345", err: true}, | 
|  | {in: "", user: "joanna", gid: "bad", err: true}, | 
|  | } | 
|  |  | 
|  | func TestListGroups(t *testing.T) { | 
|  | for _, tc := range listGroupsTests { | 
|  | u := &User{Username: tc.user, Gid: tc.gid} | 
|  | got, err := listGroupsFromReader(u, strings.NewReader(tc.in)) | 
|  | if tc.err { | 
|  | if err == nil { | 
|  | t.Errorf("listGroups(%q): got nil; want error", tc.user) | 
|  | } | 
|  | continue // no more checks | 
|  | } | 
|  | if err != nil { | 
|  | t.Errorf("listGroups(%q): got %v error, want nil", tc.user, err) | 
|  | continue // no more checks | 
|  | } | 
|  | checkSameIDs(t, got, tc.gids) | 
|  | } | 
|  | } | 
|  |  | 
|  | func checkSameIDs(t *testing.T, got, want []string) { | 
|  | t.Helper() | 
|  | if len(got) != len(want) { | 
|  | t.Errorf("ID list mismatch: got %v; want %v", got, want) | 
|  | return | 
|  | } | 
|  | sort.Strings(got) | 
|  | sort.Strings(want) | 
|  | mismatch := -1 | 
|  | for i, g := range want { | 
|  | if got[i] != g { | 
|  | mismatch = i | 
|  | break | 
|  | } | 
|  | } | 
|  | if mismatch != -1 { | 
|  | t.Errorf("ID list mismatch (at index %d): got %v; want %v", mismatch, got, want) | 
|  | } | 
|  | } |