Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions kadai2/wataboru/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
.DS_Store
107 changes: 107 additions & 0 deletions kadai2/wataboru/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# 課題 2

## 【TRY】io.Readerとio.Writer

### io.Readerとio.Writerについて調べてみよう

- 標準パッケージでどのように使われているか
- io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる

> - 標準パッケージでどのように使われているか

- 実装している標準パッケージ
- bufio
- bytes
- zip
- strings

- どの様に使われているか
- strings

```
func (w *appendSliceWriter) Write(p []byte) (int, error) {
*w = append(*w, p...)
return len(p), nil
}
```

- bufio

```
func (b *Writer) Write(p []byte) (nn int, err error) {
for len(p) > b.Available() && b.err == nil {
var n int
if b.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, b.err = b.wr.Write(p)
} else {
n = copy(b.buf[b.n:], p)
b.n += n
b.Flush()
}
nn += n
p = p[n:]
}
if b.err != nil {
return nn, b.err
}
n := copy(b.buf[b.n:], p)
b.n += n
nn += n
return nn, nil
}
```

- zip

```
func (w *fileWriter) Write(p []byte) (int, error) {
if w.closed {
return 0, errors.New("zip: write to closed file")
}
w.crc32.Write(p)
return w.rawCount.Write(p)
}
```

> - io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる

1. 各パッケージで扱っている書き込み先は全て違うが、同じインターフェースにて抽象化されていること中の実装は隠蔽され、利用者としてはそれぞれ全て等しく `Write` や `Read` で利用することができる。
2. 上記の通り隠蔽されることで、書き込み先や読み込み先という仕様が変更になったとしても、利用するパッケージを変更するだけで実現が可能になる。(変更容易性が上がる)

## 【TRY】テストを書いてみよう

### 1回目の課題のテストを作ってみて下さい
- テストのしやすさを考えてリファクタリングしてみる
- テストのカバレッジを取ってみる
- テーブル駆動テストを行う
- テストヘルパーを作ってみる

## 動作手順

```
$ go test ./imageconverter
$ go test ./commands/imageconverter
```

## カバレッジ

- imageconverter
`./imageconverter_coveage.html`

- main
`./imageconverter_coveage.html`

## 感想等

### io.Readerとio.Writerについて調べてみよう

- Interfaceを利用した

### 【TRY】テストを書いてみよう

- 可能な限り、テスト対象の関数内で利用しているパッケージをMockして、依存を減らす様にしました。(Mockのパッケージを使わないで実現するのに相当苦労しました。)
- 画像のエンコードを、ストラテジーパターンを利用してリファクタリングしました。
- テスタブルなコードを目指すあまり、実装として読みにくくなっていないか気になっています。

69 changes: 69 additions & 0 deletions kadai2/wataboru/commands/imageconverter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"flag"
"fmt"
"os"
"testing"

"github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter"
)

const (
// ExitCodeSuccess is the exit code on success
ExitCodeSuccess = 0
// ExitCodeError is the exit code when failed
ExitCodeError = 1
// ExitCodeError is the exit code when failed
ExitCodeInvalidDirectoryError = 2
)

var (
args imageconverter.Args
osStat func(name string) (os.FileInfo, error)
osIsNotExist func(err error) bool
imgconv func(args imageconverter.Args) error
)

func init() {
testing.Init()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mainのテストにて、flag.Parse() をinitの中で実行していたため、testでflag引数を渡すことができず苦戦しました。こちら、run()側にflag.Parse() を渡すのか、 testing.Init() を呼び出すか、どちらが良いか悩んでいます。あまりテストコードが実装に漏れるのが嫌なのですが。。。他の方法があれば知りたいです。

flag.StringVar(&args.Directory, "dir", "", "Input target Directory.\n ex) `--dir=./convert_image`")
flag.StringVar(&args.Directory, "d", "", "Input target Directory. (short)")
flag.StringVar(&args.BeforeExtension, "before", "jpg", "Input extension before conversion.\n ex) `--before=png`")
flag.StringVar(&args.BeforeExtension, "b", "jpg", "Input extension before conversion. (short)")
flag.StringVar(&args.AfterExtension, "after", "png", "Input extension after conversion.\n ex) `--after=jpg`")
flag.StringVar(&args.AfterExtension, "a", "png", "Input extension after conversion. (short)")
flag.Parse()

osStat = func(name string) (os.FileInfo, error) {
return os.Stat(name)
}
osIsNotExist = func(err error) bool {
return os.IsNotExist(err)
}
imgconv = imageconverter.Convert

}

func run() int {
if args.Directory == "" {
fmt.Fprintln(os.Stderr, "Input target Directory.\n ex) `--dir=./convert_image`")
return ExitCodeInvalidDirectoryError
}

if _, err := osStat(args.Directory); osIsNotExist(err) {
fmt.Fprintln(os.Stderr, "Target directory is not found.")
return ExitCodeInvalidDirectoryError
}

if err := imgconv(args); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
return ExitCodeError
}

return ExitCodeSuccess
}

func main() {
os.Exit(run())
}
138 changes: 138 additions & 0 deletions kadai2/wataboru/commands/imageconverter/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package main

import (
"flag"
"fmt"
"os"
"testing"

"github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter"
)

type mockFns struct {
osStat func(name string) (os.FileInfo, error)
osIsNotExist func(err error) bool
imgconv func(args imageconverter.Args) error
}

func Test_run(t *testing.T) {
tests := []struct {
name string
args imageconverter.Args
mocks mockFns
want int
}{
{
name: "Normal",
args: imageconverter.Args{
Directory: "./hoge",
BeforeExtension: "jpg",
AfterExtension: "png",
},
mocks: mockFns{
osStat: func(name string) (os.FileInfo, error) {
return nil, nil
},
osIsNotExist: func(err error) bool {
return false
},
imgconv: func(args imageconverter.Args) error {
return nil
},
},
want: ExitCodeSuccess,
},
{
name: "ErrorBecauseWithoutArgsDirectory",
args: imageconverter.Args{
Directory: "",
BeforeExtension: "jpg",
AfterExtension: "png",
},
mocks: mockFns{
osStat: func(name string) (os.FileInfo, error) {
return nil, nil
},
osIsNotExist: func(err error) bool {
return false
},
imgconv: func(args imageconverter.Args) error {
return nil
},
},
want: ExitCodeInvalidDirectoryError,
},
{
name: "ErrorBecauseOsIsNotExitReturnError",
args: imageconverter.Args{
Directory: "./hoge",
BeforeExtension: "jpg",
AfterExtension: "png",
},
mocks: mockFns{
osStat: func(name string) (os.FileInfo, error) {
return nil, nil
},
osIsNotExist: func(err error) bool {
return true
},
imgconv: func(args imageconverter.Args) error {
return nil
},
},
want: ExitCodeInvalidDirectoryError,
},
{
name: "ErrorBecauseImgconvReturnError",
args: imageconverter.Args{
Directory: "./hoge",
BeforeExtension: "jpg",
AfterExtension: "png",
},
mocks: mockFns{
osStat: func(name string) (os.FileInfo, error) {
return nil, nil
},
osIsNotExist: func(err error) bool {
return false
},
imgconv: func(args imageconverter.Args) error {
return fmt.Errorf("error")
},
},
want: ExitCodeError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
flagsSet(t, tt.args)
MockSet(t, tt.mocks)
if got := run(); got != tt.want {
t.Errorf("run() = %v, want %v", got, tt.want)
}
})
}
}

func flagsSet(t *testing.T, args imageconverter.Args) {
t.Helper()
if err := flag.CommandLine.Set("dir", args.Directory); err != nil {
t.Errorf("error occurred in flagSet helper: %v", err)

}
if err := flag.CommandLine.Set("before", args.BeforeExtension); err != nil {
t.Errorf("error occurred in flagSet helper: %v", err)

}
if err := flag.CommandLine.Set("after", args.AfterExtension); err != nil {
t.Errorf("error occurred in flagSet helper: %v", err)

}
}

func MockSet(t *testing.T, m mockFns) {
t.Helper()
osStat = m.osStat
osIsNotExist = m.osIsNotExist
imgconv = m.imgconv
}
40 changes: 40 additions & 0 deletions kadai2/wataboru/cover.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
mode: set
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:29.13,33.46 4 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:36.2,37.44 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:40.2,41.33 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:33.46,35.3 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:37.44,39.3 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:57.65,59.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:63.64,65.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:69.64,71.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:75.64,77.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:81.65,83.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:85.52,86.45 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:87.23,88.28 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:89.14,90.27 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:91.14,92.27 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:93.14,94.27 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:95.23,96.28 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:97.10,98.106 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:102.44,104.16 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:107.2,110.16 3 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:114.2,114.15 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:120.2,121.16 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:125.2,126.16 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:129.2,129.32 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:104.16,106.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:110.16,112.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:114.15,115.17 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:115.17,117.4 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:121.16,123.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:126.16,128.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:132.78,133.16 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:137.2,137.18 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:141.2,142.37 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:145.2,145.72 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:133.16,135.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:137.18,139.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:142.37,144.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:151.31,152.92 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:152.92,154.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:157.48,159.2 1 1
8 changes: 8 additions & 0 deletions kadai2/wataboru/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/gopherdojo/dojo8/kadai1/wataboru

go 1.14

require (
golang.org/x/image v0.0.0-20200618115811-c13761719519
golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed // indirect
)
Loading