Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions kadai1/en-ken/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

out/
44 changes: 44 additions & 0 deletions kadai1/en-ken/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 課題1

## 次の仕様を満たすコマンドを作って下さい

- [x] ディレクトリを指定する
- [x] 指定したディレクトリ以下のJPGファイルをPNGに変換 (デフォルト)
- [x] ディレクトリ以下は再帰的に処理する
- [ ] 変換前と変換後の画像形式を指定できる (オプション)

## 以下を満たすように開発してください

- [x] mainパッケージと分離する
- [x] 自作パッケージと標準パッケージと準標準パッケージのみ使う
- [x] 準標準パッケージ:golang.org/x以下のパッケージ
- [x] ユーザ定義型を作ってみる
- [ ] GoDocを生成してみる

## 使い方

```:go
go build -o kadai1
./kadai1 INPUT_DIR -input-ext [input extension(.jpg/.png)] -output-dir [output directory] -output-ext [output extension(.jpg/.png)]
```

- `-output-dir`は指定されない場合、`INPUT_DIR`に出力される。
- 出力ディレクトリには、`INPUT_DIR`の各処理ファイルと同じディレクトリ構造を保った形で出力される。

## ハマったところ

- os.FileInfoをスライスにappendして返したらヌルポになった。
- ポインタで渡そうにもinterfaceだし、どうやって渡す?
- ひとまずファイルパスだけ返してごまかした。
- go testが最初うまく走らなかった。
- modファイルができてから動くようになった?
- `INPUT_DIRECTORY`を`-output-dir`のデフォルト値に取りたかった関係で、`INPUT_DIRECTORY`を第一引数に取る形にした。
- よく考えると`-help`などするとき変なことになる...
- cliのテストをするために結構無駄な構造を作る羽目になってしまった。
- テストはgolang.org/x以下じゃなくてもセーフという解釈で、gomock使っている。
- コンストラクタでmockに入れ替える以外でスタブを差し込む方法がわからなかった。
- DIコンテナとか、あまり一般的ではない?
- 上から差し込むためにFactoryクラスを作ったが、実装上あまり意味がない構造をテストのために作ってしまい、ちょっと気持ち悪い。
- package自体のinterfaceってないのだろうか。
- GoDocの生成というのがよくわからなかった。masterにマージされればできる?
- go.modとかgo.sumとかなんとなくバージョン管理のためのものなのだと思うがよくわかっていない。
68 changes: 68 additions & 0 deletions kadai1/en-ken/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import (
"flag"
"path/filepath"
"strings"

"github.com/gopherdojo/dojo6/kadai1/en-ken/imgcnv"
)

// CLI is for DI
type CLI struct {
dirPath imgcnv.DirPath
imageFactory imgcnv.ImageFileFactory
}

// Execute executes this app according to options
// arg[0] application name
// arg[1] input directory
// arg[2:] options
func (cli *CLI) Execute(args []string) error {

flags := flag.NewFlagSet(args[0], flag.ExitOnError)
var (
inputExt = flags.String("input-ext", ".jpg", "input extension (.jpg/.png)")
outputExt = flags.String("output-ext", ".png", "output extension (.jpg/.png)")
output = flags.String("output-dir", args[1], "output directory")
inputDir string
outputDir string
)

inputDir, err := filepath.Abs(args[1])
if err != nil {
return err
}

if err := flags.Parse(args[2:]); err != nil {
return err
}

outputDir = *output
if outputDir == "" {
outputDir = inputDir
}

paths, err := cli.dirPath.AllFilePaths(inputDir, *inputExt)
if err != nil {
return err
}

for _, path := range paths {
img, err := cli.imageFactory.Create(path)
if err != nil {
return err
}

// Copy the hierarchy of the input dir to that of the output dir.
outputPath := strings.Replace(img.AbsPath(), inputDir, outputDir, -1)
outputPath = strings.Replace(outputPath, *inputExt, *outputExt, 1)

err = img.SaveAs(outputPath)
if err != nil {
return err
}
}

return nil
}
98 changes: 98 additions & 0 deletions kadai1/en-ken/cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package main

import (
"path/filepath"
"strings"
"testing"

"github.com/golang/mock/gomock"
"github.com/gopherdojo/dojo6/kadai1/en-ken/mock_imgcnv"
)

func TestExecuteSuccess(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

tests := []struct {
argString string
inputDir string
inputExt string
outputDir string
outputExt string
}{
{
argString: "./kadai1 ./testdata",
inputDir: "./testdata", inputExt: ".jpg",
outputDir: "./testdata", outputExt: ".png",
},
{
argString: "./kadai1 ./testdata -input-ext .png",
inputDir: "./testdata", inputExt: ".png",
outputDir: "./testdata", outputExt: ".png",
},
{
argString: "./kadai1 ./testdata -input-ext .png -output-ext .jpg",
inputDir: "./testdata", inputExt: ".png",
outputDir: "./testdata", outputExt: ".jpg",
},
{
argString: "./kadai1 ./testdata -output-dir ./out",
inputDir: "./testdata", inputExt: ".jpg",
outputDir: "./out", outputExt: ".png",
},
}

for _, test := range tests {
testName := "input: " + test.argString
t.Run(testName, func(t *testing.T) {

mockDirPath := mock_imgcnv.NewMockDirPath(ctrl)

inputAbsDir, _ := filepath.Abs(test.inputDir)
inputPath1 := inputAbsDir + "/test1" + test.inputExt
inputPath2 := inputAbsDir + "/test2" + test.inputExt

mockDirPath.
EXPECT().
AllFilePaths(gomock.Eq(inputAbsDir), gomock.Eq(test.inputExt)).
Return([]string{inputPath1, inputPath2}, nil)

mockImg := mock_imgcnv.NewMockImageFile(ctrl)
gomock.InOrder(
mockImg.
EXPECT().
AbsPath().
Return(inputPath1),
mockImg.
EXPECT().
AbsPath().
Return(inputPath2),
)

outputPath1 := test.outputDir + "/test1" + test.outputExt
outputPath2 := test.outputDir + "/test2" + test.outputExt
gomock.InOrder(
mockImg.
EXPECT().
SaveAs(gomock.Eq(outputPath1)),
mockImg.
EXPECT().
SaveAs(gomock.Eq(outputPath2)),
)

mockImgFactory := mock_imgcnv.NewMockImageFileFactory(ctrl)
mockImgFactory.
EXPECT().
Create(gomock.Any()).
Return(mockImg, nil).
AnyTimes()

cli := &CLI{
dirPath: mockDirPath,
imageFactory: mockImgFactory,
}
cli.Execute(strings.Split(test.argString, " "))

})
}
}
5 changes: 5 additions & 0 deletions kadai1/en-ken/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/gopherdojo/dojo6/kadai1/en-ken

go 1.12

require github.com/golang/mock v1.3.1
10 changes: 10 additions & 0 deletions kadai1/en-ken/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/golang/image v0.0.0-20190703141733-d6a02ce849c9 h1:ijbHThKv+QPsGeQnJ0/DiSrRLBR2QlQtRVcl1u2JhR8=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
56 changes: 56 additions & 0 deletions kadai1/en-ken/imgcnv/dirpath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package imgcnv

import (
"io/ioutil"
"path/filepath"
)

// DirPath expresses I/F to DirPathStruct
type DirPath interface {
AllFilePaths(path string, ext string) ([]string, error)
}

// DirPathStruct expresses searching dir
type DirPathStruct struct {
}

// NewDirPath is a constructor of DirPath
func NewDirPath() DirPath {
return &DirPathStruct{}
}

// AllFilePaths returns
// the paths of the files in the specified directory
// filtered by the specified extension.
func (dirPath *DirPathStruct) AllFilePaths(path string, ext string) ([]string, error) {
absPath, err := filepath.Abs(path)
if err != nil {
return nil, err
}

return searchFiles(absPath, ext)
}

func searchFiles(dirPath string, ext string) ([]string, error) {

files, err := ioutil.ReadDir(dirPath)
if err != nil {
return nil, err
}

result := make([]string, 0) // allocate 0 items to merge 2 slices
for _, file := range files {
path := filepath.Join(dirPath, file.Name())
if file.IsDir() {
paths, _ := searchFiles(path, ext)
result = append(result, paths...)
} else {
// ignore error
isMatch, _ := filepath.Match("*"+ext, file.Name())
if isMatch {
result = append(result, path)
}
}
}
return result, nil
}
52 changes: 52 additions & 0 deletions kadai1/en-ken/imgcnv/dirpath_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package imgcnv

import (
"path/filepath"
"sort"
"testing"
)

func TestAllFilePathsSuccess(t *testing.T) {
dirPath := NewDirPath()
actual, _ := dirPath.AllFilePaths("../testdata/", "jpg")
expected := []string{
"../testdata/lenna_color.jpg",
"../testdata/lenna_gray.jpg",
"../testdata/layer1/girl_color.jpg",
"../testdata/layer1/girl_gray.jpg",
"../testdata/layer1/layer2/Mandrill.jpg",
}
for i := 0; i < len(expected); i++ {
expected[i], _ = filepath.Abs(expected[i])
}

if isEqualArray(actual, expected) == false {
t.Errorf("\nactual:%v\nexpected:%v", actual, expected)
}
}

func TestAllFilePathsFailure(t *testing.T) {
dirPath := NewDirPath()
_, err := dirPath.AllFilePaths("../foo/", "jpg")

if err == nil {
t.Errorf("directory does not exist")
}
}

func compare(x string, y string) bool {
return x < y
}
func isEqualArray(array1 []string, array2 []string) bool {
sort.Slice(array1, func(i, j int) bool { return array1[i] > array1[j] })
sort.Slice(array2, func(i, j int) bool { return array2[i] > array2[j] })
if len(array1) != len(array2) {
return false
}
for i := 0; i < len(array1); i++ {
if array1[i] != array2[i] {
return false
}
}
return true
}
Loading