Skip to content

Commit e681e9f

Browse files
authored
Set up test repository (#44)
* test: set up testrepo * test: print log to os.Stdout * test: fix lint * command: implement dual writer * commit: add some unit tests * command: print command together with the stdout
1 parent e8ec135 commit e681e9f

13 files changed

+205
-71
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.idea
22
*.sublime-project
33
*.sublime-workspace
4+
/testdata

Diff for: .travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ go:
99

1010
install: go get -v -t ./...
1111
script:
12-
- go test -v -cover -race
12+
- go test -v -cover -race -verbose

Diff for: appveyor.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ deploy: false
1313
install:
1414
- go version
1515
- go env
16-
- go test -v -cover -race
16+
- go test -v -cover -race -verbose

Diff for: blob.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
// Blob is a blob object.
1313
type Blob struct {
14-
repo *Repository
1514
*TreeEntry
1615
}
1716

@@ -22,7 +21,7 @@ func (b *Blob) Bytes() ([]byte, error) {
2221
stderr := new(bytes.Buffer)
2322

2423
// Preallocate memory to save ~50% memory usage on big files.
25-
stdout.Grow(int(b.Size() + 2048))
24+
stdout.Grow(int(b.Size()))
2625

2726
if err := b.Pipeline(stdout, stderr); err != nil {
2827
return nil, concatenateError(err, stderr.String())
@@ -32,5 +31,5 @@ func (b *Blob) Bytes() ([]byte, error) {
3231

3332
// Pipeline reads the content of the blob and pipes stdout and stderr to supplied io.Writer.
3433
func (b *Blob) Pipeline(stdout, stderr io.Writer) error {
35-
return NewCommand("show", b.id.String()).RunInDirPipeline(stdout, stderr, b.repo.path)
34+
return NewCommand("show", b.id.String()).RunInDirPipeline(stdout, stderr, b.parent.repo.path)
3635
}

Diff for: blob_test.go

+20-22
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,30 @@ import (
1212
)
1313

1414
func TestBlob(t *testing.T) {
15-
expOutput := `Copyright (c) 2015 All Gogs Contributors
16-
17-
Permission is hereby granted, free of charge, to any person obtaining a copy
18-
of this software and associated documentation files (the "Software"), to deal
19-
in the Software without restriction, including without limitation the rights
20-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21-
copies of the Software, and to permit persons to whom the Software is
22-
furnished to do so, subject to the following conditions:
23-
24-
The above copyright notice and this permission notice shall be included in
25-
all copies or substantial portions of the Software.
26-
27-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33-
THE SOFTWARE.`
15+
expOutput := `This is a sample project students can use during Matthew's Git class.
16+
17+
Here is an addition by me
18+
19+
We can have a bit of fun with this repo, knowing that we can always reset it to a known good state. We can apply labels, and branch, then add new code and merge it in to the master branch.
20+
21+
As a quick reminder, this came from one of three locations in either SSH, Git, or HTTPS format:
22+
23+
* [email protected]:matthewmccullough/hellogitworld.git
24+
* git://github.com/matthewmccullough/hellogitworld.git
25+
* https://[email protected]/matthewmccullough/hellogitworld.git
26+
27+
We can, as an example effort, even modify this README and change it as if it were source code for the purposes of the class.
28+
29+
This demo also includes an image with changes on a branch for examination of image diff on GitHub.
30+
`
3431

3532
blob := &Blob{
36-
repo: &Repository{},
3733
TreeEntry: &TreeEntry{
38-
id: MustIDFromString("176d8dfe018c850d01851b05fb8a430096247353"), // Blob ID of "LICENSE" file
34+
mode: EntryBlob,
35+
typ: ObjectBlob,
36+
id: MustIDFromString("adfd6da3c0a3fb038393144becbf37f14f780087"), // Blob ID of "README.txt" file
3937
parent: &Tree{
40-
repo: &Repository{},
38+
repo: testrepo,
4139
},
4240
},
4341
}

Diff for: command.go

+46-9
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,34 @@ func (c *Command) AddEnvs(envs ...string) *Command {
5353
// DefaultTimeout is the default timeout duration for all commands.
5454
const DefaultTimeout = time.Minute
5555

56+
// A limitDualWriter writes to W but limits the amount of data written to just N bytes.
57+
// On the other hand, it passes everything to w.
58+
type limitDualWriter struct {
59+
W io.Writer // underlying writer
60+
N int64 // max bytes remaining
61+
prompted bool
62+
63+
w io.Writer
64+
}
65+
66+
func (w *limitDualWriter) Write(p []byte) (int, error) {
67+
if w.N > 0 {
68+
limit := int64(len(p))
69+
if limit > w.N {
70+
limit = w.N
71+
}
72+
n, _ := w.W.Write(p[:limit])
73+
w.N -= int64(n)
74+
}
75+
76+
if !w.prompted && w.N <= 0 {
77+
w.prompted = true
78+
_, _ = w.W.Write([]byte("... (more omitted)"))
79+
}
80+
81+
return w.w.Write(p)
82+
}
83+
5684
// RunInDirPipelineWithTimeout executes the command in given directory and timeout duration.
5785
// It pipes stdout and stderr to supplied io.Writer. DefaultTimeout will be used if the timeout
5886
// duration is less than time.Nanosecond (i.e. less than or equal to 0).
@@ -61,12 +89,25 @@ func (c *Command) RunInDirPipelineWithTimeout(timeout time.Duration, stdout, std
6189
timeout = DefaultTimeout
6290
}
6391

64-
if len(dir) == 0 {
65-
log("[timeout: %v] %s", timeout, c)
66-
} else {
67-
log("[timeout: %v] %s: %s", timeout, dir, c)
92+
buf := new(bytes.Buffer)
93+
w := stdout
94+
if logOutput != nil {
95+
buf.Grow(512)
96+
w = &limitDualWriter{
97+
W: buf,
98+
N: int64(buf.Cap()),
99+
w: stdout,
100+
}
68101
}
69102

103+
defer func() {
104+
if len(dir) == 0 {
105+
log("[timeout: %v] %s\n%s", timeout, c, buf.Bytes())
106+
} else {
107+
log("[timeout: %v] %s: %s\n%s", timeout, dir, c, buf.Bytes())
108+
}
109+
}()
110+
70111
ctx, cancel := context.WithTimeout(context.Background(), timeout)
71112
defer cancel()
72113

@@ -75,7 +116,7 @@ func (c *Command) RunInDirPipelineWithTimeout(timeout time.Duration, stdout, std
75116
cmd.Env = append(os.Environ(), c.envs...)
76117
}
77118
cmd.Dir = dir
78-
cmd.Stdout = stdout
119+
cmd.Stdout = w
79120
cmd.Stderr = stderr
80121
if err := cmd.Start(); err != nil {
81122
return err
@@ -115,10 +156,6 @@ func (c *Command) RunInDirWithTimeout(timeout time.Duration, dir string) ([]byte
115156
if err := c.RunInDirPipelineWithTimeout(timeout, stdout, stderr, dir); err != nil {
116157
return nil, concatenateError(err, stderr.String())
117158
}
118-
119-
if stdout.Len() > 0 {
120-
log("stdout:\n%s", stdout.Bytes()[:1024])
121-
}
122159
return stdout.Bytes(), nil
123160
}
124161

Diff for: commit.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ func (c *Commit) Summary() string {
5050
return strings.Split(c.message, "\n")[0]
5151
}
5252

53+
// ParentsCount returns number of parents of the commit.
54+
// It returns 0 if this is the root commit, otherwise returns 1, 2, etc.
55+
func (c *Commit) ParentsCount() int {
56+
return len(c.parents)
57+
}
58+
5359
// ParentID returns the SHA-1 hash of the n-th parent (0-based) of this commit.
5460
// It returns ErrRevisionNotExist if no such parent exists.
5561
func (c *Commit) ParentID(n int) (*SHA1, error) {
@@ -70,12 +76,6 @@ func (c *Commit) Parent(n int, opts ...CatFileCommitOptions) (*Commit, error) {
7076
return c.repo.CatFileCommit(id.String(), opts...)
7177
}
7278

73-
// ParentsCount returns number of parents of the commit.
74-
// It returns 0 if this is the root commit, otherwise returns 1, 2, etc.
75-
func (c *Commit) ParentsCount() int {
76-
return len(c.parents)
77-
}
78-
7979
// CommitByPath returns the commit of the path in the state of this commit.
8080
func (c *Commit) CommitByPath(opts ...CommitByRevisionOptions) (*Commit, error) {
8181
return c.repo.CommitByRevision(c.id.String(), opts...)

Diff for: commit_test.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package git
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestCommit(t *testing.T) {
11+
c, err := testrepo.CatFileCommit("435ffceb7ba576c937e922766e37d4f7abdcc122")
12+
if err != nil {
13+
t.Fatal(err)
14+
}
15+
t.Run("ID", func(t *testing.T) {
16+
assert.Equal(t, "435ffceb7ba576c937e922766e37d4f7abdcc122", c.ID().String())
17+
})
18+
19+
author := &Signature{
20+
Name: "Jordan McCullough",
21+
22+
When: time.Unix(1415213395, 0),
23+
}
24+
t.Run("Author", func(t *testing.T) {
25+
assert.Equal(t, author.Name, c.Author().Name)
26+
assert.Equal(t, author.Email, c.Author().Email)
27+
assert.Equal(t, author.When.Unix(), c.Author().When.Unix())
28+
})
29+
30+
t.Run("Committer", func(t *testing.T) {
31+
assert.Equal(t, author.Name, c.Committer().Name)
32+
assert.Equal(t, author.Email, c.Committer().Email)
33+
assert.Equal(t, author.When.Unix(), c.Committer().When.Unix())
34+
})
35+
36+
t.Run("Message", func(t *testing.T) {
37+
message := `Merge pull request #35 from githubtraining/travis-yml-docker
38+
39+
Add special option flag for Travis Docker use case`
40+
assert.Equal(t, message, c.Message())
41+
})
42+
43+
t.Run("Summary", func(t *testing.T) {
44+
assert.Equal(t, "Merge pull request #35 from githubtraining/travis-yml-docker", c.Summary())
45+
})
46+
}
47+
48+
func TestCommit_Parent(t *testing.T) {
49+
c, err := testrepo.CatFileCommit("435ffceb7ba576c937e922766e37d4f7abdcc122")
50+
if err != nil {
51+
t.Fatal(err)
52+
}
53+
54+
t.Run("ParentsCount", func(t *testing.T) {
55+
assert.Equal(t, 2, c.ParentsCount())
56+
})
57+
58+
t.Run("Parent", func(t *testing.T) {
59+
t.Run("no such parent", func(t *testing.T) {
60+
_, err := c.Parent(c.ParentsCount() + 1)
61+
assert.NotNil(t, err)
62+
assert.Equal(t, `revision does not exist [rev: , path: ]`, err.Error())
63+
})
64+
65+
tests := []struct {
66+
n int
67+
parentID string
68+
}{
69+
{
70+
n: 0,
71+
parentID: "a13dba1e469944772490909daa58c53ac8fa4b0d",
72+
},
73+
{
74+
n: 1,
75+
parentID: "7c5ee6478d137417ae602140c615e33aed91887c",
76+
},
77+
}
78+
for _, test := range tests {
79+
t.Run("", func(t *testing.T) {
80+
p, err := c.Parent(test.n)
81+
if err != nil {
82+
t.Fatal(err)
83+
}
84+
assert.Equal(t, test.parentID, p.ID().String())
85+
})
86+
}
87+
})
88+
}

Diff for: git_test.go

+29-5
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,45 @@ package git
22

33
import (
44
"bytes"
5+
"flag"
56
"fmt"
7+
stdlog "log"
8+
"os"
69
"testing"
710

811
goversion "github.com/mcuadros/go-version"
912
"github.com/stretchr/testify/assert"
1013
"golang.org/x/sync/errgroup"
1114
)
1215

13-
func TestSetOutput(t *testing.T) {
14-
assert.Nil(t, logOutput)
16+
const repoPath = "testdata/testrepo.git"
1517

16-
var buf bytes.Buffer
17-
SetOutput(&buf)
18+
var testrepo *Repository
1819

19-
assert.NotNil(t, logOutput)
20+
func TestMain(m *testing.M) {
21+
verbose := flag.Bool("verbose", false, "")
22+
flag.Parse()
23+
24+
if *verbose {
25+
SetOutput(os.Stdout)
26+
}
27+
28+
// Set up the test repository
29+
if !isExist(repoPath) {
30+
if err := Clone("https://github.com/gogs/git-module-testrepo.git", repoPath, CloneOptions{
31+
Bare: true,
32+
}); err != nil {
33+
stdlog.Fatal(err)
34+
}
35+
}
36+
37+
var err error
38+
testrepo, err = Open(repoPath)
39+
if err != nil {
40+
stdlog.Fatal(err)
41+
}
42+
43+
os.Exit(m.Run())
2044
}
2145

2246
func TestSetPrefix(t *testing.T) {

Diff for: repo.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,16 @@ func Init(path string, opts ...InitOptions) error {
7979

8080
// Open opens the repository at the given path. It returns an os.ErrNotExist
8181
// if the path does not exist.
82-
func Open(path string) (*Repository, error) {
83-
path, err := filepath.Abs(path)
82+
func Open(repoPath string) (*Repository, error) {
83+
repoPath, err := filepath.Abs(repoPath)
8484
if err != nil {
8585
return nil, err
86-
} else if !isDir(path) {
86+
} else if !isDir(repoPath) {
8787
return nil, os.ErrNotExist
8888
}
8989

9090
return &Repository{
91-
path: path,
91+
path: repoPath,
9292
cachedCommits: newObjectCache(),
9393
cachedTags: newObjectCache(),
9494
}, nil

Diff for: signature.go

-19
Original file line numberDiff line numberDiff line change
@@ -43,25 +43,6 @@ func parseSignature(line []byte) (*Signature, error) {
4343
return nil, err
4444
}
4545
sig.When = time.Unix(seconds, 0)
46-
47-
// Handle the timezone shift
48-
timezone := line[timestop+1:]
49-
50-
// Discard malformed timezone
51-
if len(timezone) != 5 {
52-
return sig, err
53-
}
54-
55-
// Add or minus, default to add
56-
var direction int64 = 1
57-
if timezone[0] == '-' {
58-
direction = -1
59-
}
60-
61-
hours, _ := strconv.ParseInt(string(timezone[1:3]), 10, 64)
62-
minutes, _ := strconv.ParseInt(string(timezone[3:]), 10, 64)
63-
sig.When = sig.When.Add(time.Duration(direction*hours) * time.Hour)
64-
sig.When = sig.When.Add(time.Duration(direction*minutes) * time.Minute)
6546
return sig, nil
6647
}
6748

0 commit comments

Comments
 (0)