diff --git a/kadai1/.gitkeep b/kadai2/.gitkeep similarity index 100% rename from kadai1/.gitkeep rename to kadai2/.gitkeep diff --git a/kadai2/shinta/.gitignore b/kadai2/shinta/.gitignore new file mode 100644 index 0000000..830f581 --- /dev/null +++ b/kadai2/shinta/.gitignore @@ -0,0 +1 @@ +testdata \ No newline at end of file diff --git a/kadai2/shinta/README.md b/kadai2/shinta/README.md new file mode 100644 index 0000000..e5e7d10 --- /dev/null +++ b/kadai2/shinta/README.md @@ -0,0 +1,36 @@ +# 課題2 +## io.Readerとio.Writerについて調べてみよう +### 標準パッケージでどのように使われているか +- os(os.Open, os.Create, os.Stdin, os.Stdout, os.Stderr) + - *os.File型、ファイルを開いたりするときに使う +- bytes.Buffer (struct), bytes.Reader (struct) + - ファイルではなくメモリへデータを書き込むのに使う。*bytes.Buffer が io.Writer として利用可能。 + - *bytes.Reader が io.Reader として利用可能。 +- bufio.Scanner + - ファイルや標準入力から作られた io.Reader から1行ずつ文字列を読み込む。 +- io/ioutil.ReadAll, io/ioutil.ReadFile, io/ioutil.WriteFile, io.Copy + - io/ioutil.ReadAll: io.Reader から全てデータを読み込んで[]byte を作成する。 + - io/ioutil.ReadFile: 指定されたファイル名から全てのデータを読み込んで[]byte を作成する。 + - io/ioutil.WriteFile: 指定されたファイル名に[]byte を書き込む。os.Create に合わせるなら第三引数permは0666を渡す。 + - io.Copy: io.Reader から io.Writer にデータを全てコピーする便利関数 + +### io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる +- io.Reader io.Writerを持っている関数であれば、抽象的にIOしていると考えて良い +- 呼び出し側はI/O処理がどんなことをしているのかを理解する必要が無い +- DIPにできる。 + +### test +``` +// 通常のtest +go test ./imageconversion +``` + +``` +// カバレッジ +go test -cover ./imageconversion +go test -coverprofile=cover.out ./imageconversion +go tool cover -html=cover.out -o cover.html +``` + +- エラーケースのテストがまだできていない。 +- test しやすい設計についてもっと考えないといけない。 \ No newline at end of file diff --git a/kadai2/shinta/cover.html b/kadai2/shinta/cover.html new file mode 100644 index 0000000..9195677 --- /dev/null +++ b/kadai2/shinta/cover.html @@ -0,0 +1,228 @@ + + + + + + + + +
+ +
+ not tracked + + not covered + covered + +
+
+
+ + + +
+ + + diff --git a/kadai2/shinta/cover.out b/kadai2/shinta/cover.out new file mode 100644 index 0000000..8cd4670 --- /dev/null +++ b/kadai2/shinta/cover.out @@ -0,0 +1,32 @@ +mode: set +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:26.29,27.28 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:30.2,32.35 3 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:35.2,35.56 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:38.2,38.12 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:27.28,29.3 1 0 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:32.35,34.3 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:35.56,37.3 1 0 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:41.28,43.2 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:53.48,55.2 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:58.53,62.2 3 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:68.47,71.16 3 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:74.2,75.16 2 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:78.2,79.16 2 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:83.2,83.18 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:91.2,93.12 3 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:71.16,73.3 1 0 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:75.16,77.3 1 0 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:79.16,81.3 1 0 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:84.23,85.39 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:86.14,87.38 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:88.10,89.33 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:107.49,109.36 2 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:112.2,113.85 2 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:125.2,125.12 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:109.36,111.3 1 0 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:113.85,114.17 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:117.3,117.39 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:123.3,123.13 1 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:114.17,116.4 1 0 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:117.39,119.18 2 1 +github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion/imageconversion.go:119.18,121.5 1 0 diff --git a/kadai2/shinta/go.mod b/kadai2/shinta/go.mod new file mode 100644 index 0000000..ddc7d85 --- /dev/null +++ b/kadai2/shinta/go.mod @@ -0,0 +1,3 @@ +module github.com/gopherdojo/dojo7/kadai2/shinta + +go 1.12 diff --git a/kadai2/shinta/imageconversion/export_test.go b/kadai2/shinta/imageconversion/export_test.go new file mode 100644 index 0000000..af66041 --- /dev/null +++ b/kadai2/shinta/imageconversion/export_test.go @@ -0,0 +1,6 @@ +package imageconversion + +// var ImageFile imageFile +var GetFileNameWithoutExt = getFileNameWithoutExt +var CreateImgStruct = createImgStruct +var ConvertExec = convertExec diff --git a/kadai2/shinta/imageconversion/imageconversion.go b/kadai2/shinta/imageconversion/imageconversion.go new file mode 100644 index 0000000..c37e0fa --- /dev/null +++ b/kadai2/shinta/imageconversion/imageconversion.go @@ -0,0 +1,129 @@ +/* +Package imageconversion は画像ファイル形式の変換を行います。 +optionで、実行するディレクトリと変換前と変換後の画像形式を指定できます。 +option を指定しない場合、コマンドを実行するディレクトリと、 変換前の画像タイプがjpeg、変換後の画像タイプがpngになります。 +変換可能な拡張子として、jpg、jpeg、png、gif としています。 +*/ +package imageconversion + +import ( + "errors" + "image" + "image/gif" + "image/jpeg" + "image/png" + "os" + "path/filepath" + "strings" +) + +type arg struct { + dir string + preExt string + afterExt string +} + +func (a *arg) valid() error { + if a.preExt == a.afterExt { + return errors.New("変換前と変換後で拡張子が同じです。") + } + allowExtList := []string{"jpg", "jpeg", "png", "gif"} + allowExtMap := map[string]bool{ + "jpg": true, + "jpeg": true, + "png": true, + "gif": true, + } + if !allowExtMap[a.preExt] || !allowExtMap[a.afterExt] { + return errors.New("指定できる拡張子: " + strings.Join(allowExtList, ",")) + } + return nil +} + +// func (a *arg) convertExt() { +// a.preExt, a.afterExt = "."+a.preExt, "."+a.afterExt +// } + +// imageFile struct は変換対象の画像のpath(path)、拡張子を除いたファイル名(base)、拡張子(ext)を持っています。 +type imageFile struct { + Path string + Base string + Ext string +} + +// getFileNameWithoutExt は対象ファイルのpathと拡張子を除いたファイル名を返します。 +func getFileNameWithoutExt(path string) string { + return filepath.Base(path[:len(path)-len(filepath.Ext(path))]) +} + +// createImgStrunct は、imageFile structを生成し、返します。 +func createImgStruct(path string) imageFile { + base := getFileNameWithoutExt(path) + return imageFile{filepath.Dir(path), base, filepath.Ext(path)} +} + +/* +convertExec は画像ファイルを引数で指定された変換後の拡張子(defaultはpng)に変換した新しい画像ファイルを生成します。 +処理が成功するとnil、errorが起きた場合、errorを返します。 +*/ +func convertExec(path, afterExt string) error { + img := createImgStruct(path) + targetImg, err := os.Open(filepath.Join(img.Path, (img.Base + img.Ext))) + if err != nil { + return err + } + readImg, _, err := image.Decode(targetImg) + if err != nil { + return err + } + outputImg, err := os.Create(filepath.Join(img.Path, (img.Base + "." + afterExt))) + if err != nil { + return err + } + + switch afterExt { + case ".jpeg", ".jpg": + jpeg.Encode(outputImg, readImg, nil) + case ".gif": + gif.Encode(outputImg, readImg, nil) + default: + png.Encode(outputImg, readImg) + } + if err = targetImg.Close(); err != nil { + return err + } + err = outputImg.Close() + return err +} + +/* +Excute は画像変換処理を実行します。 +このpackageで呼び出せる唯一の関数です。 +引数で、ディレクトリ(デフォルトは ./)、変換前拡張子(デフォルトは jpg)、変換後拡張子(デフォルトは png)を受け取ります。 +引数が指定されない場合はデフォルトの値が適用されます。 +引数で受け取ったディレクトリ以下の変換前拡張子のファイルを変換後拡張子に変換した新しいファイルを作成します。 +処理が成功の場合、nilをerrorが起きた場合はerrorを返します。 +引数で指定されたディレクトリ以下から引数で指定した変換前拡張子(defaultはjpg)のファイルを、 +変換後拡張子(defaultはpng)に変換した新しい画像ファイルを生成します。 +処理が成功するとnil、errorが起きた場合、errorを返します。 +*/ +func Excute(dir, preExt, afterExt string) error { + arg := &arg{dir, preExt, afterExt} + if err := arg.valid(); err != nil { + return err + } + // arg.convertExt() + err := filepath.Walk(arg.dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if filepath.Ext(path) == ("." + arg.preExt) { + err = convertExec(path, arg.afterExt) + if err != nil { + return err + } + } + return err + }) + return err +} diff --git a/kadai2/shinta/imageconversion/imageconversion_test.go b/kadai2/shinta/imageconversion/imageconversion_test.go new file mode 100644 index 0000000..36423a3 --- /dev/null +++ b/kadai2/shinta/imageconversion/imageconversion_test.go @@ -0,0 +1,109 @@ +package imageconversion_test + +import ( + "errors" + "strings" + "testing" + + "github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion" +) + +type exampleArg struct { + testCase string + dir string + preExt string + afterExt string +} + +type expectedImageFile struct { + path string + base string + ext string +} + +func (a *exampleArg) valid() error { + if a.preExt == a.afterExt { + return errors.New("変換前と変換後で拡張子が同じです。") + } + allowExtList := []string{"jpg", "jpeg", "png", "gif"} + allowExtMap := map[string]bool{} + for _, ext := range allowExtList { + allowExtMap[ext] = true + } + if !allowExtMap[a.preExt] || !allowExtMap[a.afterExt] { + return errors.New("指定できる拡張子: " + strings.Join(allowExtList, ",")) + } + return nil +} + +func TestValid(t *testing.T) { + var exampleArgs = []exampleArg{ + {"カレントディレクトリ以下のjpeg=>png", "./", "jpeg", "png"}, + {"カレントディレクトリ以下のpng=>gif", "./", "png", "gif"}, + {"カレントディレクトリ以下のpng=>jpeg", "./", "png", "jpeg"}, + } + for _, arg := range exampleArgs { + if err := arg.valid(); err != nil { + t.Error("failed to call Imageconversion valid", err, "expected: nil") + } + } + // t.Skip("func valid") +} + +func TestGetFileNamaWithoutExt(t *testing.T) { + var testDatas = []string{"testdata/1.jpeg", "testdata/sub/3.jpeg"} + var expectedImageFiles = []expectedImageFile{ + {"testdata", "1", ".jpeg"}, {"testdata/sub", "3", ".jpeg"}, + } + for i, data := range testDatas { + res := imageconversion.GetFileNameWithoutExt(data) + if res != expectedImageFiles[i].base { + t.Error("failed to call Imageconversion getFileNameWithoutExt", res, "expected", expectedImageFiles[i].base) + } + } + // t.Skip("func getFileNameWithoutExt") +} +func TestCreateImgStruct(t *testing.T) { + var testDatas = []string{"testdata/1.jpeg", "testdata/sub/3.jpeg"} + var expectedImageFiles = []expectedImageFile{ + {"testdata", "1", ".jpeg"}, {"testdata/sub", "3", ".jpeg"}, + } + for i, data := range testDatas { + img := imageconversion.CreateImgStruct(data) + if img.Path != expectedImageFiles[i].path || img.Base != expectedImageFiles[i].base || img.Ext != expectedImageFiles[i].ext { + t.Error("failed to call Imageconversion createImgStruct", img, "expected:", expectedImageFiles[i]) + } + } + // t.Skip("func createImgStruct") +} +func TestConvertExec(t *testing.T) { + var convertedExtArgs = []exampleArg{ + {"カレントディレクトリ以下のjpeg=>png", "./", ".jpeg", ".png"}, + {"カレントディレクトリ以下のpng=>gif", "./", ".png", ".gif"}, + {"カレントディレクトリ以下のpng=>jpeg", "./", ".png", ".jpeg"}, + } + var testDatas = []string{"testdata/1.jpeg", "testdata/sub/3.jpeg"} + for _, arg := range convertedExtArgs { + for _, data := range testDatas { + err := imageconversion.ConvertExec(data, arg.afterExt) + if err != nil { + t.Error("failed to call Imageconversion ConvertExcute", err, "expected: nil") + } + } + } + // t.Skip("func convertExec") +} +func TestImageConversionExcute(t *testing.T) { + var exampleArgs = []exampleArg{ + {"カレントディレクトリ以下のjpeg=>png", "./", "jpeg", "png"}, + {"カレントディレクトリ以下のpng=>gif", "./", "png", "gif"}, + {"カレントディレクトリ以下のpng=>jpeg", "./", "png", "jpeg"}, + } + for _, arg := range exampleArgs { + err := imageconversion.Excute(arg.dir, arg.preExt, arg.afterExt) + if err != nil { + t.Error("failed to call Imageconversion Excute", err, "expected: nil") + } + } + // t.Skip("Execute") +} diff --git a/kadai2/shinta/main.go b/kadai2/shinta/main.go new file mode 100644 index 0000000..c7d1da8 --- /dev/null +++ b/kadai2/shinta/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "os" + + "github.com/gopherdojo/dojo7/kadai2/shinta/imageconversion" +) + +// passArgs は引数を受け取りその引数(ディレクトリ、変換前拡張子、変換後拡張子)が正しいか判別し、引数の値を返します。 +func passArgs() (dir string, preExt string, afterExt string, err error) { + flag.StringVar(&dir, "d", "./", "対象ディレクトリ") + flag.StringVar(&preExt, "p", "jpeg", "変換前拡張子") + flag.StringVar(&afterExt, "a", "png", "変換後拡張子") + if flag.Parse(); flag.Parsed() { + return + } + err = errors.New("引数のparseに失敗しました。") + return +} + +func main() { + dir, preExt, afterExt, err := passArgs() + fmt.Println(dir, preExt, afterExt) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + err = imageconversion.Excute(dir, preExt, afterExt) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +}