diff --git a/kadai2/.gitkeep b/kadai2/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/kadai2/asuke-yasukuni/.gitignore b/kadai2/asuke-yasukuni/.gitignore
new file mode 100644
index 0000000..485dee6
--- /dev/null
+++ b/kadai2/asuke-yasukuni/.gitignore
@@ -0,0 +1 @@
+.idea
diff --git a/kadai2/asuke-yasukuni/README.md b/kadai2/asuke-yasukuni/README.md
new file mode 100644
index 0000000..bab50ce
--- /dev/null
+++ b/kadai2/asuke-yasukuni/README.md
@@ -0,0 +1,54 @@
+## dojo7 [課題2] io.Writer io.Reader調べ。 テスト作成
+
+**io.Readerとio.Writerについて調べる**
+- 標準パッケージでどのように使われているか
+ - バイトを扱う読み込み、書き込み、処理にはほぼ共通的に使われている模様。
+- io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる
+ - 標準パッケージで入出力のインターフェースを定義してくれてるので、機能を作る側も使う側も統一されたインターフェースを扱えるという点で利点がありそう。インターフェースが予め用意されている+共通で使われているおかげでテストも実装しやすい。
+ - randパッケージのrand.Intのテスト見てて思ったが、自分で何かの機能を作るときも io.Readerとかio.Writer型を引数に持たせたりすると使う側が振る舞いをある程度制御できてよさそう(小並感)
+```go
+type countingReader struct {
+ r io.Reader
+ n int
+}
+
+func (r *countingReader) Read(p []byte) (n int, err error) {
+ n, err = r.r.Read(p)
+ r.n += n
+ return n, err
+}
+
+// Test that Int reads only the necessary number of bytes from the reader for
+// max at each bit length
+func TestIntReads(t *testing.T) {
+ for i := 0; i < 32; i++ {
+ max := int64(1 << uint64(i))
+ t.Run(fmt.Sprintf("max=%d", max), func(t *testing.T) {
+ reader := &countingReader{r: rand.Reader}
+
+ _, err := rand.Int(reader, big.NewInt(max))
+ if err != nil {
+ t.Fatalf("Can't generate random value: %d, %v", max, err)
+ }
+ expected := (i + 7) / 8
+ if reader.n != expected {
+ t.Errorf("Int(reader, %d) should read %d bytes, but it read: %d", max, expected, reader.n)
+ }
+ })
+ }
+}
+```
+
+**課題1のテストを書く**
+- テストのしやすさを考えてリファクタリングしてみる
+ - インターフェース使ってmockを作ってみました
+ - https://github.com/gopherdojo/dojo7/pull/21/files#diff-5f0f05a4693bd5628f7d44efae0b1425R8-R12
+ - https://github.com/gopherdojo/dojo7/pull/21/files#diff-5f0f05a4693bd5628f7d44efae0b1425R48
+- テストのカバレッジを取ってみる
+ - 以下のファイルに出力しました
+ - https://github.com/gopherdojo/dojo7/pull/21/files#diff-31cdc2027b482a00d09e119a29101b68R1
+ - https://github.com/gopherdojo/dojo7/pull/21/files#diff-85087c52fb8411bf5f8a97be6c6938adR1
+- テーブル駆動テストを行う
+ - 全体的にテーブル駆動テストにしました
+- テストヘルパーを作ってみる
+ - https://github.com/gopherdojo/dojo7/pull/21/files#diff-5f0f05a4693bd5628f7d44efae0b1425R57-R65
\ No newline at end of file
diff --git a/kadai2/asuke-yasukuni/cover.html b/kadai2/asuke-yasukuni/cover.html
new file mode 100644
index 0000000..bb6f224
--- /dev/null
+++ b/kadai2/asuke-yasukuni/cover.html
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+ not tracked
+
+ not covered
+ covered
+
+
+
+
+
+
// Recursive image encoder command implementation.
+package walk
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+)
+
+type File interface {
+ Encode(path, toExt string) error
+}
+
+type Walk struct {
+ File File
+}
+
+// Recursively search the directory and perform encoding.
+func (w *Walk) Encoder(src *string, fromExt, toExt string) (encodeFiles []string, err error) {
+ err = filepath.Walk(*src, func(path string, info os.FileInfo, err error) error {
+ if filepath.Ext(path) != "."+fromExt {
+ return nil
+ }
+
+ // Use to output.
+ encodeFiles = append(encodeFiles, fmt.Sprintf("%s%s -> %s", "[replace file]", path, toExt))
+
+ if err := w.File.Encode(path, toExt); err != nil {
+ return err
+ }
+
+ return nil
+ })
+
+ return
+}
+
+
+
+
+
+
diff --git a/kadai2/asuke-yasukuni/cover.out b/kadai2/asuke-yasukuni/cover.out
new file mode 100644
index 0000000..2a961eb
--- /dev/null
+++ b/kadai2/asuke-yasukuni/cover.out
@@ -0,0 +1,8 @@
+mode: set
+dojo7/kadai1/asuke-yasukuni/walk/encoder.go:19.94,20.81 1 1
+dojo7/kadai1/asuke-yasukuni/walk/encoder.go:35.2,35.8 1 1
+dojo7/kadai1/asuke-yasukuni/walk/encoder.go:20.81,21.40 1 1
+dojo7/kadai1/asuke-yasukuni/walk/encoder.go:26.3,28.52 2 1
+dojo7/kadai1/asuke-yasukuni/walk/encoder.go:32.3,32.13 1 1
+dojo7/kadai1/asuke-yasukuni/walk/encoder.go:21.40,23.4 1 1
+dojo7/kadai1/asuke-yasukuni/walk/encoder.go:28.52,30.4 1 0
diff --git a/kadai2/asuke-yasukuni/go.mod b/kadai2/asuke-yasukuni/go.mod
new file mode 100644
index 0000000..979b114
--- /dev/null
+++ b/kadai2/asuke-yasukuni/go.mod
@@ -0,0 +1,3 @@
+module github.com/gopherdojo/dojo7/asuke-yasukuni
+
+go 1.12
diff --git a/kadai2/asuke-yasukuni/imgreplacer b/kadai2/asuke-yasukuni/imgreplacer
new file mode 100755
index 0000000..00b340b
Binary files /dev/null and b/kadai2/asuke-yasukuni/imgreplacer differ
diff --git a/kadai2/asuke-yasukuni/main.go b/kadai2/asuke-yasukuni/main.go
new file mode 100644
index 0000000..729d6f5
--- /dev/null
+++ b/kadai2/asuke-yasukuni/main.go
@@ -0,0 +1,38 @@
+package main
+
+import (
+ "flag"
+ "log"
+
+ "github.com/gopherdojo/dojo7/asuke-yasukuni/replacer"
+ "github.com/gopherdojo/dojo7/asuke-yasukuni/validation"
+ "github.com/gopherdojo/dojo7/asuke-yasukuni/walk"
+)
+
+var src = flag.String("src", "", "ファイルパス書いて")
+var from = flag.String("from", "jpg", "変換したい画像の拡張子 jpg or png")
+var to = flag.String("to", "png", "変換後の拡張子 jpg or png")
+
+func main() {
+ flag.Parse()
+
+ // do ext validation
+ if !validation.Ext(*from) || !validation.Ext(*to) {
+ log.Fatalf("\x1b[31mfrom:%s to:%s encode is unsupported\x1b[0m\n", *from, *to)
+ }
+
+ log.Printf("\x1b[33m%s\x1b[0m\n", "[replace start]")
+
+ walker := walk.Walk{File: &replacer.File{}}
+ files, err := walker.Encoder(src, *from, *to)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // encoding result
+ for _, f := range files {
+ log.Print(f)
+ }
+
+ log.Printf("\x1b[33m%s\x1b[0m\n", "[replace end]")
+}
diff --git a/kadai2/asuke-yasukuni/replacer/encode.go b/kadai2/asuke-yasukuni/replacer/encode.go
new file mode 100644
index 0000000..46fe324
--- /dev/null
+++ b/kadai2/asuke-yasukuni/replacer/encode.go
@@ -0,0 +1,69 @@
+// Replacer is a package that can convert to the specified image format (jpg, png) by generating File structure.
+// Supported formats png,jpg
+package replacer
+
+import (
+ "fmt"
+ "image"
+ "image/jpeg"
+ "image/png"
+ "log"
+ "os"
+ "path/filepath"
+)
+
+// A structure that stores image files.
+type File struct{}
+
+// This method encodes an image file into jpg or png.
+func (f *File) Encode(path, to string) error {
+ file, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ defer fileClose(file)
+
+ img, _, err := image.Decode(file)
+ if err != nil {
+ return err
+ }
+
+ // create output file
+ outPath := path[:len(path)-len(filepath.Ext(path))] + "." + to
+ out, err := os.Create(outPath)
+ if err != nil {
+ return err
+ }
+ defer fileClose(out)
+
+ // select encoder
+ switch to {
+ case "jpg":
+ if err := jpeg.Encode(out, img, &jpeg.Options{Quality: 100}); err != nil {
+ return err
+ }
+ case "png":
+ if err := png.Encode(out, img); err != nil {
+ return err
+ }
+ default:
+ // delete fail file
+ if err := os.Remove(outPath); err != nil {
+ return err
+ }
+ return fmt.Errorf("%s is unsupported extension", to)
+ }
+
+ // delete original file
+ if err := os.Remove(path); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func fileClose(file *os.File) {
+ if err := file.Close(); err != nil {
+ log.Printf("\x1b[31m%s:%s\x1b[0m\n", "[encode error]", err)
+ }
+}
diff --git a/kadai2/asuke-yasukuni/replacer/encode_test.go b/kadai2/asuke-yasukuni/replacer/encode_test.go
new file mode 100644
index 0000000..2d52297
--- /dev/null
+++ b/kadai2/asuke-yasukuni/replacer/encode_test.go
@@ -0,0 +1,30 @@
+package replacer
+
+import (
+ "testing"
+)
+
+func TestEncode(t *testing.T) {
+
+ var testCase = []struct {
+ Name string
+ Src string
+ To string
+ Result bool
+ }{
+ {"jpg -> png encode", "../testdata/test-single.jpg", "png", true},
+ {"png -> jpg encode", "../testdata/test-single.png", "jpg", true},
+ {"jpg -> gif not support encode", "../testdata/test-single.jpg", "gif", false},
+ {"not found file encode", "../testdata/test-nofile.gif", "png", false},
+ }
+
+ f := File{}
+ for _, tc := range testCase {
+ t.Run(tc.Name, func(t *testing.T) {
+ err := f.Encode(tc.Src, tc.To)
+ if err != nil && tc.Result {
+ t.Error(err)
+ }
+ })
+ }
+}
diff --git a/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-1.jpg b/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-1.jpg
new file mode 100644
index 0000000..a9a10ac
Binary files /dev/null and b/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-1.jpg differ
diff --git a/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-1.png b/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-1.png
new file mode 100644
index 0000000..d5dc988
Binary files /dev/null and b/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-1.png differ
diff --git a/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-2.jpg b/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-2.jpg
new file mode 100644
index 0000000..a9a10ac
Binary files /dev/null and b/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-2.jpg differ
diff --git a/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-2.png b/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-2.png
new file mode 100644
index 0000000..d5dc988
Binary files /dev/null and b/kadai2/asuke-yasukuni/testdata/multiple_replace/recursiondata/test-2.png differ
diff --git a/kadai2/asuke-yasukuni/testdata/multiple_replace/test-1.jpg b/kadai2/asuke-yasukuni/testdata/multiple_replace/test-1.jpg
new file mode 100644
index 0000000..2ff7ede
Binary files /dev/null and b/kadai2/asuke-yasukuni/testdata/multiple_replace/test-1.jpg differ
diff --git a/kadai2/asuke-yasukuni/testdata/multiple_replace/test-1.png b/kadai2/asuke-yasukuni/testdata/multiple_replace/test-1.png
new file mode 100644
index 0000000..d5dc988
Binary files /dev/null and b/kadai2/asuke-yasukuni/testdata/multiple_replace/test-1.png differ
diff --git a/kadai2/asuke-yasukuni/testdata/multiple_replace/test-2.jpg b/kadai2/asuke-yasukuni/testdata/multiple_replace/test-2.jpg
new file mode 100644
index 0000000..a9a10ac
Binary files /dev/null and b/kadai2/asuke-yasukuni/testdata/multiple_replace/test-2.jpg differ
diff --git a/kadai2/asuke-yasukuni/testdata/multiple_replace/test-2.png b/kadai2/asuke-yasukuni/testdata/multiple_replace/test-2.png
new file mode 100644
index 0000000..d5dc988
Binary files /dev/null and b/kadai2/asuke-yasukuni/testdata/multiple_replace/test-2.png differ
diff --git a/kadai2/asuke-yasukuni/testdata/test-single.jpg b/kadai2/asuke-yasukuni/testdata/test-single.jpg
new file mode 100644
index 0000000..dd107d9
Binary files /dev/null and b/kadai2/asuke-yasukuni/testdata/test-single.jpg differ
diff --git a/kadai2/asuke-yasukuni/validation/extention.go b/kadai2/asuke-yasukuni/validation/extention.go
new file mode 100644
index 0000000..a6e7e9f
--- /dev/null
+++ b/kadai2/asuke-yasukuni/validation/extention.go
@@ -0,0 +1,11 @@
+// Validation package for commands.
+package validation
+
+// Returns true for allowed formats, false otherwise.
+func Ext(ext string) bool {
+ // Because there are few correspondence formats, we do not make map.
+ if ext == "jpg" || ext == "png" {
+ return true
+ }
+ return false
+}
diff --git a/kadai2/asuke-yasukuni/validation/extention_test.go b/kadai2/asuke-yasukuni/validation/extention_test.go
new file mode 100644
index 0000000..a666d1d
--- /dev/null
+++ b/kadai2/asuke-yasukuni/validation/extention_test.go
@@ -0,0 +1,29 @@
+package validation
+
+import (
+ "testing"
+)
+
+func TestExtValidation(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Ext string
+ Result bool
+ }{
+ {Name: "validate success", Ext: "png", Result: true},
+ {Name: "validate success", Ext: "jpg", Result: true},
+ {Name: "validate fail not support", Ext: "gif", Result: false},
+ {Name: "validate fail not support", Ext: "pdf", Result: false},
+ {Name: "validate fail number", Ext: "23456886", Result: false},
+ {Name: "validate fail symbol", Ext: ":;@[]_/23!-^~#/.,", Result: false},
+ {Name: "validate fail symbol number", Ext: ":;@po234[]_/23!-^~#/.,", Result: false},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ if Ext(tc.Ext) != tc.Result {
+ t.Fatalf("%s %s", "ext", tc.Ext)
+ }
+ })
+ }
+}
diff --git a/kadai2/asuke-yasukuni/walk/encoder.go b/kadai2/asuke-yasukuni/walk/encoder.go
new file mode 100644
index 0000000..ccbf666
--- /dev/null
+++ b/kadai2/asuke-yasukuni/walk/encoder.go
@@ -0,0 +1,36 @@
+// Recursive image encoder command implementation.
+package walk
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+)
+
+type File interface {
+ Encode(path, toExt string) error
+}
+
+type Walk struct {
+ File File
+}
+
+// Recursively search the directory and perform encoding.
+func (w *Walk) Encoder(src *string, fromExt, toExt string) (encodeFiles []string, err error) {
+ err = filepath.Walk(*src, func(path string, info os.FileInfo, err error) error {
+ if filepath.Ext(path) != "."+fromExt {
+ return nil
+ }
+
+ // Use to output.
+ encodeFiles = append(encodeFiles, fmt.Sprintf("%s%s -> %s", "[replace file]", path, toExt))
+
+ if err := w.File.Encode(path, toExt); err != nil {
+ return err
+ }
+
+ return nil
+ })
+
+ return
+}
diff --git a/kadai2/asuke-yasukuni/walk/encoder_test.go b/kadai2/asuke-yasukuni/walk/encoder_test.go
new file mode 100644
index 0000000..055135b
--- /dev/null
+++ b/kadai2/asuke-yasukuni/walk/encoder_test.go
@@ -0,0 +1,65 @@
+package walk
+
+import (
+ "reflect"
+ "testing"
+)
+
+type testFile struct{}
+
+func (t *testFile) Encode(path, toExt string) error {
+ return nil
+}
+
+func TestWalkEncoder(t *testing.T) {
+
+ src := "../testdata/multiple_replace/"
+
+ testCase := []struct {
+ Name string
+ From string
+ To string
+ Files []string
+ }{
+ {
+ Name: "walk jpg -> png",
+ From: "jpg",
+ To: "png",
+ Files: []string{
+ "[replace file]../testdata/multiple_replace/recursiondata/test-1.jpg -> png",
+ "[replace file]../testdata/multiple_replace/recursiondata/test-2.jpg -> png",
+ "[replace file]../testdata/multiple_replace/test-1.jpg -> png",
+ "[replace file]../testdata/multiple_replace/test-2.jpg -> png",
+ },
+ },
+ {
+ Name: "walk png -> jpg",
+ From: "png",
+ To: "jpg",
+ Files: []string{
+ "[replace file]../testdata/multiple_replace/recursiondata/test-1.png -> jpg",
+ "[replace file]../testdata/multiple_replace/recursiondata/test-2.png -> jpg",
+ "[replace file]../testdata/multiple_replace/test-1.png -> jpg",
+ "[replace file]../testdata/multiple_replace/test-2.png -> jpg",
+ },
+ },
+ }
+
+ walker := Walk{File: &testFile{}}
+ for _, tc := range testCase {
+ t.Run(tc.Name, func(t *testing.T) {
+ files, err := walker.Encoder(&src, tc.From, tc.To)
+ testDeepEqual(t, tc.Files, files, err)
+ })
+ }
+}
+
+func testDeepEqual(t *testing.T, tcFiles, files []string, err error) {
+ t.Helper()
+ if err != nil {
+ t.Error(err)
+ }
+ if !reflect.DeepEqual(tcFiles, files) {
+ t.Fatal(files)
+ }
+}