Skip to content
This repository was archived by the owner on Aug 12, 2022. It is now read-only.

Commit ec38e00

Browse files
authored
Create contest with default template (#53)
1 parent 9805414 commit ec38e00

File tree

7 files changed

+216
-9
lines changed

7 files changed

+216
-9
lines changed

internal/cmd/ach/ach.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ach
22

33
import (
44
"github.com/spf13/cobra"
5+
"github.com/yuchiki/atcoderHelper/internal/cmd/ach/contest"
56
"github.com/yuchiki/atcoderHelper/internal/cmd/ach/version"
67
)
78

@@ -19,6 +20,7 @@ func NewAchCmd() *cobra.Command {
1920

2021
func registerSubcommands(cmd *cobra.Command) {
2122
cmd.AddCommand(version.NewVersionCmd())
23+
cmd.AddCommand(contest.NewContestCmd())
2224
}
2325

2426
/*

internal/cmd/ach/ach_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88

99
func TestAch_Execute(t *testing.T) {
1010
testutil.TestCaseTemplates{
11-
testutil.RootSucceeds(),
12-
testutil.HasSubcommand("version"),
11+
testutil.HasName("ach"),
12+
testutil.HasSubcommands("version", "contest"),
1313
}.
1414
Build(NewAchCmd).
1515
Run(t)

internal/cmd/ach/contest/contest.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package contest
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
"github.com/yuchiki/atcoderHelper/internal/cmd/ach/contest/create"
6+
)
7+
8+
func NewContestCmd() *cobra.Command {
9+
cmd := &cobra.Command{
10+
Use: "contest",
11+
Short: "manipulates an AtCoder contest",
12+
Long: `manipulates an AtCoder contest.`,
13+
}
14+
15+
registerSubcommands(cmd)
16+
17+
return cmd
18+
}
19+
20+
func registerSubcommands(cmd *cobra.Command) {
21+
cmd.AddCommand(create.NewContestCreateCmd())
22+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package contest
2+
3+
import (
4+
"testing"
5+
6+
"github.com/yuchiki/atcoderHelper/internal/testutil"
7+
)
8+
9+
func TestAch_Execute(t *testing.T) {
10+
testutil.TestCaseTemplates{
11+
testutil.HasName("contest"),
12+
testutil.HasSubcommands("create"),
13+
}.
14+
Build(NewContestCmd).
15+
Run(t)
16+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package create
2+
3+
import (
4+
"log"
5+
"os/exec"
6+
"os/user"
7+
"path"
8+
9+
"github.com/spf13/cobra"
10+
)
11+
12+
// NewContestCreateCmd returns a new contest create command.
13+
func NewContestCreateCmd() *cobra.Command {
14+
user, _ := user.Current()
15+
templateDirName := path.Join(user.HomeDir, "projects", "private", "atcoder", "D")
16+
taskNames := []string{"A", "B", "C", "D", "E", "F"}
17+
18+
useDefaultTemplate := new(bool)
19+
20+
cmd := &cobra.Command{
21+
Use: "create [contestName]",
22+
Short: "creates contest directory",
23+
Long: "creates contest directory.",
24+
Args: cobra.ExactArgs(1),
25+
RunE: func(cmd *cobra.Command, args []string) error {
26+
contestName := args[0]
27+
err := exec.Command("mkdir", contestName).Run()
28+
if err != nil {
29+
return err
30+
}
31+
32+
for _, taskName := range taskNames {
33+
taskDirName := path.Join(contestName, taskName)
34+
35+
err := exec.Command("cp", "-r", templateDirName, taskDirName).Run()
36+
if err != nil {
37+
return err
38+
}
39+
}
40+
41+
return nil
42+
},
43+
}
44+
45+
cmd.Flags().BoolVarP(useDefaultTemplate, "default-template", "d", false, "use default contest template")
46+
47+
if cmd.MarkFlagRequired("default-template") != nil {
48+
log.Fatal("default-template require")
49+
}
50+
51+
return cmd
52+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package create
2+
3+
import (
4+
"testing"
5+
6+
"github.com/yuchiki/atcoderHelper/internal/testutil"
7+
)
8+
9+
func TestCreate_Execute(t *testing.T) {
10+
testutil.TestCaseTemplates{
11+
testutil.HasName("create"),
12+
// This is a temporal implementation with temporal args, so the test are given later.
13+
}.
14+
Build(NewContestCreateCmd).
15+
Run(t)
16+
}

internal/testutil/testutil.go

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"errors"
66
"fmt"
7+
"strings"
78
"testing"
89

910
"github.com/google/go-cmp/cmp"
@@ -39,6 +40,12 @@ type (
3940

4041
// TestCaseTemplates is a suit of testCaseTemplates.
4142
TestCaseTemplates []TestCaseTemplate
43+
44+
// HelpInfo contains information obtained from help message.
45+
HelpInfo struct {
46+
CommandName string
47+
Subcommands []string
48+
}
4249
)
4350

4451
// OutputShouldBe returns a check for output.
@@ -52,6 +59,34 @@ func OutputShouldBe(expected string) OutputCheck {
5259
}
5360
}
5461

62+
// HelpShouldShowCommandName returns a check for command name.
63+
func HelpShouldShowCommandName(name string) OutputCheck {
64+
return func(t *testing.T, output string) {
65+
t.Helper()
66+
67+
help := ParseHelp(t, output)
68+
69+
if help.CommandName != name {
70+
t.Errorf("expected %v, but actual %v", name, help.CommandName)
71+
}
72+
}
73+
}
74+
75+
// HelpShouldShowSubcommands returns a check for subcommands.
76+
func HelpShouldShowSubcommands(subcommands []string) OutputCheck {
77+
return func(t *testing.T, output string) {
78+
t.Helper()
79+
80+
help := ParseHelp(t, output)
81+
82+
for _, subcommand := range subcommands {
83+
if !Contains(subcommand, help.Subcommands) {
84+
t.Errorf("subcommand %v is not included in %v", subcommand, help.Subcommands)
85+
}
86+
}
87+
}
88+
}
89+
5590
// ErrorShouldBe returns a check for error.
5691
func ErrorShouldBe(expected error) ErrorCheck {
5792
return func(t *testing.T, err error) {
@@ -158,17 +193,81 @@ func (cts TestCaseTemplates) Build(cmdBuilder func() *cobra.Command) TestCases {
158193
return TestCases(testCases)
159194
}
160195

161-
// RootSucceeds returns a template which tests if the root succeeds.
162-
func RootSucceeds() TestCaseTemplate {
196+
// HasName returns a template which tests if the root -h succeeds and shows the name.
197+
func HasName(name string) TestCaseTemplate {
163198
return TestCaseTemplate{
164-
Name: "root succeeds",
199+
Name: fmt.Sprintf("root has Name %s", name),
200+
Args: []string{"-h"},
201+
OutputCheck: HelpShouldShowCommandName(name),
165202
}
166203
}
167204

168-
// HasSubcommand returns a template which tests if it has the subcommand and it succeeds.
169-
func HasSubcommand(name string) TestCaseTemplate {
205+
// HasSubcommands returns a template which tests if it has the subcommand and it succeeds.
206+
func HasSubcommands(subcommands ...string) TestCaseTemplate {
170207
return TestCaseTemplate{
171-
Name: fmt.Sprintf("has Subcommand %s and it succeeds", name),
172-
Args: []string{name},
208+
Name: fmt.Sprintf("has Subcommands %s and it succeeds", subcommands),
209+
Args: []string{"-h"},
210+
OutputCheck: HelpShouldShowSubcommands(subcommands),
211+
}
212+
}
213+
214+
// ParseHelp parses a help message.
215+
func ParseHelp(t *testing.T, helpMessage string) HelpInfo {
216+
t.Helper()
217+
218+
lines := strings.Split(helpMessage, "\n")
219+
220+
literal := map[string][]string{}
221+
222+
i := 0
223+
for i < len(lines) {
224+
if lines[i] == "" {
225+
i++
226+
227+
continue
228+
}
229+
230+
if !strings.HasSuffix(lines[i], ":") {
231+
i++
232+
233+
continue
234+
}
235+
236+
key := strings.TrimSuffix(lines[i], ":")
237+
i++
238+
239+
for j := i; j < len(lines); j++ {
240+
if !strings.HasPrefix(lines[j], " ") {
241+
break
242+
}
243+
244+
literal[key] = append(literal[key], strings.TrimSpace(lines[j]))
245+
}
173246
}
247+
248+
if len(literal["Usage"]) == 0 {
249+
t.Fatal("help message has no Usage section")
250+
}
251+
252+
cmdName := strings.Fields(literal["Usage"][0])[0]
253+
254+
subcommands := []string{}
255+
for _, cmdDescriptions := range literal["Available Commands"] {
256+
subcommands = append(subcommands, strings.Fields(cmdDescriptions)[0])
257+
}
258+
259+
return HelpInfo{
260+
CommandName: cmdName,
261+
Subcommands: subcommands,
262+
}
263+
}
264+
265+
func Contains(str string, arr []string) bool {
266+
for _, elem := range arr {
267+
if str == elem {
268+
return true
269+
}
270+
}
271+
272+
return false
174273
}

0 commit comments

Comments
 (0)