From 8a7dcf16864e04dbf767e847ea6a584023a09ddf Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Thu, 8 Nov 2018 15:52:32 +0900 Subject: [PATCH 01/39] add kadai1 files :lol: --- kadai1/tomoyukikobayashi/.gitignore | 1 + kadai1/tomoyukikobayashi/Makefile | 20 +++++ kadai1/tomoyukikobayashi/README.md | 32 +++++++ .../tomoyukikobayashi/src/file/file_finder.go | 77 ++++++++++++++++ .../tomoyukikobayashi/src/imconv/formats.go | 54 +++++++++++ .../src/imconv/formats_test.go | 73 +++++++++++++++ .../src/imconv/image_converter.go | 71 +++++++++++++++ kadai1/tomoyukikobayashi/src/main.go | 89 +++++++++++++++++++ 8 files changed, 417 insertions(+) create mode 100644 kadai1/tomoyukikobayashi/.gitignore create mode 100644 kadai1/tomoyukikobayashi/Makefile create mode 100644 kadai1/tomoyukikobayashi/README.md create mode 100644 kadai1/tomoyukikobayashi/src/file/file_finder.go create mode 100644 kadai1/tomoyukikobayashi/src/imconv/formats.go create mode 100644 kadai1/tomoyukikobayashi/src/imconv/formats_test.go create mode 100644 kadai1/tomoyukikobayashi/src/imconv/image_converter.go create mode 100644 kadai1/tomoyukikobayashi/src/main.go diff --git a/kadai1/tomoyukikobayashi/.gitignore b/kadai1/tomoyukikobayashi/.gitignore new file mode 100644 index 0000000..78d2089 --- /dev/null +++ b/kadai1/tomoyukikobayashi/.gitignore @@ -0,0 +1 @@ +src/testdata* diff --git a/kadai1/tomoyukikobayashi/Makefile b/kadai1/tomoyukikobayashi/Makefile new file mode 100644 index 0000000..f96d59a --- /dev/null +++ b/kadai1/tomoyukikobayashi/Makefile @@ -0,0 +1,20 @@ +.PHONY: deps +deps: + go get github.com/golang/lint/golint + +.PHONY: build +build: deps + go build -o imconv + +.PHONY: test +test: deps + go test -v ./... + +.PHONY: lint +lint: deps + go vet ./... + golint ./... + +.PHONY: fmt +fmt: deps + go fmt *.go \ No newline at end of file diff --git a/kadai1/tomoyukikobayashi/README.md b/kadai1/tomoyukikobayashi/README.md new file mode 100644 index 0000000..a7cbe6f --- /dev/null +++ b/kadai1/tomoyukikobayashi/README.md @@ -0,0 +1,32 @@ +GoImageConverter +===== + +# Overview + +GoImageConverterはGolangで書かれた画像コンバーターです。 + +# SetUp + +下記のようにコマンドを叩くと、実行形式のimconvファイルが生成されます +``` +cd kadai1/src/ +make build +``` + +# Usage + +実行時の引数は下記の通りです。 +オプションの後に、ディレクトリ名を引数として与えてください +``` +./imconv [OPTIONS] dirname + -f string + input image file format (default "jpg") + -t string + output image file format (default "png") +Supported formats are [jpg jpeg png] +``` + +オプションについて知りたい場合は、下記のようにしてヘルプを参照してください +``` +./imconv --help +``` \ No newline at end of file diff --git a/kadai1/tomoyukikobayashi/src/file/file_finder.go b/kadai1/tomoyukikobayashi/src/file/file_finder.go new file mode 100644 index 0000000..e9252a5 --- /dev/null +++ b/kadai1/tomoyukikobayashi/src/file/file_finder.go @@ -0,0 +1,77 @@ +/* +Package file provides methods and structs +that handle file and directory operations. +Almost all of them just wrap primitive methods +defined in golang native packages. +*/ +package file + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" +) + +// Find returns all filepaths in directory specified as dir. +// If you want to filter the paths by file extensions, use exts. +func Find(dir string, exts []string) ([]string, error) { + paths, err := find(dir) + if err != nil { + return nil, err + } + + return paths.filter(exts), nil +} + +// find dirで指定したディレクトリ配下のファイルパス一覧を返却 +func find(dir string) (paths, error) { + files, err := ioutil.ReadDir(dir) + if err != nil { + return nil, fmt.Errorf("could not open dir %s", dir) + } + + paths := paths{} + for _, file := range files { + path := filepath.Join(dir, file.Name()) + // FileInfoがディレクトリなら再帰処理 + if file.IsDir() { + dsc, err := find(path) + if err != nil { + return nil, fmt.Errorf("could not open dir %s", path) + } + // ...を付けるとslice同士連結できるよう + paths = append(paths, dsc...) + continue + } + paths = append(paths, path) + } + + return paths, nil +} + +// paths ディレクトリパスの集合に対する操作を提供する +type paths []string + +// filter extsで指定した拡張子にマッチする結果を絞り込む +// filter(条件1).filter(条件2) のようにして順に絞り込んでもよい +func (p paths) filter(exts []string) paths { + paths := paths{} + for _, v := range p { + if matches(v, exts) { + paths = append(paths, v) + } + } + return paths +} + +// matches ignore caseでpathがextsで指定した拡張子にマッチするかを検証する +func matches(path string, exts []string) bool { + lpath := strings.ToLower(path) + for _, v := range exts { + if strings.HasSuffix(lpath, "."+strings.ToLower(v)) { + return true + } + } + return false +} diff --git a/kadai1/tomoyukikobayashi/src/imconv/formats.go b/kadai1/tomoyukikobayashi/src/imconv/formats.go new file mode 100644 index 0000000..b004f39 --- /dev/null +++ b/kadai1/tomoyukikobayashi/src/imconv/formats.go @@ -0,0 +1,54 @@ +package imconv + +import "strings" + +// HACK 本当はimmutableにしたいけどどうもできなそう +// ポインタだししょうがないか。。。厳密にやるなら直接触らせないのがいいかな +var ( + jpgExts = []string{"jpg", "jpeg"} + pngExts = []string{"png"} + supportExts = [][]string{ + jpgExts, + pngExts, + } + allSupportExts = append(jpgExts, pngExts...) +) + +// Supported returns if the package can handle speficied file format +func Supported(ext string) bool { + for _, spext := range allSupportExts { + if strings.ToLower(ext) == spext { + return true + } + } + return false +} + +// SupportedExtensions returns image formats the package can handle +func SupportedExtensions() []string { + clone := make([]string, len(allSupportExts)) + copy(clone, allSupportExts) + return clone +} + +// GetFormatThesaurus returns image formats that are the same with arg ext +func GetFormatThesaurus(ext string) []string { + for _, spexts := range supportExts { + for _, spext := range spexts { + if strings.ToLower(ext) == spext { + return spexts + } + } + } + return nil +} + +func isSameFormat(from string, to string) bool { + th := GetFormatThesaurus(from) + for _, v := range th { + if strings.ToLower(to) == v { + return true + } + } + return false +} diff --git a/kadai1/tomoyukikobayashi/src/imconv/formats_test.go b/kadai1/tomoyukikobayashi/src/imconv/formats_test.go new file mode 100644 index 0000000..f2130f6 --- /dev/null +++ b/kadai1/tomoyukikobayashi/src/imconv/formats_test.go @@ -0,0 +1,73 @@ +package imconv + +import ( + "reflect" + "testing" +) + +func Test_Supported(t *testing.T) { + tests := []struct { + ext string + want bool + }{ + {"jpg", true}, + {"jpeg", true}, + {"JPG", true}, + {"png", true}, + {"invalid", false}, + {"", false}, + } + + for _, tt := range tests { + t.Run(tt.ext, func(t *testing.T) { + if got := Supported(tt.ext); got != tt.want { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } +} + +func Test_GetFormatThesaurus(t *testing.T) { + tests := []struct { + ext string + want []string + }{ + {"jpg", jpgExts}, + {"JPG", jpgExts}, + {"jpeg", jpgExts}, + {"png", pngExts}, + {"invalid", nil}, + {"", nil}, + } + + for _, tt := range tests { + t.Run(tt.ext, func(t *testing.T) { + if got := GetFormatThesaurus(tt.ext); !reflect.DeepEqual(got, tt.want) { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } +} + +func Test_isSameFormat(t *testing.T) { + tests := []struct { + from string + to string + want bool + }{ + {"jpg", "jpeg", true}, + {"JPG", "jpeg", true}, + {"jpeg", "jpeg", true}, + {"JPG", "png", false}, + {"png", "invalid", false}, + {"invalid", "invalid", false}, + } + + for _, tt := range tests { + t.Run(tt.from+"_"+tt.to, func(t *testing.T) { + if got := isSameFormat(tt.from, tt.to); got != tt.want { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } +} diff --git a/kadai1/tomoyukikobayashi/src/imconv/image_converter.go b/kadai1/tomoyukikobayashi/src/imconv/image_converter.go new file mode 100644 index 0000000..ef12d0f --- /dev/null +++ b/kadai1/tomoyukikobayashi/src/imconv/image_converter.go @@ -0,0 +1,71 @@ +/* +Package imconv provides image convert mathods and +some validation logics, like if the package can handle +input format or not. +*/ +package imconv + +import ( + "fmt" + "image" + "image/jpeg" + "image/png" + "log" + "os" + "strings" +) + +// Convert convert image from from fromat to to fromat +func Convert(path string, from string, to string) (string, error) { + if !Supported(from) { + return "", fmt.Errorf("can not handle %s", from) + } + + if !Supported(to) { + return "", fmt.Errorf("can not handle %s", to) + } + + if isSameFormat(from, to) { + return "", fmt.Errorf("%s and %s are the same format", from, to) + } + + parts := strings.Split(path, ".") + newPath := strings.Join(parts[:len(parts)-1], ".") + "." + to + + src, err := os.Open(path) + if err != nil { + log.Fatal(err) + return "", fmt.Errorf("cannot open file %s", path) + } + defer src.Close() + + img, _, err := image.Decode(src) + if err != nil { + log.Fatal(err) + return "", fmt.Errorf("cannot decode file %s", path) + } + + dst, err := os.Create(newPath) + if err != nil { + log.Fatal(err) + return "", fmt.Errorf("cannot create file %s", newPath) + } + defer dst.Close() + + // HACK caseにベタがきしているが、formats定義を展開した方が良い + // case条件にsliceを展開した物を使えないのだろうか。。。 + switch strings.ToLower(to) { + case "jpg", "jpeg": + if err := jpeg.Encode(dst, img, nil); err != nil { + return "", err + } + case "png": + if err := png.Encode(dst, img); err != nil { + return "", err + } + default: + log.Fatal("reached to invaid condition") + } + + return newPath, nil +} diff --git a/kadai1/tomoyukikobayashi/src/main.go b/kadai1/tomoyukikobayashi/src/main.go new file mode 100644 index 0000000..8af8210 --- /dev/null +++ b/kadai1/tomoyukikobayashi/src/main.go @@ -0,0 +1,89 @@ +/* +Pacakge main is the entry point of this project. +This mainly provides interaction logics and parameters +used in CLI intrerfaces. +*/ +package main + +import ( + "flag" + "fmt" + "os" + + "file" + "imconv" +) + +// CLIのExitコード +const ( + ExitSuccess = 0 + ExitError = iota +) + +// CLIのオプションパラメタ +var ( + f = flag.String("f", "jpg", "input image file format") + t = flag.String("t", "png", "output image file format") +) + +func init() { + // 引数がおかしいときはUsageを表示する + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] dirname \n", os.Args[0]) + flag.PrintDefaults() + fmt.Fprintf(os.Stderr, "Supported formats are %v \n", imconv.SupportedExtensions()) + } +} + +// CLIツールのエントリーポイント +func main() { + flag.Parse() + dir := flag.Arg(0) + + // 引数のバリデーション + if len(dir) < 1 { + flag.Usage() + os.Exit(ExitError) + } + + if !imconv.Supported(*f) { + fmt.Fprintf(os.Stderr, "Supported formats are %v \n", imconv.SupportedExtensions()) + os.Exit(ExitError) + } + + if !imconv.Supported(*t) { + fmt.Fprintf(os.Stderr, "Supported formats are %v \n", imconv.SupportedExtensions()) + os.Exit(ExitError) + } + + if *f == *t { + fmt.Fprintf(os.Stderr, "input format and output format are the same \n") + os.Exit(ExitError) + } + + // 条件にマッチするファイルパスを検索 + paths, err := file.Find(dir, imconv.GetFormatThesaurus(*f)) + if err != nil { + fmt.Fprintf(os.Stderr, "%v", err) + os.Exit(ExitError) + } + + if len(paths) < 1 { + fmt.Println("no files matched") + os.Exit(ExitSuccess) + } + + // 画像を変換する + // TOOD 画像が大量になる場合はgoroutineで並列処理しても良さそう + for _, path := range paths { + fmt.Println("src:", path) + newPath, err := imconv.Convert(path, *f, *t) + if err != nil { + fmt.Fprintf(os.Stderr, "%v", err) + os.Exit(ExitError) + } + fmt.Println("dst:", newPath) + } + + os.Exit(ExitSuccess) +} From ab313866ab25829f1d7f4aa269459b07f10c9023 Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Thu, 8 Nov 2018 16:14:23 +0900 Subject: [PATCH 02/39] add todos --- kadai1/tomoyukikobayashi/README.md | 34 +++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/kadai1/tomoyukikobayashi/README.md b/kadai1/tomoyukikobayashi/README.md index a7cbe6f..563845c 100644 --- a/kadai1/tomoyukikobayashi/README.md +++ b/kadai1/tomoyukikobayashi/README.md @@ -29,4 +29,36 @@ Supported formats are [jpg jpeg png] オプションについて知りたい場合は、下記のようにしてヘルプを参照してください ``` ./imconv --help -``` \ No newline at end of file +``` +# 課題に対するアプローチ +## 次の仕様を満たすコマンドを作って下さい +- [x] ディレクトリを指定する + main.go + 特に工夫なくflagで指定 +- [x] 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト) + image_converter.go で以下の流れで実現 + 入力ファイルを開く + 入力ファイルをimageパッケージのDecode関数でデコード + 出力ファイルを開く + 出力ファイルに指定したフォーマットのimageサブパッケージのEncode関数でエンコード + deferで入力、出力ファイルをクローズ +- [x] ディレクトリ以下は再帰的に処理する + file_finder.go + ioutil.ReadDir で []os.FileInfo を取得して、FileInfoがdirectoryだったら + 再帰的に処理するようにした +- [x] 変換前と変換後の画像形式を指定できる(オプション) + image_converter.go + switch条件で、指定したフォーマットにマッチしたImageサブパッケージでEncodeするようにした +## 以下を満たすように開発してください +- [x] mainパッケージと分離する + /file 汎用的なファイル操作 + /imconv 画像処理特有のデータと操作 + の2パッケージを作成 +- [x] 自作パッケージと標準パッケージと準標準パッケージのみ使う +- [x] 準標準パッケージ:golang.org/x以下のパッケージ + 標準外パッケージは特に使わなかった +- [x] ユーザ定義型を作ってみる + file_finder.go + type structは使う場面がなかったので、[]string 型をラップして、レシーバを定義した +- [x] GoDocを生成してみる +  Packageとパブリックには全てGoDocコメントをつけて、godocコマンドで表示を確認した \ No newline at end of file From ef36126f754db262b3aa9a1998324910c8c9173e Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Sun, 18 Nov 2018 08:20:24 +0900 Subject: [PATCH 03/39] add kadai2 files --- kadai2/Makefile | 29 +++ kadai2/README.md | 106 ++++++++++ .../src/tomoyukikobayashi/file/file_finder.go | 77 ++++++++ .../file/file_finder_test.go | 136 +++++++++++++ .../src/tomoyukikobayashi/imconv/formats.go | 54 ++++++ .../tomoyukikobayashi/imconv/formats_test.go | 80 ++++++++ .../imconv/image_converter.go | 80 ++++++++ .../imconv/image_converter_test.go | 136 +++++++++++++ kadai2/src/tomoyukikobayashi/main.go | 110 +++++++++++ kadai2/src/tomoyukikobayashi/main_test.go | 182 ++++++++++++++++++ .../src/tomoyukikobayashi/testdata/file1.jpg | Bin 0 -> 18088 bytes .../src/tomoyukikobayashi/testdata/file2.jpeg | Bin 0 -> 18088 bytes .../src/tomoyukikobayashi/testdata/file3.png | Bin 0 -> 190334 bytes .../src/tomoyukikobayashi/testdata/file4.txt | 0 .../tomoyukikobayashi/testdata/invalid.jpg | 0 15 files changed, 990 insertions(+) create mode 100644 kadai2/Makefile create mode 100644 kadai2/README.md create mode 100644 kadai2/src/tomoyukikobayashi/file/file_finder.go create mode 100644 kadai2/src/tomoyukikobayashi/file/file_finder_test.go create mode 100644 kadai2/src/tomoyukikobayashi/imconv/formats.go create mode 100644 kadai2/src/tomoyukikobayashi/imconv/formats_test.go create mode 100644 kadai2/src/tomoyukikobayashi/imconv/image_converter.go create mode 100644 kadai2/src/tomoyukikobayashi/imconv/image_converter_test.go create mode 100644 kadai2/src/tomoyukikobayashi/main.go create mode 100644 kadai2/src/tomoyukikobayashi/main_test.go create mode 100755 kadai2/src/tomoyukikobayashi/testdata/file1.jpg create mode 100755 kadai2/src/tomoyukikobayashi/testdata/file2.jpeg create mode 100755 kadai2/src/tomoyukikobayashi/testdata/file3.png create mode 100755 kadai2/src/tomoyukikobayashi/testdata/file4.txt create mode 100755 kadai2/src/tomoyukikobayashi/testdata/invalid.jpg diff --git a/kadai2/Makefile b/kadai2/Makefile new file mode 100644 index 0000000..0e9ae14 --- /dev/null +++ b/kadai2/Makefile @@ -0,0 +1,29 @@ +.PHONY: deps +deps: + go get github.com/golang/lint/golint + +.PHONY: build +build: + go build -o imconv tomoyukikobayashi + +.PHONY: test +test: + go test -v -cover ./... + +.PHONY: cover +cover: + go test -coverprofile=main tomoyukikobayashi + go tool cover -html=main + go test -coverprofile=imconv tomoyukikobayashi/imconv + go tool cover -html=imconv + go test -coverprofile=file tomoyukikobayashi/file + go tool cover -html=file + +.PHONY: lint +lint: deps + go vet ./... + golint ./... + +.PHONY: fmt +fmt: deps + go fmt *.go diff --git a/kadai2/README.md b/kadai2/README.md new file mode 100644 index 0000000..28370cf --- /dev/null +++ b/kadai2/README.md @@ -0,0 +1,106 @@ +GoImageConverter +===== + +# Overview + +GoImageConverterはGolangで書かれた画像コンバーターです。 + +# SetUp + +下記のようにコマンドを叩くと、実行形式のimconvファイルが生成されます +``` +cd kadai2 +make build +``` + +# Usage + +実行時の引数は下記の通りです。 +オプションの後に、ディレクトリ名を引数として与えてください +``` +./imconv [OPTIONS] dirname + -f string + input image file format (default "jpg") + -t string + output image file format (default "png") +Supported formats are [jpg jpeg png] +``` + +# 課題に対するアプローチ +## 1回目の宿題のテストを作ってみて下さい +テーブル駆動テストを行う +基本的にほぼテーブル駆動でテストは作成 + +テストヘルパーを作ってみる +あまりヘルパーにしたいところが見当たらなかった +setup/teardown系は*testing.Tのヘルパー関数群を利用している + +テストのしやすさを考えてリファクタリングしてみる +https://mattn.kaoriya.net/software/lang/go/20161025113154.htm +https://deeeet.com/writing/2014/12/18/golang-cli-test/ +CLIのインターフェイス部がテストしづらくなっていたので、上記を参考に、リファクタリングを行った +主にやったことは +- Exit処理をテストから上書き可能にするために、エイリアスを与え、テストではExit処理をラップして、エラーコードを取れるようにした +- CLIの出力先に任意のio.Writerを与えられるように構造体を定義し、実行環境ではos.Std、テストではbytes.Bufferに出力するように変更している + +テストのカバレッジを取ってみる +make test > coverage がみれる +make cover > プロファイルで通っていないところがみれる +というコマンドを定義して確認した +概ね全体をカバーしたが、いくつか通しづらいところがあったので、こちらはプルリクでコメント補足する + +## io.Readerとio.Writerについて調べてみよう +標準パッケージでどのように使われているか +io.Readerとio.Writerがあることで +どういう利点があるのか具体例を挙げて考えてみる + +https://github.com/golang/go/tree/master/src +をio.Reader、io.Writerで検索してみると + +binary、csv、http、バッファ、XML、アーカイブファイル、標準入出力、etc +多様なIOフォーマットの読み書きで、基底となる共通のIFとして使われている + +例としてpngのreaderを取り出して見てみると、 + +image/png/reader.go + +// Decode reads a PNG image from r and returns it as an image.Image. +func Decode(r io.Reader) (image.Image, error) { +PNGを扱うライブラリを実装しているにも関わらず、入力をPNG特有のなにかとして扱っていない + +// 実際に処理しているところ d.r がio.Reader +n, err := io.ReadFull(d.r, d.tmp[:8]) +読み出しは、同じ規約(io.Reader)を持った標準ライブラリ関数に処理させている + +IOに関して固有のロジックを実装せずにすむ (読み出した後に、PNG特有のデータ構造として考える) +同じ規約を持ったライブラリに、まるっとIO処理を委譲できる(取り回しがしやすい) +そもそもここ、どういう風に引数作ったらいいんだろう?を悩む必要がない +-> 作る側として便利 + +image/png/reader_test.go + +// PNGのテストだが、stringから読み出させている +const ( + ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x02\x08\x00\x00\x00\x00\xbc\xea\xe9\xfb" + idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae" + iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82" +) +Decode(strings.NewReader(pngHeader + ihdr + idatWhite + idatZero + iend)) + +// PNGのテストだが、バイト列から読み出させている +_, err := Decode(bytes.NewReader([]byte{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x08, 0x06, 0x00, 0x00, 0x00, 0x30, 0x57, 0xb3, + 0xfd, 0x00, 0x00, 0x00, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x62, 0x20, 0x12, 0x8c, + 0x2a, 0xa4, 0xb3, 0x42, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x38, 0x00, 0x15, 0x2d, 0xef, + 0x5f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +})) + +Readerインターフェイスを実装していればいいので、IO元/先をPNGファイルにこだわらずにすむ (テストしやすい) +IOする必要があるライブラリを使おうとするたびに個別の規約を覚える必要がない +同じIO IFを提供しているライブラリに読み出しを任せられる(取り回しがしやすい) +-> 使う側として便利 + +何かを読んでデータをp []byteに書き込むという振る舞いが満たされていることを共通の規約にすることで、 +実装の詳細がどんな性質であるかを意識することなく、上記にあげたような諸々の便利さを手に入れることができている +func Read(p []byte) (n int, err error) diff --git a/kadai2/src/tomoyukikobayashi/file/file_finder.go b/kadai2/src/tomoyukikobayashi/file/file_finder.go new file mode 100644 index 0000000..16c4bd6 --- /dev/null +++ b/kadai2/src/tomoyukikobayashi/file/file_finder.go @@ -0,0 +1,77 @@ +/* +Package file provides methods and structs +that handle file and directory operations. +Almost all of them just wrap primitive methods +defined in golang native packages. +*/ +package file + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" +) + +// Find returns all filepaths in directory specified as dir. +// If you want to filter the paths by file extensions, use exts. +func Find(dir string, exts []string) ([]string, error) { + paths, err := findPaths(dir) + if err != nil { + return nil, err + } + + return paths.filter(exts), nil +} + +// find dirで指定したディレクトリ配下のファイルパス一覧を返却 +func findPaths(dir string) (paths, error) { + files, err := ioutil.ReadDir(dir) + if err != nil { + return nil, fmt.Errorf("could not open dir %s", dir) + } + + paths := paths{} + for _, file := range files { + path := filepath.Join(dir, file.Name()) + // FileInfoがディレクトリなら再帰処理 + if file.IsDir() { + dsc, err := findPaths(path) + if err != nil { + return nil, fmt.Errorf("could not open dir %s", path) + } + // ...を付けるとslice同士連結できるよう + paths = append(paths, dsc...) + continue + } + paths = append(paths, path) + } + + return paths, nil +} + +// paths ディレクトリパスの集合に対する操作を提供する +type paths []string + +// filter extsで指定した拡張子にマッチする結果を絞り込む +// filter(条件1).filter(条件2) のようにして順に絞り込んでもよい +func (p paths) filter(exts []string) paths { + paths := paths{} + for _, v := range p { + if matches(v, exts) { + paths = append(paths, v) + } + } + return paths +} + +// matches ignore caseでpathがextsで指定した拡張子にマッチするかを検証する +func matches(path string, exts []string) bool { + lpath := strings.ToLower(path) + for _, v := range exts { + if strings.HasSuffix(lpath, "."+strings.ToLower(v)) { + return true + } + } + return false +} diff --git a/kadai2/src/tomoyukikobayashi/file/file_finder_test.go b/kadai2/src/tomoyukikobayashi/file/file_finder_test.go new file mode 100644 index 0000000..e65f127 --- /dev/null +++ b/kadai2/src/tomoyukikobayashi/file/file_finder_test.go @@ -0,0 +1,136 @@ +package file + +import ( + "os" + "reflect" + "testing" +) + +// fixture +var ( + dataRoot = "../testdata" + jpg, jpeg, png, txt = "file1.jpg", "file2.jpeg", "file3.png", "file4.txt" + tmpRoot = dataRoot + "/root" + imgDir, txtDir = tmpRoot + "/dir1/img", tmpRoot + "/dir2/txt" +) + +// TODO 愚直にOS使ったが、finderでIF差し替えられるようにしておいて仮想osにやらせてもいいかも +// setup / teardown用のヘルパー関数 +func testSetup(t *testing.T) func() { + // testdataの存在確認 + if _, err := os.Stat(dataRoot); err != nil { + t.Fatal(err) + } + + // 元になるテストデータの存在確認 + for _, v := range []string{jpg, jpeg, png, txt} { + if _, err := os.Stat(dataRoot + "/" + v); err != nil { + t.Fatal(err) + } + } + + // テストデータを格納するフォルダの作成 + for _, v := range []string{imgDir, txtDir} { + if err := os.MkdirAll(v, 0777); err != nil { + t.Fatal(err) + } + } + + // ファイルの作成 + for _, v := range []string{imgDir + "/" + + jpg, imgDir + "/" + jpeg, imgDir + "/" + png, txtDir + "/" + txt} { + file, err := os.OpenFile(v, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := file.Close(); err != nil { + t.Error(err) + } + }() + } + + return func() { + if err := os.RemoveAll(tmpRoot); err != nil { + t.Error(err) + } + } +} + +func Test_Find(t *testing.T) { + // 指定した条件でフォルダとファイルを配置する + tearDown := testSetup(t) + + tests := []struct { + name string + dir string + exts []string + want []string + isErr bool + }{ + { + name: "findAll", + dir: tmpRoot, + exts: []string{"jpg", "jpeg", "png", "txt"}, + want: []string{imgDir + "/" + jpg, + imgDir + "/" + jpeg, imgDir + "/" + png, txtDir + "/" + txt}, + isErr: false, + }, + { + name: "notExistExt", + dir: tmpRoot, + exts: []string{"notFound"}, + want: []string{}, + isErr: false, + }, + { + name: "cannotOpenDir", + dir: "notExist", + exts: []string{"jpg"}, + want: nil, + isErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Find(tt.dir, tt.exts) + // TODO error型定義していないので有無の判断だけになっている + if tt.isErr { + if err == nil { + t.Fatal(err) + } + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } + + // 事前条件で作った構成を削除 + tearDown() +} + +// TODO ザ・テストのためのテストって感じで微妙 +func Test_findpaths_errcase(t *testing.T) { + tearDown := testSetup(t) + + // 書き込みだけ許して、読み込めないdirを作成 + newDir := dataRoot + "/unReadable" + if err := os.Mkdir(newDir, 0333); err != nil { + t.Fatal(err) + } + defer func() { + if err := os.RemoveAll(newDir); err != nil { + t.Error(err) + } + }() + + got, err := Find(dataRoot, []string{"jpg"}) + if err == nil { + t.Fatalf("expected to get err, but got=%#v", got) + } + + tearDown() +} diff --git a/kadai2/src/tomoyukikobayashi/imconv/formats.go b/kadai2/src/tomoyukikobayashi/imconv/formats.go new file mode 100644 index 0000000..b004f39 --- /dev/null +++ b/kadai2/src/tomoyukikobayashi/imconv/formats.go @@ -0,0 +1,54 @@ +package imconv + +import "strings" + +// HACK 本当はimmutableにしたいけどどうもできなそう +// ポインタだししょうがないか。。。厳密にやるなら直接触らせないのがいいかな +var ( + jpgExts = []string{"jpg", "jpeg"} + pngExts = []string{"png"} + supportExts = [][]string{ + jpgExts, + pngExts, + } + allSupportExts = append(jpgExts, pngExts...) +) + +// Supported returns if the package can handle speficied file format +func Supported(ext string) bool { + for _, spext := range allSupportExts { + if strings.ToLower(ext) == spext { + return true + } + } + return false +} + +// SupportedExtensions returns image formats the package can handle +func SupportedExtensions() []string { + clone := make([]string, len(allSupportExts)) + copy(clone, allSupportExts) + return clone +} + +// GetFormatThesaurus returns image formats that are the same with arg ext +func GetFormatThesaurus(ext string) []string { + for _, spexts := range supportExts { + for _, spext := range spexts { + if strings.ToLower(ext) == spext { + return spexts + } + } + } + return nil +} + +func isSameFormat(from string, to string) bool { + th := GetFormatThesaurus(from) + for _, v := range th { + if strings.ToLower(to) == v { + return true + } + } + return false +} diff --git a/kadai2/src/tomoyukikobayashi/imconv/formats_test.go b/kadai2/src/tomoyukikobayashi/imconv/formats_test.go new file mode 100644 index 0000000..81f7976 --- /dev/null +++ b/kadai2/src/tomoyukikobayashi/imconv/formats_test.go @@ -0,0 +1,80 @@ +package imconv + +import ( + "reflect" + "testing" +) + +func Test_Supported(t *testing.T) { + tests := []struct { + ext string + want bool + }{ + {"jpg", true}, + {"jpeg", true}, + {"JPG", true}, + {"png", true}, + {"invalid", false}, + {"", false}, + } + + for _, tt := range tests { + t.Run(tt.ext, func(t *testing.T) { + if got := Supported(tt.ext); got != tt.want { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } +} + +func Test_SupportedExtensions(t *testing.T) { + got := SupportedExtensions() + if len(got) != len(jpgExts)+len(pngExts) { + t.Fatalf("unexpected exts got = %#v", got) + } +} + +func Test_GetFormatThesaurus(t *testing.T) { + tests := []struct { + ext string + want []string + }{ + {"jpg", jpgExts}, + {"JPG", jpgExts}, + {"jpeg", jpgExts}, + {"png", pngExts}, + {"invalid", nil}, + {"", nil}, + } + + for _, tt := range tests { + t.Run(tt.ext, func(t *testing.T) { + if got := GetFormatThesaurus(tt.ext); !reflect.DeepEqual(got, tt.want) { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } +} + +func Test_isSameFormat(t *testing.T) { + tests := []struct { + from string + to string + want bool + }{ + {"jpg", "jpeg", true}, + {"JPG", "jpeg", true}, + {"jpeg", "jpeg", true}, + {"JPG", "png", false}, + {"png", "invalid", false}, + {"invalid", "invalid", false}, + } + + for _, tt := range tests { + t.Run(tt.from+"_"+tt.to, func(t *testing.T) { + if got := isSameFormat(tt.from, tt.to); got != tt.want { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } +} diff --git a/kadai2/src/tomoyukikobayashi/imconv/image_converter.go b/kadai2/src/tomoyukikobayashi/imconv/image_converter.go new file mode 100644 index 0000000..c0afe00 --- /dev/null +++ b/kadai2/src/tomoyukikobayashi/imconv/image_converter.go @@ -0,0 +1,80 @@ +/* +Package imconv provides image convert mathods and +some validation logics, like if the package can handle +input format or not. +*/ +package imconv + +import ( + "fmt" + "image" + "image/jpeg" + "image/png" + "log" + "os" + "strings" +) + +// Convert convert image from from fromat to to fromat +func Convert(path string, from string, to string) (string, error) { + if !Supported(from) { + return "", fmt.Errorf("can not handle %s", from) + } + + if !Supported(to) { + return "", fmt.Errorf("can not handle %s", to) + } + + if isSameFormat(from, to) { + return "", fmt.Errorf("%s and %s are the same format", from, to) + } + + parts := strings.Split(path, ".") + newPath := strings.Join(parts[:len(parts)-1], ".") + "." + to + + src, err := os.Open(path) + if err != nil { + log.Print(err) + return "", fmt.Errorf("cannot open file %s", path) + } + defer func() { + if err := src.Close(); err != nil { + log.Fatal(err) + } + }() + + img, _, err := image.Decode(src) + if err != nil { + log.Print(err) + return "", fmt.Errorf("cannot decode file %s", path) + } + + dst, err := os.Create(newPath) + if err != nil { + log.Print(err) + return "", fmt.Errorf("cannot create file %s", newPath) + } + defer func() { + if err := dst.Close(); err != nil { + log.Print(err) + } + }() + + // HACK caseにベタがきしているが、formats定義を展開した方が良い + // case条件にsliceを展開した物を使えないのだろうか。。。 + switch strings.ToLower(to) { + case "jpg", "jpeg": + if err := jpeg.Encode(dst, img, nil); err != nil { + return "", err + } + case "png": + if err := png.Encode(dst, img); err != nil { + return "", err + } + default: + log.Print(err) + return "", fmt.Errorf("reached to invaid condition") + } + + return newPath, nil +} diff --git a/kadai2/src/tomoyukikobayashi/imconv/image_converter_test.go b/kadai2/src/tomoyukikobayashi/imconv/image_converter_test.go new file mode 100644 index 0000000..cf2a7d1 --- /dev/null +++ b/kadai2/src/tomoyukikobayashi/imconv/image_converter_test.go @@ -0,0 +1,136 @@ +package imconv + +import ( + "os" + "strings" + "testing" +) + +// fixture +var ( + dataRoot = "../testdata" + jpgF, jpegF, pngF, txtF = "file1.jpg", "file2.jpeg", "file3.png", "file4.txt" + invalidJpgF = "invalid.jpg" +) + +// 前提ファイルがあるかチェック +func checkPrecondition(t *testing.T) { + // testdataの存在確認 + if _, err := os.Stat(dataRoot); err != nil { + t.Fatal(err) + } + + // 元になるテストデータの存在確認 + for _, v := range []string{jpgF, jpegF, pngF, txtF, invalidJpgF} { + if _, err := os.Stat(dataRoot + "/" + v); err != nil { + t.Fatal(err) + } + } + + return +} + +func Test_Find(t *testing.T) { + // 指定した条件でフォルダとファイルを配置する + checkPrecondition(t) + + tests := []struct { + name string + path string + from string + to string + want string + isErr bool + }{ + // TOOD 毎回全埋めすると長くなるので、composition使ってデフォルト値+上書きにしてしまうと短くできるかも? + { + name: "jpgToPng", + path: path(jpgF), + from: "jpg", + to: "png", + want: newPath(jpgF, "png"), + isErr: false, + }, + { + name: "pngToJpg", + path: path(pngF), + from: "png", + to: "jpg", + want: newPath(pngF, "jpg"), + isErr: false, + }, + { + name: "unSupportedFrom", + path: path(jpgF), + from: "INVALID", + to: "png", + want: "", + isErr: true, + }, + { + name: "unSupportedTo", + path: path(jpgF), + from: "jpg", + to: "INVALID", + want: "", + isErr: true, + }, + { + name: "fromAndToAreEquals", + path: path(jpgF), + from: "jpg", + to: "jpg", + want: "", + isErr: true, + }, + { + name: "failedToOpenFile", + path: "notExistFile.jpg", + from: "jpg", + to: "png", + want: "", + isErr: true, + }, + { + name: "failedToDecodeFile", + path: path(invalidJpgF), + from: "jpg", + to: "png", + want: "", + isErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Convert(tt.path, tt.from, tt.to) + // TODO error型定義していないので有無の判断だけになっている + // TOOD ここちょっと長いのでGiven When Then (TearDown)の見通しが悪くなっている + if tt.isErr { + if err == nil { + t.Fatal(err) + } + return + } + // 終わったらゴミ掃除 + defer func() { + if err := os.Remove(got); err != nil { + t.Error(err) + } + }() + if got != tt.want { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } +} + +func path(file string) string { + return dataRoot + "/" + file +} + +func newPath(file string, to string) string { + parts := strings.Split(file, ".") + newPath := dataRoot + "/" + strings.Join(parts[:len(parts)-1], ".") + "." + to + return newPath +} diff --git a/kadai2/src/tomoyukikobayashi/main.go b/kadai2/src/tomoyukikobayashi/main.go new file mode 100644 index 0000000..964b621 --- /dev/null +++ b/kadai2/src/tomoyukikobayashi/main.go @@ -0,0 +1,110 @@ +/* +Pacakge main is the entry point of this project. +This mainly provides interaction logics and parameters +used in CLI intrerfaces. +*/ +package main + +import ( + "flag" + "fmt" + "io" + "os" + + "tomoyukikobayashi/file" + "tomoyukikobayashi/imconv" +) + +// CLIのExitコード +const ( + ExitSuccess = 0 + ExitInvalidArgs = iota + ExitError = iota +) + +// Exitしてもテスト落ちない操作するようにエイリアスにしている +var exit = os.Exit + +// CLI テストしやすいようにCLIの出力先を差し替えられるようにしている +type CLI struct { + outStream io.Writer + errStream io.Writer +} + +// CLIツールのエントリーポイント +func main() { + cli := &CLI{outStream: os.Stdout, errStream: os.Stderr} + exit(cli.Run(os.Args)) +} + +// Run テスト用に実行ロジックを切り出した内容 +func (c *CLI) Run(args []string) int { + flags := flag.NewFlagSet("imconv", flag.ContinueOnError) + flags.SetOutput(c.errStream) + + // TODO flagsに突っ込まないといけない一方、オプション書式にも定義したいので、上書きしてしまっている + tmpf, tmpt := "", "" + f, t := &tmpf, &tmpt + flags.StringVar(t, "f", "jpg", "input image file format") + flags.StringVar(f, "t", "png", "output image file format") + + flags.Usage = func() { + fmt.Fprintf(c.errStream, "Usage: %s [OPTIONS] dirname \n", os.Args[0]) + flags.PrintDefaults() + fmt.Fprintf(c.errStream, "Supported formats are %v \n", imconv.SupportedExtensions()) + } + + if err := flags.Parse(args[1:]); err != nil { + return ExitInvalidArgs + } + + dir := flags.Arg(0) + + // 引数のバリデーション + if len(dir) < 1 { + // stream設定しているおかげで、こいつも指定したstreamに書いているよう + flags.Usage() + return ExitInvalidArgs + } + + if !imconv.Supported(*f) { + fmt.Fprintf(c.errStream, "supported formats are %v \n", imconv.SupportedExtensions()) + return ExitInvalidArgs + } + + if !imconv.Supported(*t) { + fmt.Fprintf(c.errStream, "supported formats are %v \n", imconv.SupportedExtensions()) + return ExitInvalidArgs + } + + if *f == *t { + fmt.Fprintf(c.errStream, "input format and output format are the same \n") + return ExitInvalidArgs + } + + // 条件にマッチするファイルパスを検索 + paths, err := file.Find(dir, imconv.GetFormatThesaurus(*f)) + if err != nil { + fmt.Fprintf(c.errStream, "%v", err) + return ExitError + } + + if len(paths) < 1 { + fmt.Fprintf(c.outStream, "no files matched") + return ExitSuccess + } + + // 画像を変換する + // TOOD 画像が大量になる場合はgoroutineで並列処理しても良さそう + for _, path := range paths { + fmt.Fprintf(c.outStream, "src:"+path+"\n") + newPath, err := imconv.Convert(path, *f, *t) + if err != nil { + fmt.Fprintf(c.errStream, "%v", err) + return ExitError + } + fmt.Fprintf(c.outStream, "dst:"+newPath+"\n") + } + + return ExitSuccess +} diff --git a/kadai2/src/tomoyukikobayashi/main_test.go b/kadai2/src/tomoyukikobayashi/main_test.go new file mode 100644 index 0000000..0211754 --- /dev/null +++ b/kadai2/src/tomoyukikobayashi/main_test.go @@ -0,0 +1,182 @@ +package main + +import ( + "bytes" + "errors" + "fmt" + "os" + "strings" + "testing" +) + +// fixture +var ( + dataRoot = "testdata" + emptyDir = "testdata/empty" +) + +type ExitCode int + +func (e ExitCode) Error() string { + return fmt.Sprintf("exit code: %d", int(e)) +} + +func init() { + // mainロジックの exit を上書き + exit = func(n int) { + panic(ExitCode(n)) + } +} + +// 前提ファイルがあるかチェック +func checkPrecondition(t *testing.T) func() { + // testdataの存在確認 + if _, err := os.Stat(dataRoot); err != nil { + t.Fatal(err) + } + + // 空のフォルダの作成 + if err := os.Mkdir(emptyDir, 0777); err != nil { + t.Fatal(err) + } + + return func() { + if err := os.RemoveAll(emptyDir); err != nil { + t.Error(err) + } + } +} + +// exitに差し込まれたpanic(ExitCode)の内容が意図した通りか検証 +func testExit(code int, f func()) (err error) { + defer func() { + // exitでわざとpanicさせてrecoverからエラーを取る + e := recover() + switch t := e.(type) { + case ExitCode: + if int(t) == code { + err = nil + } else { + err = fmt.Errorf("expected exit %v got %v", code, e) + } + default: + err = fmt.Errorf("expected exit %v got %v", code, e) + } + }() + + f() + + return errors.New("not exit") +} +func Test_main(t *testing.T) { + tests := []struct { + name string + args []string + want int + }{ + { + name: "invalidArgs", + args: []string{"main", "-i=invalid", "dir"}, + want: ExitInvalidArgs, + }, + { + name: "validArgs", + args: []string{"main", "-f=jpg", "dir"}, + want: ExitError, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + os.Args = tt.args + err := testExit(tt.want, func() { + main() + }) + if err != nil { + t.Fatal(err) + } + }) + } +} + +func Test_Run(t *testing.T) { + tearDown := checkPrecondition(t) + + tests := []struct { + name string + args []string + wantCode int + wantTxt string + }{ + { + name: "invalidArgs", + args: []string{"main", "-i=invalid", "dir"}, + wantCode: ExitInvalidArgs, + wantTxt: "Usage", + }, + { + name: "dirNotSpecified", + args: []string{"main"}, + wantCode: ExitInvalidArgs, + wantTxt: "Usage", + }, + { + name: "unsupported-f", + args: []string{"main", "-f=unsp", "dir"}, + wantCode: ExitInvalidArgs, + wantTxt: "supported", + }, + { + name: "unsupported-t", + args: []string{"main", "-t=unsp", "dir"}, + wantCode: ExitInvalidArgs, + wantTxt: "supported", + }, + { + name: "same-f-t", + args: []string{"main", "-f=jpg", "-t=jpg", "dir"}, + wantCode: ExitInvalidArgs, + wantTxt: "same", + }, + { + name: "dirNotFount", + args: []string{"main", "notfound"}, + wantCode: ExitError, + wantTxt: "open", + }, + { + name: "fileNotFount", + args: []string{"main", "testdata/empty"}, + wantCode: ExitSuccess, + wantTxt: "no file", + }, + { + name: "valid", + args: []string{"main", "testdata"}, + wantCode: ExitSuccess, + wantTxt: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // CLIの書き込みストリームを渡す + outStream, errStream := new(bytes.Buffer), new(bytes.Buffer) + cli := &CLI{outStream: outStream, errStream: errStream} + + got := cli.Run(tt.args) + if got != tt.wantCode { + t.Errorf("Exitcode got=%d, want %d", got, tt.wantCode) + } + + if len(tt.wantTxt) > 1 { + if !strings.Contains(errStream.String(), tt.wantTxt) && + !strings.Contains(outStream.String(), tt.wantTxt) { + t.Errorf("Text got=%q, want %q", errStream.String(), tt.wantTxt) + } + } + }) + } + + tearDown() +} diff --git a/kadai2/src/tomoyukikobayashi/testdata/file1.jpg b/kadai2/src/tomoyukikobayashi/testdata/file1.jpg new file mode 100755 index 0000000000000000000000000000000000000000..7bb271534a4eb9f734260f6547db86a0202921e4 GIT binary patch literal 18088 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 H>+$~sL@-Tp literal 0 HcmV?d00001 diff --git a/kadai2/src/tomoyukikobayashi/testdata/file2.jpeg b/kadai2/src/tomoyukikobayashi/testdata/file2.jpeg new file mode 100755 index 0000000000000000000000000000000000000000..7bb271534a4eb9f734260f6547db86a0202921e4 GIT binary patch literal 18088 zcmdUX2UJt*wr=PGA|eD3fuJZzRZ8d(Md}wgJJNdzz4zXG z3*klgIp^(t#~F8=d+)h#jJL-4|36u2YpylRH|P9TuE(w?0UrT3@bL-o@oo?h5D*gH zAi7CGa+8?&=G{Bwq!e^CAbL6)T3UvOoXiXl*coYQAM>-YKjq@(QYxP5O>h=-*V{Ul`MQQiZ-br>L`e8@ZhX#bZ; zza{$D1p4sblIR}-{X?GX3BXMpEX)PNAp<}F=V;jDWbFUnM%1^$Npt?ws zOmEJ8<(W?KkyAN+8PsBt{XgeIZ}$aP8D$^4s-aCvjLysp+r^A5L6=C-8i{s>(h-ky z4x%K46SkXnrjw&rad3zHwi_4u8%fZj;>NKj@g{F|BJ^xPFF)y~b4f2#vHjK-1U^iNB3O7# zigMef&v}qED46YJQbn1k0ySGPXOmqOAGZ3*PvhKe2qg<#JY3gmHk=#c9a;ID^*usK zf`RC9>ihqVL4i~)l6ZKKuf!r+B81F*&D%M*jO^@_*qeD8*RS&wHa>25`$&MlUJEaC>7M~*{&k8>K#lV zg7vW)4gd>TmEvc+U`o3`U_c}SZ<_bdviGm4^dDE;L-fw}`;-o8dGQVy{;W7G)Tuk2 zfIMn(amNn#c9tSm5WOX{$SRkUU_O7v*-TnIHkkryE($_>TfTe{(g@cNw=WpFGq&nz z>!5(gDeTJl`s5Mlq1Q0N-J}xzB9*!-Y{TtV+Pt@sT$k>c>}_b*%X`s>bP$@h7H@Vn zo$P#3l|~xA{JWVY5)s#cF3mbgRGUE?xJv|dP@GG>d=fSnp^J)<5VWIx14Z7 zP)q}L0YX;9brBbcIg(7>dBK3D()H0V?GkbfXbx#t`-rAgONFsKq_V#q6_^YeC44g$*IsD+xt`_T(CXm+kzU5!u+ApC$+l|FNt=X6#dJi zJ&lJSUh+yMaXi(%Qha_f3Qaw;l$rQ4nZvcUuw(RNAZjLh(#W~HnfOJOV_d( zS-Ds36iX`K6t+Y9+9$F_gODoUNqF7pVM$b0?~xC9`Yd#Qdib+vFnet?V>sD0K(k(P z7YZq`>(0>3!@eQFa8gJ(7(tir@igadUQ19ajFsrWYt~c+nkB^*^_6m-nu%KR^GcF3 z1td64!{5ttk;G_;$zueCbhCoX?XA4j@e{w4ve}6 ziB%jWERF7cfwMf$#2%250B}%5y#Ci7z-1p}H4HWlshUK7SPt^126wxK|5T@0;BCjH z^3HgET0cW~4M=dhpDW=t;+UD9-}6SPWtwMo+u&v00A~=@_Xy^lie+67Kdr{Pz@ei& zQT?9n!g(zto3?DRA^rmu7dWk8U z1ce665QUNi;J-3}gJ&9z8e$Ad-ERl9?l*OX=!|<`U8>fR?L|~wHB?mGjN(d)4ZweK zHE<>GNm5WksosH0mT%86k(&k}+H9Jyyjr(%cxx|`xaE2`YyD(GFI7jowW!taJtX!R zEG!C!$H;pW!K6Z2_*yvNJo2g8VM8ir$plLBsqg6|_+S8MiiyWq|J7Z;zUV!ZMn=@* zod;%1YVY2B$mhOsl>Iy_Zy}>~nMLzpmfDT(XqWpMATQ}@Q2X#jIH=;-U>Q{*=4c>u z`v>O@#FPE34-)0EIqPBQ&0RU7&}uaD(j#PeW#Q?_X|hL!l?Hz`Pv7HI*u3zEf6)=) z?dm=Gh4UG_9ct({QPPAGI))k`#`4`Cr^O{qrE^Dt(f3gyiT>PN@r<#=G zYmGcF$lh2k zrjN|seNl!dF7Ijg0ro@WZcoQFO#W`>O3-j@2*wMq1oh(-OPW`v@dCC6etTJAUXwUD z&$Cz@wbLwgZqzEX~<3sJ7nHxDAfn%BK#I!ASUwVr}HdV`Z% zs(tjc(c-;8z#LKgUM~zz9}KJ`i1%^Vbafm>L5K!j5T+zj--LGDDyn9X+E$Ew^8CE9 zY2Xq2FA_|c(8WO!i#fw3$|gbm7@vwcy~O{UyWw_LG$xo|)mES4(l_2GZK{)R7~YOP zwGr90>bv{)FRg{g0@bcij#->h!IZc}SJukRY&zf`*uTYKe0&w;rQv zVyD##-`q98-4oPVTs;aKROmhpm)FDSt@J<3DDaj>t@m~%E^RU=g7RXlD_)&`y#~BT zzpZE=$Ou!SC@unE4Z%+pN!P!mt(Bc5lWbIe4cT&MJ}k_CB<4PSBv|C^e&4v7H{m!8Jg;`f&|^YOAkgq>262np)_Jq$Mm6`WgW@zRP$o`fg(EHu?SwFNAo!L5V(VdyLlO+lN~ zRMq(>QtuSJ^H;kWlA)z&=P~-xlE!mgyR;{Kl}n5wadQaN1zcKWap+2L7L5n$n;nSx zds8UsN|`B1PyqhJz#2Itkh}D$d042;7`xWZ`G2=`$AM zh-IE}mBhu+e|*dl+6GKwS;*L%$%LDJDcQ`CzVYaB^AVP>F8W>`&33l%y?&V<-3uHB znpkJJ4MbxZ9E>`p85KhQXLh zjM=|k;=gy!dEUZ<@p2rk!>iYTJ0(d`vns*vDoY)q3c1i2l!U8NIT*j#5MCJOXf}4W zahq^!`Z?+rTN0UKA_<+QMSS?HJy}+%;IyzbJGG&&4JQxM5I0W6|6^<}TrX41-cUdJ z9N)#D8`4yX!c#8nnx77x0C#Z47+WvSUGUgJ+t;E!opg~7Ti*`B;-uQJ5O@2K!!x~THof?1xLi8n!W>OeN z1`k++n{|BiR|+9v8 zg&a0t#8k$++huj#WLRQAr=NoRej58BOSi307GX$#I*F;?y89iP{bjgV8mjwh3%Xe9 zSO{6x_QQ8OZNHD*;`1+2rvI-DE@nME&iB@^&eM2%j%<<%ehd6}332~P6ilCf)`oc# z|3KAO(%->x@Q>3`_5QCX>Gt0Osi+RTB*0-I$4{ksm|LPH@x`Jg>36qp4tasA*v2Ri zbn+;_e%wB1n|E;OMu^eZG;eBX;FV?fCpXPNhat1my~EYa7z|MG{r+bg)yVY{EI!~B zqTze7gJ9p*pPk>dEjsh)q%{mw{eH^Gd<-7{5WHbf)DaSyGg;}QzLL@- z^rA-!eiAlc^hd^Z8{^_t@6QV*JC7rWe1I9;815r0cz?mGYC(#o;456lB#n~dO_D2Z)jzYEt zqwT#;(1p9=(r^Z?gp?A~5f36DGp8&nEB5|Eybe&GxjjpGPNUSDlyV{gn;WXbIolD z6f}9IM1*Ya+_^v)isp5C>zfiBJ*Z+|2^z&M0vgwZ ziVIjMO)!h0arPEDDLW!AJ}>oc3ahE%DWX;NuyU4sZh6Mfu6}Sqf38aEI+RSmk z4`tLSk`9;1D~d;O99OC`6SE#*ynO$m*8HLVxyaM$hCSWZZ_SH0L%44sSoU2%zUz;l z8S@WUFlrZxk=gRDC7tuWx+%sbf6yoCJ{CltS(%b6STg_*HsD2#CfgCiF=4^n>h=AW zoVPMIOqzzl%aOf6GMR^$Sn9mq4|etIDa&92D>C=?%sx#aYin1u*k$4} zV9EW;vY>~_Ld_#@n|qo_aVxDIJd1Bp^?-NtOP=vkjp4FX-V@uA%1!9emzZP%e5o6^ zc!n10dr4WpRiQ{a~m)V$?q!G*@I@M4ktS8RRd6>*Q(e=-% z3?=&I+To;8jgvWl(1KrXrs-7sS{xfDpE`XF;$FQB*7Kr#Wf%W z_DF>0izH-UZS5Lxd_{l`(rzN#EA{~05&Qh2MHqaCo@E@7B=#{J+~}oZ<)#MN+=hf} z*u&@0tVw4XjMo4}30huz0R4cw0LAC-UHy*eo6*4#W!5r~y=@ucZskVmSa7Dn2)b>M z>Bct>@gvXNhL`Dfg3#)$w~>C5ie`8%IqRzrksTas@0)yiA^nt*F-#7=D8jrjDeBX;O{si`!k7KY z>7v-;#i(H+-HTiPKP*~vrhLqQw9z#`!EHy}%TZUqi%UO*2@Y5b`b-0$rmCr^nmE_1 zrJJ5P+sAZ}E9dxvF#W83Qdyb+|Cx0vo^cRZ(L1Rb!U>%NFNMj3>90_hm-tsFQgVhC zC&p3AWVTR@Mk{GgrnqMq2BuYl@73YCU@D_dzM|8uMXbwjuDS)$`yeh97S296!=tYE zmZTGlP`HvX;?|lvnjVRqVCU^Ql;;%0e2Db9c4cOGxD0&n5i_%kH}2ci{D76O-<7`E zu@K|oCVr>{QMzUJJs+IdIe1Yl{PwfhQSch1^_#{$7a>7sIkyljcJJIrBX5`^?ATWI z3>H9v&N6XZhgbenS7UP^KGCtmq)aw$Qi`*v2}Le&T%%*W%zgWFJ_)Pr_Lh;)$FKOg z6u5&(OBFzLc`YdNeTEHHIe0O+t1*K;pvW=OByYNpsW~+O|e$MET2?VsKDI zv=o@o+6Z-%{O%BBxlWP=vFWo}`8a(D;|ynE%S zUrV-7)3?Ab3~ql!M-PD9ZbM19pRLU#^vOMm%+*}PlyweBH8C^b?_)!RkScZ|Fb?hB zx_x1EF`6suhTu7KsvKRh-I**$e=J>BhMznoJ~a)Cfy~?{?_n~}Dgdt0+hIVCHN~%* z!Cz-A+AKdMj6oiNe_JIf#wwj^Sw-)mC2K+G`jl%xKgM~Y*w<3_8P}XdREm{S782m+ zhie2Y6%be!nnmCm@Y_zE|FF~a#s6TZ&WLFs@2pF5*=1@*+|FoC9eD$kY|xA28W2{u ziLNoY2D}KV4OZ-AYeiFD1KMYuF?RX{<1E_rn@O>2bPe{kw&x)q1Ac)$>9%EXw^KZS zGn7mw#q8F}ZrQaN^1qIrGu!5+kC zU)&A16S+|K;qB6C#0@f+>nL(%1mp9{53^QDV=XKad`4tqQA zuYiHSqF7Ih%JR_B_fo+UVN9tDlc(4x`0|?D+djPWexN;rntm>b=6rt9Q@CT>V7G}; za`v_T72BGFy*}YW2_>3q__Yzf0}5Jt2-V(g=tP>$wYBcQO5;s>qJ8@0##k_aW0exej?Z{ks@5WJeh3*z~FKg;YZUZL&whK%*|_gkE(|u0+zW*BVxwZpi{*cS3 zh?g;=2`C|fVVi&}F3`nogm}zosW1ADh%R(r3k%aJVG0M&*!`s#_uzNFG4*)r@$dWt z#`ElEbcR$K=q$G+^a34#u>gv7MIe0;6cZ{Tn-`#~n;N3fPYD!a54~>&z+5bNF=l5V z;(Y^!5#$vnma&1Zyt580b{ut?TQFgfA|HBulGKr>4QZBCtM~8YEL&qW5AAxbLysrN zb#6IpKsW~pT=$_&I?F4p-)y-C90_cW{hZSXMsYy)HT0mP80f!d{@dJM{lnb8CXz6j zlgh3LDKebp^Sed0p=%*0E`|EzZom;Q^-!Dotd3rqDa2A6$}*x{gy`hE<*C`eeNwY= z5fZ-mAeuMGt#I*UPqhMxM^d9*Q=_9PfVi!#Vj}6v<6txGC*sYSt%ZKIXNhyVXVq$7 zX+ukX;3VxN)Fc1!ze(5+ka1|{D%UG-iC=Kzf{PS*D4)MDsZTuch@Z{hh$QyDT^(V&m>3``uCLwJ8o&M_f z<7LY`%wif|ON2Ji^*QJohn@&iB{Z-N{qQU4g*)hiScB=wnX<4QwE6Rjs*;2obH$(g zFD@X`do)XnBU2cKkkkJHcF%f0YZ}OPwH5Bahn`qD! zZBk#>qW%I=8%-O{CkX%`4L~Y;xF3_HIPJfh;@6m)cy#hKWslT9`PmO086K#I%oqse zA*YJ}nx0<9l0)o}6){=_BwiC^o*#5mEW zH?Z_}58bJ4)$vF~h1d3+<{vit0~8haXQKsXKtILypM@RKV1R4((7r;C*ZvXXq3)AE z8)Ze0T|I>&O1TK*z$Tzf1!mU(vb*Z&dm?p{JFqAWe&uWPRqpiBzQEf{#5oIzp0~$b z^D<0eOv1PY*%Jmg;9?SSC`zr|;CSg8a6zN-9j5_7O7#oBDq`^KZ}nLbQea;24?3M- zr-@*x+=__?I4+lQL$L|w;fKU7a~l1N?*cJFE6kthdppAkc%|B-g2XG ztDXSGQ<)8jL4k(=`%9x8>BFKJce&Zmt$r=wN$kvUg`s3e+6nIofvx`Ukj}y}Hx2#a zcf|Nfyk^kl{0*aLq7EMDy9i}9!^laI3S^v%bjOdutPy7&Z?Q4*hrLt+mub}?LvG7i z>w>r6e|$hC5J7HSri{thS zHEJKavv2NvGPt)n?dxPBLQ$?vwUMy&;qy=$P3@e?h{Yut&PUR3B;DT`X2ZBO69K?o zdRip(dyTMZR=j&y<~zpf@Te=9{9O_^Ok^*7!W_P>G1l<4kP>n#g{VI$3{J8~JfR9y z(@QJbc=L4UTTS`Rzaf_QtFS%LZA{oGwY|8&1lu~Ug&N{!W;E;eA5Lo0_iVf+63=@F??(~ueU?Mn=xZ+{#^#VFkr#t(gCa%a+38k9-Y>Z3ke(zq&L9$65Kz~Y zR;vcLQypDXKU6rr)LFI zZNeA#U)o3R8?GCmx#H!D0My%o^Nw){2_MZ*x=DKal!Qbu)Ay?|$F0jrE?=4`!5=Xm*wCk`y%H_RR;% z)JU~RPvb6#kO}{n4k;E(UZ!|LFHK z+g2B$?)wEy&B-Qm=C|me&7QD*R-~PeRJm*}{7~3x0G>gah8xdgC3b8mfA{XKto?lX zFT^|u$qfEG^v9Z=etC%1*K*k}sOQT;&!15)$2^F3$qln$W3Lt|qd#tUL$sb~1FOMJ zpE;$w)w6v}L_Z(VAFIr(7q}LzR~1|x?!x7Io3JEX8hyuz3NViUR3);Q zsNJY&_+{u#Lv~lfE{3BwQ{I9XBkTk3)Zd*{_5qs$ld~$P1M=sKw)xP)$c#X4OZDf_ z^?X>sffY?&j+gkrb{dnsf?(gfva5O9X8UvaK2z~(hM2IJhX!_|g{z2ufW!hRQ12|t zSw55a@g!H{Eg7+V&!lCw?Qnpaj;EIq4Egyf8!|;MOpD&xqtIr{(XKRSE|8=zgm*tE z-*`#Hsgc&FB0p9E&lN<>8||4ltuN7RQTOuY>BA3gdkv`dx657fp@j4nfk#sVaB=ta z9yjJHVKBxwQmjtSUuya;@BBnzm!H9QHRojztYnUVkT^9pD{s8(7q_TVa z&=%9mkLWveaKeQ+%&1fdZx-Wau_ZTOA`dekg4PWuC3{H^m8>CWm4jx*YY2Hht9nDf z?61z0`Ea(axP|;JA0Xa}rDLl~pm$^R&#;jx9^=JiO4=NaK^7b~ZD*Xf3nBDUo7!EA z!B4XpwFljg69|qGc%Bf8FI%R_@J5OvsVY5`_`saiA}s&yVZS$l^R`{Gz*)nLUM8}M;};=&!_)9e{J{q2>7V9jH^YC zq)=FGEp>9Ddi{0LPxaCkf6>%}$1PlvEW;y_A(A^RE6GtJF!)!PwJv#4n7wuUzEaNMa=naS*Cq^YLQ+xcT%VwVdFGd@Ya$Pbmpgs^(acXy^T7-#|m2W6e=<@ zWdID+l}}5U$+7>5A{u3Wlq{NyW4x`a+*sK5&R(%{qQ1pW<Kb6N7}Z`3RCbtrF*W%mSt~=%AC+yt`+br1V|z|oYo-2 zldZQwyGA)E1;s_pg>Nt=z9xRACGFq+uTiowKJ`{XM54{=rFyZq!!57L&mf38Q*B!_ z&HKR;&&k@cr;Xk9yGEg-z&RH`UwwaUp)xz8j8w^dJyva)Sj5FiR2%H{7Z>U+onmN# zgvv{92_{}WeMY7lKfR=Fp`dT8!qYL{gb0;k8?mnRDY^LD0U(uzE6aA<5F602s% zi^xbroKo$EbOE49V4;g-Hf5pqn1TN7vM5(_j7f;3a-edes369}R;#@tl>^rRCj46T zy_Io)K-f$rmq3v6tSs^mt2G`x>ox14#bPp;tP059U+965!L3sFXNepandEDb}+3_>>|Fq_Dn-< z1#XI8z+8E5Wjm_Y1212aT~Ya6ETO?`b$t5*10~UBvsB?L0#p^pwoT^?H9iAE8a_k` z%iVU1tXd9myIXkdZ7|JNJU*iqPrm;B-5ez9uf|@7DWXB=15m-ykiR7mh+7|OXifLE zc7B)Dz9zCf7G;ZA7YSWRo?c)BT^wWjd=r(_3%E{&^$Ve>Aa#S|O-$PZ?9hJ%+%X(p zPitwZR()q}Luz3<9+08mkcM3?3KzIaWBFpguAI-{lC#HR_FCN0i1I1xGeBz4@K{s} zri}Wl5b7w;j)yyRr;b(UJF;@xOJFd$wzjUK^3x;V2V_=Z&vA?T4h%-hk8S;FoY&2r zs-sFA`=VxrFfCSdAG-0Y=*ApjyHRD{`haQB#Vsh!hTo6)4-#%_`#frKexei9WPX(Ce|xZo4a-T)d&}>R?$e))15$6awD24JS+uV<8wX_|SpMV^icmp&=fOlqzl20iNR@vGPP4~UpXY@CLSZOzYHQ*~KPb*^ zNjrBX@+9}uR*q&Le30&*Jg`wkGCENb^Hb0b{o>5&ic95H1-R$z?O{jl)1q@@T5l=E zO+D4knYs@Vg`wCbR-IaDy!`la@HN3dI{%oSerDxD(4axaO|vY!B^-TB-@UDy0SI(OvHQoLq+6<7^2y z{`MLYTj;8E7M7-ieJj<}c1I`23M~rJK2+5iJJS6&wVPQDBkOv1LA2~8w{R&+Io9FK zWXyP~zUE^J>GvFs->4^lvQsXVG0E{GQDj`q6-9vISU>c-?D9`s(SVx@nz}qpYOP|K zUuMqfQz4@+AifC6aYtC#db^FA%)j;?(r-|Ub7}zU&)hVE^tnDUu?A*J@>WEjDWW;k6=6bJ41r+U z%T(yTbJy1D*!s!W9iHFW1*{8Q3q3I@)FyApL$hKsw$IM@EaTi(o>PeFxF=&P$ZRPV zn6@BmyJ@vE!$ov;!{f|~e^X5g2Lmz8j9;X{zp^T@H22jWO>L1$YJ~uf!N#FLg}HY$ zusE6*jx$@@lk$TFR+P^#1EWMj;%}-ghtV2qf-<~LjTd@Mr5j(^uQWoIBNpvYq)KNr zYgTnE4Nsp~F@3Hsxk>FKMSW$tn)^E5z;QN9|Bha$anD_ZQ0ZBCY9~JU1sQs)==+s@ z=t#-perwcbq9W*BwIfTr z%_0k}EqDk*5o~JEsCeIq)GW-(+4Ny5MsB}4Ehok`Xsp%hOQzdw^O9MJLy%A75%rI? zI?@VmKx<~Q&eds=dK*+s3kUAZ5y$TLmZ%LmH{DbF4>D+kgBiA)zr1E;f;Ib6Y}JGCTP`xmfn5wQ0U$r8sVRLAiS z+eG8g3d}SNmSxfttAU@=qnjW+%tsAgYneLK{*Fm8Ff65y!o0!Aa9$cfp3suI>fXCV zHtSKa-h7|MYK~&eOoALWiDjRxhqkAB+jv`ssCC1(Cc9vDp{a!Vwnl1so5lAcqf*%% z*bIg8(**U_)VG89b_xcgsW`-y$L^c3UTy-h$~P{qV=NsTpv-VQI=yc2CMt!J&;P zOOh#l#6_UC>lM(YZcS;%s+o^4r))mUb6VS zAL;66+Zv3a6nimQ3ssErjD+&vaVWM1&3ep@xc+`n7Sb5*l_1yxwSdTBj4~h<_V~{OTlyVWXQ437+P3NA4t8sQTyFS^rH*`@gB5e+ z`cYxcBRRQ4AlS)z z=js<_K4Tn7b6vwh9-WxKHB7LPZ-WNl>{KKrf2@>2NDKoP=Z_*cU<+yTA4mpRziAPG ze?tDklpwbL!jC3~|6(NoFo5NcUq}ri!aV=3tE=>z-V4|LGhfcdEORA3jp_ZPU0ui{0(HcKf|8NekS@I-^e&jffRy_9B!d>=OQ5`OQ;RW~`!hN#peM8#CR5X$lYcLRMtDOV}iP+r!~$ z*>i*q@d26J^EI|R=+(}k_dX_b8YC8I6&%%n%)rXOR*=ZR}7N7q_iNX5kOqu`DZe%|;EQ+@yGxgo@ z*jplQi+5Yf8X$@{R7{mu2FjN&&dK<9A`YbXL3?>LbvaEs&t`g?&Nak#eRYdt2XBxm zK7RQy9!vMCxz$~cJ|Q4EPvcbI$lbz6X>@9sXn!AEImB z)%6-spi}te!H({2Z?+)G_5wX&70b7bU!%SpYaibirq^9C>!2rjDarQoBmC*wbAqoD zs^1=vIJIgBF5kPY^c+_OLwWsg-<$KN51Kz?dQYL6LoZL(Dw9{#eEZ#ytU8P2JjHWK zd6Nxz_A~44$(6NkVNX&IlZ-sOH|4e%f;t;X9I#JtYV9Ld??h*P%|UAmMDU7LXY%JA zkD@g;lGubVTpgnF?iKKpQ=`iDH`xX=Und@?)(qDCoy+l`tt<_Z41OPkXsju)%8k4p z-qW0K+tfB22X^EE4Lj|mJjROZ?!3{RuTSt0u#&$TrxgeitzAn1hNL`-GxQor%tHhF zC3yM6Y=eE#k?o8hSevQt=(N)_F`OQO*ILy@)?cn=yocp>N+_Dn$bLiEjDG@yVg`U7xjIrRCa=C{s6X}w;V!kdze(bFNTJ|NPb zUAZ@p!ZI9o9OSuSvWNF7*S0V&;$Oe7_qDVj0Y2vTrIF(LwQsZ@AnlQm*Afm18{4Iq z5}&*xFyN=kpDlViwnOJ;M!rIjdSqh3Ljjo|>zh;1;I}UvXtAkpdk(*E7|poLoGUUe zQE+#-?IFKR6(7Ncs)Np`FfD>OAQ)DjU7%xanmY9aMF?#y3szX+{?u0TQuW>2l86=| z8%bRHxglHYZ5@`(cZ+X7Fy4<^cS0G7hf^ zCXoMSCyV_vd~391#TCA z1#HMGRSqwNvH@mhYU?kK!$iBwIbwB_o{y<>XImtjO>f@q)FC~)3u;%Fs}`m-c@(N_ zp6>fv=ILwqd}Gd)qTrK?RgL$p=Zw?Cb?!)^ACIo3O zh)167AO%eYnc^gXs|hK8>I3~fME)x)Zh!9{pX!yAk@gAvLmfpz!LFuiX(=l*tE^Rx zMKW_$^P8XY2NK8E1|5_*!Ce<41+@#ZVk${C$Lq??tseB=y1XelS+qZ1PNq3zjg3Um zbQR)uR6yN5Mz}jkq;G(Sl8hn?SLJ}irS0dsru}>7+t`Vb-D-ihg>No@+!4JOrp;3F z0%p=QN%J}RLFyOnZFU1#(z^QK>yqfR#-x*qJhdBktTDm7DFsi9+PTxowtILd{j^!W z&2>*_hU1rzl=4o3WWClB)s=px!7HpoGnmIiGsND}=I8WKgKf9kkP$i{S;4OkKN3ke z2Q?&$j2dk6tJkB3} zfoaTB`>!`BlJ#`#gpeKVl#AR#h>`EhLxskRK0nuL)WqdzzsVefNGceT0l>fK}PKh$_TEG0t`YTx71beEtmfOgM8=UxpBn;6?r_)dk`u(=6At+Qmh zaZwkmL&(xbcNS|i$Veq)yVEJQo*tT@ddF`jo>nbWBG%j4GIF!1E;W;;J7GcP2pk=fSyp;=U>%4r?6MpVz;6*7k)XLaDODgO!4Yii&m+H>W$H>O~r}Z zVg*WN2)k+QC-Yh;r7+nob}eu5^@~oa{`lP)`+@X|P71Xa@6(kEz4;hEHJ*4N z(8i8u(vwqcf4i+Rf5EXM9oV7*6tqqmzsbv3mo_66RxV5_cGUT}&~s`E6R4j38BF~< z9s{{`Ws{sI*GEz^GUIe`WOnrAWz{Yl_}5i3^C6$5FK9>9fcf@7DivnQxHTKj9I}mO zZwc9|AStvqY4fP4oHo1hOOw6trqWN|iqb)a3`(<3qYn!$VD*JvMG%!x$BavDs5?~X zg(vmK;$fi;Ostyo=dr-45arNY(7^x{M~N@zHu{0jhwnKS`rK(OZx}4a^6MMZuiSEX zb2_IfND*N1s2@=e(l*SpRrClGcT6{yyalpl9kK0o^PpAgcG|5LsFfB1RQ H>+$~sL@-Tp literal 0 HcmV?d00001 diff --git a/kadai2/src/tomoyukikobayashi/testdata/file3.png b/kadai2/src/tomoyukikobayashi/testdata/file3.png new file mode 100755 index 0000000000000000000000000000000000000000..f8f2af772c8146298c0d7dfaf2751d784602cc27 GIT binary patch literal 190334 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

xeaA)@9m)C~oW~Ng@ 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%0AEHAE5#Y6gYU- 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 BvmO8d literal 0 HcmV?d00001 diff --git a/kadai2/src/tomoyukikobayashi/testdata/file4.txt b/kadai2/src/tomoyukikobayashi/testdata/file4.txt new file mode 100755 index 0000000..e69de29 diff --git a/kadai2/src/tomoyukikobayashi/testdata/invalid.jpg b/kadai2/src/tomoyukikobayashi/testdata/invalid.jpg new file mode 100755 index 0000000..e69de29 From c9587ee1d0fd0d9acdc122ca7a3bdf70b2eaac7e Mon Sep 17 00:00:00 2001 From: baki33 Date: Sun, 18 Nov 2018 08:28:58 +0900 Subject: [PATCH 04/39] Delete .gitignore --- kadai1/tomoyukikobayashi/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 kadai1/tomoyukikobayashi/.gitignore diff --git a/kadai1/tomoyukikobayashi/.gitignore b/kadai1/tomoyukikobayashi/.gitignore deleted file mode 100644 index 78d2089..0000000 --- a/kadai1/tomoyukikobayashi/.gitignore +++ /dev/null @@ -1 +0,0 @@ -src/testdata* From e69b6649a450fbc642ee6e644732d25c79271016 Mon Sep 17 00:00:00 2001 From: baki33 Date: Sun, 18 Nov 2018 08:29:08 +0900 Subject: [PATCH 05/39] Delete Makefile --- kadai1/tomoyukikobayashi/Makefile | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 kadai1/tomoyukikobayashi/Makefile diff --git a/kadai1/tomoyukikobayashi/Makefile b/kadai1/tomoyukikobayashi/Makefile deleted file mode 100644 index f96d59a..0000000 --- a/kadai1/tomoyukikobayashi/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -.PHONY: deps -deps: - go get github.com/golang/lint/golint - -.PHONY: build -build: deps - go build -o imconv - -.PHONY: test -test: deps - go test -v ./... - -.PHONY: lint -lint: deps - go vet ./... - golint ./... - -.PHONY: fmt -fmt: deps - go fmt *.go \ No newline at end of file From 9e5a80744a77e7ba3479421d969ff89ca4a28fe7 Mon Sep 17 00:00:00 2001 From: baki33 Date: Sun, 18 Nov 2018 08:29:18 +0900 Subject: [PATCH 06/39] Delete README.md --- kadai1/tomoyukikobayashi/README.md | 64 ------------------------------ 1 file changed, 64 deletions(-) delete mode 100644 kadai1/tomoyukikobayashi/README.md diff --git a/kadai1/tomoyukikobayashi/README.md b/kadai1/tomoyukikobayashi/README.md deleted file mode 100644 index 563845c..0000000 --- a/kadai1/tomoyukikobayashi/README.md +++ /dev/null @@ -1,64 +0,0 @@ -GoImageConverter -===== - -# Overview - -GoImageConverterはGolangで書かれた画像コンバーターです。 - -# SetUp - -下記のようにコマンドを叩くと、実行形式のimconvファイルが生成されます -``` -cd kadai1/src/ -make build -``` - -# Usage - -実行時の引数は下記の通りです。 -オプションの後に、ディレクトリ名を引数として与えてください -``` -./imconv [OPTIONS] dirname - -f string - input image file format (default "jpg") - -t string - output image file format (default "png") -Supported formats are [jpg jpeg png] -``` - -オプションについて知りたい場合は、下記のようにしてヘルプを参照してください -``` -./imconv --help -``` -# 課題に対するアプローチ -## 次の仕様を満たすコマンドを作って下さい -- [x] ディレクトリを指定する - main.go - 特に工夫なくflagで指定 -- [x] 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト) - image_converter.go で以下の流れで実現 - 入力ファイルを開く - 入力ファイルをimageパッケージのDecode関数でデコード - 出力ファイルを開く - 出力ファイルに指定したフォーマットのimageサブパッケージのEncode関数でエンコード - deferで入力、出力ファイルをクローズ -- [x] ディレクトリ以下は再帰的に処理する - file_finder.go - ioutil.ReadDir で []os.FileInfo を取得して、FileInfoがdirectoryだったら - 再帰的に処理するようにした -- [x] 変換前と変換後の画像形式を指定できる(オプション) - image_converter.go - switch条件で、指定したフォーマットにマッチしたImageサブパッケージでEncodeするようにした -## 以下を満たすように開発してください -- [x] mainパッケージと分離する - /file 汎用的なファイル操作 - /imconv 画像処理特有のデータと操作 - の2パッケージを作成 -- [x] 自作パッケージと標準パッケージと準標準パッケージのみ使う -- [x] 準標準パッケージ:golang.org/x以下のパッケージ - 標準外パッケージは特に使わなかった -- [x] ユーザ定義型を作ってみる - file_finder.go - type structは使う場面がなかったので、[]string 型をラップして、レシーバを定義した -- [x] GoDocを生成してみる -  Packageとパブリックには全てGoDocコメントをつけて、godocコマンドで表示を確認した \ No newline at end of file From f9f7c4e81d5e7d75e846ba9172b46cea715f118c Mon Sep 17 00:00:00 2001 From: baki33 Date: Sun, 18 Nov 2018 08:29:44 +0900 Subject: [PATCH 07/39] Delete main.go --- kadai1/tomoyukikobayashi/src/main.go | 89 ---------------------------- 1 file changed, 89 deletions(-) delete mode 100644 kadai1/tomoyukikobayashi/src/main.go diff --git a/kadai1/tomoyukikobayashi/src/main.go b/kadai1/tomoyukikobayashi/src/main.go deleted file mode 100644 index 8af8210..0000000 --- a/kadai1/tomoyukikobayashi/src/main.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Pacakge main is the entry point of this project. -This mainly provides interaction logics and parameters -used in CLI intrerfaces. -*/ -package main - -import ( - "flag" - "fmt" - "os" - - "file" - "imconv" -) - -// CLIのExitコード -const ( - ExitSuccess = 0 - ExitError = iota -) - -// CLIのオプションパラメタ -var ( - f = flag.String("f", "jpg", "input image file format") - t = flag.String("t", "png", "output image file format") -) - -func init() { - // 引数がおかしいときはUsageを表示する - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] dirname \n", os.Args[0]) - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, "Supported formats are %v \n", imconv.SupportedExtensions()) - } -} - -// CLIツールのエントリーポイント -func main() { - flag.Parse() - dir := flag.Arg(0) - - // 引数のバリデーション - if len(dir) < 1 { - flag.Usage() - os.Exit(ExitError) - } - - if !imconv.Supported(*f) { - fmt.Fprintf(os.Stderr, "Supported formats are %v \n", imconv.SupportedExtensions()) - os.Exit(ExitError) - } - - if !imconv.Supported(*t) { - fmt.Fprintf(os.Stderr, "Supported formats are %v \n", imconv.SupportedExtensions()) - os.Exit(ExitError) - } - - if *f == *t { - fmt.Fprintf(os.Stderr, "input format and output format are the same \n") - os.Exit(ExitError) - } - - // 条件にマッチするファイルパスを検索 - paths, err := file.Find(dir, imconv.GetFormatThesaurus(*f)) - if err != nil { - fmt.Fprintf(os.Stderr, "%v", err) - os.Exit(ExitError) - } - - if len(paths) < 1 { - fmt.Println("no files matched") - os.Exit(ExitSuccess) - } - - // 画像を変換する - // TOOD 画像が大量になる場合はgoroutineで並列処理しても良さそう - for _, path := range paths { - fmt.Println("src:", path) - newPath, err := imconv.Convert(path, *f, *t) - if err != nil { - fmt.Fprintf(os.Stderr, "%v", err) - os.Exit(ExitError) - } - fmt.Println("dst:", newPath) - } - - os.Exit(ExitSuccess) -} From 4bf0dd91b981b0849a3ad063c3d1626ecd726ff9 Mon Sep 17 00:00:00 2001 From: baki33 Date: Sun, 18 Nov 2018 08:29:51 +0900 Subject: [PATCH 08/39] Delete file_finder.go --- .../tomoyukikobayashi/src/file/file_finder.go | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 kadai1/tomoyukikobayashi/src/file/file_finder.go diff --git a/kadai1/tomoyukikobayashi/src/file/file_finder.go b/kadai1/tomoyukikobayashi/src/file/file_finder.go deleted file mode 100644 index e9252a5..0000000 --- a/kadai1/tomoyukikobayashi/src/file/file_finder.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -Package file provides methods and structs -that handle file and directory operations. -Almost all of them just wrap primitive methods -defined in golang native packages. -*/ -package file - -import ( - "fmt" - "io/ioutil" - "path/filepath" - "strings" -) - -// Find returns all filepaths in directory specified as dir. -// If you want to filter the paths by file extensions, use exts. -func Find(dir string, exts []string) ([]string, error) { - paths, err := find(dir) - if err != nil { - return nil, err - } - - return paths.filter(exts), nil -} - -// find dirで指定したディレクトリ配下のファイルパス一覧を返却 -func find(dir string) (paths, error) { - files, err := ioutil.ReadDir(dir) - if err != nil { - return nil, fmt.Errorf("could not open dir %s", dir) - } - - paths := paths{} - for _, file := range files { - path := filepath.Join(dir, file.Name()) - // FileInfoがディレクトリなら再帰処理 - if file.IsDir() { - dsc, err := find(path) - if err != nil { - return nil, fmt.Errorf("could not open dir %s", path) - } - // ...を付けるとslice同士連結できるよう - paths = append(paths, dsc...) - continue - } - paths = append(paths, path) - } - - return paths, nil -} - -// paths ディレクトリパスの集合に対する操作を提供する -type paths []string - -// filter extsで指定した拡張子にマッチする結果を絞り込む -// filter(条件1).filter(条件2) のようにして順に絞り込んでもよい -func (p paths) filter(exts []string) paths { - paths := paths{} - for _, v := range p { - if matches(v, exts) { - paths = append(paths, v) - } - } - return paths -} - -// matches ignore caseでpathがextsで指定した拡張子にマッチするかを検証する -func matches(path string, exts []string) bool { - lpath := strings.ToLower(path) - for _, v := range exts { - if strings.HasSuffix(lpath, "."+strings.ToLower(v)) { - return true - } - } - return false -} From 71569f9f8cff471fb7240b17031caf321cf850da Mon Sep 17 00:00:00 2001 From: baki33 Date: Sun, 18 Nov 2018 08:30:00 +0900 Subject: [PATCH 09/39] Delete formats.go --- .../tomoyukikobayashi/src/imconv/formats.go | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 kadai1/tomoyukikobayashi/src/imconv/formats.go diff --git a/kadai1/tomoyukikobayashi/src/imconv/formats.go b/kadai1/tomoyukikobayashi/src/imconv/formats.go deleted file mode 100644 index b004f39..0000000 --- a/kadai1/tomoyukikobayashi/src/imconv/formats.go +++ /dev/null @@ -1,54 +0,0 @@ -package imconv - -import "strings" - -// HACK 本当はimmutableにしたいけどどうもできなそう -// ポインタだししょうがないか。。。厳密にやるなら直接触らせないのがいいかな -var ( - jpgExts = []string{"jpg", "jpeg"} - pngExts = []string{"png"} - supportExts = [][]string{ - jpgExts, - pngExts, - } - allSupportExts = append(jpgExts, pngExts...) -) - -// Supported returns if the package can handle speficied file format -func Supported(ext string) bool { - for _, spext := range allSupportExts { - if strings.ToLower(ext) == spext { - return true - } - } - return false -} - -// SupportedExtensions returns image formats the package can handle -func SupportedExtensions() []string { - clone := make([]string, len(allSupportExts)) - copy(clone, allSupportExts) - return clone -} - -// GetFormatThesaurus returns image formats that are the same with arg ext -func GetFormatThesaurus(ext string) []string { - for _, spexts := range supportExts { - for _, spext := range spexts { - if strings.ToLower(ext) == spext { - return spexts - } - } - } - return nil -} - -func isSameFormat(from string, to string) bool { - th := GetFormatThesaurus(from) - for _, v := range th { - if strings.ToLower(to) == v { - return true - } - } - return false -} From 4cec6247054aa67619ef64cc980ce90e929a9794 Mon Sep 17 00:00:00 2001 From: baki33 Date: Sun, 18 Nov 2018 08:30:09 +0900 Subject: [PATCH 10/39] Delete formats_test.go --- .../src/imconv/formats_test.go | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 kadai1/tomoyukikobayashi/src/imconv/formats_test.go diff --git a/kadai1/tomoyukikobayashi/src/imconv/formats_test.go b/kadai1/tomoyukikobayashi/src/imconv/formats_test.go deleted file mode 100644 index f2130f6..0000000 --- a/kadai1/tomoyukikobayashi/src/imconv/formats_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package imconv - -import ( - "reflect" - "testing" -) - -func Test_Supported(t *testing.T) { - tests := []struct { - ext string - want bool - }{ - {"jpg", true}, - {"jpeg", true}, - {"JPG", true}, - {"png", true}, - {"invalid", false}, - {"", false}, - } - - for _, tt := range tests { - t.Run(tt.ext, func(t *testing.T) { - if got := Supported(tt.ext); got != tt.want { - t.Fatalf("want = %#v, got = %#v", tt.want, got) - } - }) - } -} - -func Test_GetFormatThesaurus(t *testing.T) { - tests := []struct { - ext string - want []string - }{ - {"jpg", jpgExts}, - {"JPG", jpgExts}, - {"jpeg", jpgExts}, - {"png", pngExts}, - {"invalid", nil}, - {"", nil}, - } - - for _, tt := range tests { - t.Run(tt.ext, func(t *testing.T) { - if got := GetFormatThesaurus(tt.ext); !reflect.DeepEqual(got, tt.want) { - t.Fatalf("want = %#v, got = %#v", tt.want, got) - } - }) - } -} - -func Test_isSameFormat(t *testing.T) { - tests := []struct { - from string - to string - want bool - }{ - {"jpg", "jpeg", true}, - {"JPG", "jpeg", true}, - {"jpeg", "jpeg", true}, - {"JPG", "png", false}, - {"png", "invalid", false}, - {"invalid", "invalid", false}, - } - - for _, tt := range tests { - t.Run(tt.from+"_"+tt.to, func(t *testing.T) { - if got := isSameFormat(tt.from, tt.to); got != tt.want { - t.Fatalf("want = %#v, got = %#v", tt.want, got) - } - }) - } -} From 4458643cd57d2e962f1c307480225dd081c8d8af Mon Sep 17 00:00:00 2001 From: baki33 Date: Sun, 18 Nov 2018 08:30:16 +0900 Subject: [PATCH 11/39] Delete image_converter.go --- .../src/imconv/image_converter.go | 71 ------------------- 1 file changed, 71 deletions(-) delete mode 100644 kadai1/tomoyukikobayashi/src/imconv/image_converter.go diff --git a/kadai1/tomoyukikobayashi/src/imconv/image_converter.go b/kadai1/tomoyukikobayashi/src/imconv/image_converter.go deleted file mode 100644 index ef12d0f..0000000 --- a/kadai1/tomoyukikobayashi/src/imconv/image_converter.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -Package imconv provides image convert mathods and -some validation logics, like if the package can handle -input format or not. -*/ -package imconv - -import ( - "fmt" - "image" - "image/jpeg" - "image/png" - "log" - "os" - "strings" -) - -// Convert convert image from from fromat to to fromat -func Convert(path string, from string, to string) (string, error) { - if !Supported(from) { - return "", fmt.Errorf("can not handle %s", from) - } - - if !Supported(to) { - return "", fmt.Errorf("can not handle %s", to) - } - - if isSameFormat(from, to) { - return "", fmt.Errorf("%s and %s are the same format", from, to) - } - - parts := strings.Split(path, ".") - newPath := strings.Join(parts[:len(parts)-1], ".") + "." + to - - src, err := os.Open(path) - if err != nil { - log.Fatal(err) - return "", fmt.Errorf("cannot open file %s", path) - } - defer src.Close() - - img, _, err := image.Decode(src) - if err != nil { - log.Fatal(err) - return "", fmt.Errorf("cannot decode file %s", path) - } - - dst, err := os.Create(newPath) - if err != nil { - log.Fatal(err) - return "", fmt.Errorf("cannot create file %s", newPath) - } - defer dst.Close() - - // HACK caseにベタがきしているが、formats定義を展開した方が良い - // case条件にsliceを展開した物を使えないのだろうか。。。 - switch strings.ToLower(to) { - case "jpg", "jpeg": - if err := jpeg.Encode(dst, img, nil); err != nil { - return "", err - } - case "png": - if err := png.Encode(dst, img); err != nil { - return "", err - } - default: - log.Fatal("reached to invaid condition") - } - - return newPath, nil -} From d7df320be9701e0607ca659c6a0786099f074c42 Mon Sep 17 00:00:00 2001 From: baki33 Date: Sun, 18 Nov 2018 15:50:41 +0900 Subject: [PATCH 12/39] Update main.go --- kadai2/src/tomoyukikobayashi/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/kadai2/src/tomoyukikobayashi/main.go b/kadai2/src/tomoyukikobayashi/main.go index 964b621..16afb07 100644 --- a/kadai2/src/tomoyukikobayashi/main.go +++ b/kadai2/src/tomoyukikobayashi/main.go @@ -42,7 +42,6 @@ func (c *CLI) Run(args []string) int { flags := flag.NewFlagSet("imconv", flag.ContinueOnError) flags.SetOutput(c.errStream) - // TODO flagsに突っ込まないといけない一方、オプション書式にも定義したいので、上書きしてしまっている tmpf, tmpt := "", "" f, t := &tmpf, &tmpt flags.StringVar(t, "f", "jpg", "input image file format") From e5de4eea7246b348234fc6e4651d0bc046df687d Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Wed, 21 Nov 2018 09:07:58 +0900 Subject: [PATCH 13/39] add load question logics --- kadai3-1/Makefile | 26 +++++++ kadai3-1/README.md | 19 +++++ kadai3-1/src/tomoyukikobayashi/main.go | 48 ++++++++++++ .../src/tomoyukikobayashi/typing/answers.go | 1 + kadai3-1/src/tomoyukikobayashi/typing/game.go | 1 + .../src/tomoyukikobayashi/typing/questions.go | 77 +++++++++++++++++++ kadai3-1/words.yaml | 17 ++++ 7 files changed, 189 insertions(+) create mode 100644 kadai3-1/Makefile create mode 100644 kadai3-1/README.md create mode 100644 kadai3-1/src/tomoyukikobayashi/main.go create mode 100644 kadai3-1/src/tomoyukikobayashi/typing/answers.go create mode 100644 kadai3-1/src/tomoyukikobayashi/typing/game.go create mode 100644 kadai3-1/src/tomoyukikobayashi/typing/questions.go create mode 100644 kadai3-1/words.yaml diff --git a/kadai3-1/Makefile b/kadai3-1/Makefile new file mode 100644 index 0000000..69fcbf4 --- /dev/null +++ b/kadai3-1/Makefile @@ -0,0 +1,26 @@ +.PHONY: deps +deps: + go get github.com/golang/lint/golint + go get github.com/ameske/wordnik-go + +.PHONY: build +build: + go build -o type tomoyukikobayashi + +.PHONY: test +test: + go test -v -cover ./... + +.PHONY: cover +cover: + go test -coverprofile=mainprof tomoyukikobayashi + go tool cover -html=mainprof + +.PHONY: lint +lint: deps + go vet ./... + golint ./... + +.PHONY: fmt +fmt: deps + go fmt *.go diff --git a/kadai3-1/README.md b/kadai3-1/README.md new file mode 100644 index 0000000..edb5d9d --- /dev/null +++ b/kadai3-1/README.md @@ -0,0 +1,19 @@ +GoImageConverter +===== + +# Overview + +TODO + +# SetUp + +下記のようにコマンドを叩くと、実行形式のTODOファイルが生成されます +``` +cd kadai2 +make build +``` + +# Usage + + +# 課題に対するアプローチ diff --git a/kadai3-1/src/tomoyukikobayashi/main.go b/kadai3-1/src/tomoyukikobayashi/main.go new file mode 100644 index 0000000..45dd6b6 --- /dev/null +++ b/kadai3-1/src/tomoyukikobayashi/main.go @@ -0,0 +1,48 @@ +/* +Pacakge main is the entry point of this project. +This mainly provides interaction logics and parameters +used in CLI intrerfaces. +*/ +package main + +import ( + "fmt" + "io" + "os" + + "tomoyukikobayashi/typing" +) + +// CLIのExitコード +const ( + ExitSuccess = 0 + ExitInvalidArgs = iota + ExitError = iota +) + +// Exitしてもテスト落ちない操作するようにエイリアスにしている +var exit = os.Exit + +// CLI テストしやすいようにCLIの出力先を差し替えられるようにしている +type CLI struct { + outStream io.Writer + errStream io.Writer +} + +// CLIツールのエントリーポイント +func main() { + cli := &CLI{outStream: os.Stdout, errStream: os.Stderr} + exit(cli.Run(os.Args)) +} + +// Run テスト用に実行ロジックを切り出した内容 +func (c *CLI) Run(args []string) int { + + q, err := typing.NewQuestioner() + fmt.Printf("%v %v", q, err) + + w, err := q.GetNewWord(1) + fmt.Printf("%v %v", w, err) + + return ExitSuccess +} diff --git a/kadai3-1/src/tomoyukikobayashi/typing/answers.go b/kadai3-1/src/tomoyukikobayashi/typing/answers.go new file mode 100644 index 0000000..bf28877 --- /dev/null +++ b/kadai3-1/src/tomoyukikobayashi/typing/answers.go @@ -0,0 +1 @@ +package typing diff --git a/kadai3-1/src/tomoyukikobayashi/typing/game.go b/kadai3-1/src/tomoyukikobayashi/typing/game.go new file mode 100644 index 0000000..bf28877 --- /dev/null +++ b/kadai3-1/src/tomoyukikobayashi/typing/game.go @@ -0,0 +1 @@ +package typing diff --git a/kadai3-1/src/tomoyukikobayashi/typing/questions.go b/kadai3-1/src/tomoyukikobayashi/typing/questions.go new file mode 100644 index 0000000..37356ec --- /dev/null +++ b/kadai3-1/src/tomoyukikobayashi/typing/questions.go @@ -0,0 +1,77 @@ +// Package typing このパッケージはタイピングゲームに関するロジックとデータを格納します +package typing + +import ( + "io/ioutil" + "math/rand" + "os" + "path/filepath" + + yamler "gopkg.in/yaml.v2" +) + +const ( + wordFile = "words.yaml" +) + +// Questioner 質問で使うワードを提供します +type Questioner interface { + // GetNewWord 引数で与えた難易度に対して一つランダムにワードを返します + GetNewWord(level int) (string, error) +} + +type constQuestioner struct { + qs map[int][]string +} + +// NewQuestioner Questionerのコンストラクタです +func NewQuestioner() (Questioner, error) { + q := &constQuestioner{ + qs: map[int][]string{}, + } + if err := q.loadWords(); err != nil { + return nil, err + } + return q, nil +} + +func random(min int, max int) int { + return rand.Intn(max-min) + min +} + +func (q *constQuestioner) GetNewWord(level int) string { + rand := random(0, len(q.qs[level])) + // HACK ほんとはmap okを見た方がいいけど、省略 + return "", q.qs[level][rand] +} + +// Yaml HACK 使用しているパッケージの使用でしょうがなくpublicにしている +type Yaml struct { + Level1 []string `yaml:"Level1,flow"` + Level2 []string `yaml:"Level2,flow"` + Level3 []string `yaml:"Level3,flow"` +} + +// TOOD ファイル直呼びするんじゃなく、NewQuestionerでio.Reader渡すようにしておくとテストしやすいかな +func (q *constQuestioner) loadWords() error { + cur, _ := os.Getwd() + // yamlファイルから語彙リストを読み出す + data, err := ioutil.ReadFile(filepath.Join(cur, wordFile)) + if err != nil { + return err + } + + var y Yaml + err = yamler.Unmarshal([]byte(data), &y) + if err != nil { + return err + } + + // HACK レベルは今の所少ないのでとりあえずベタがき。 + // yamlの構成工夫して動的に取れた方がいい + q.qs[1] = y.Level1 + q.qs[2] = y.Level2 + q.qs[3] = y.Level3 + + return nil +} diff --git a/kadai3-1/words.yaml b/kadai3-1/words.yaml new file mode 100644 index 0000000..e9716b9 --- /dev/null +++ b/kadai3-1/words.yaml @@ -0,0 +1,17 @@ +Level1: +- hoge +- foo +- baz +- bar + +Level2: +- difficult +- omororororo +- feiajfiejawefa +- femiejaigfeag + +Level3: +- testewatawetawet +- gvewagweawegawe +- geawegawegawegawe +- gwageawegaweg \ No newline at end of file From 06ad2ec66e2f443347b1b77760e0b0a373ebec60 Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Thu, 22 Nov 2018 09:44:30 +0900 Subject: [PATCH 14/39] =?UTF-8?q?=E3=81=A8=E3=82=8A=E3=81=82=E3=81=88?= =?UTF-8?q?=E3=81=9A=E4=B8=80=E9=80=9A=E3=82=8A=E5=8B=95=E3=81=8F=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai3-1/src/tomoyukikobayashi/main.go | 38 +++++-- .../src/tomoyukikobayashi/typing/answers.go | 1 - kadai3-1/src/tomoyukikobayashi/typing/game.go | 106 ++++++++++++++++++ .../src/tomoyukikobayashi/typing/questions.go | 16 +-- 4 files changed, 145 insertions(+), 16 deletions(-) delete mode 100644 kadai3-1/src/tomoyukikobayashi/typing/answers.go diff --git a/kadai3-1/src/tomoyukikobayashi/main.go b/kadai3-1/src/tomoyukikobayashi/main.go index 45dd6b6..899ee42 100644 --- a/kadai3-1/src/tomoyukikobayashi/main.go +++ b/kadai3-1/src/tomoyukikobayashi/main.go @@ -15,9 +15,8 @@ import ( // CLIのExitコード const ( - ExitSuccess = 0 - ExitInvalidArgs = iota - ExitError = iota + ExitSuccess = 0 + ExitError = 1 ) // Exitしてもテスト落ちない操作するようにエイリアスにしている @@ -25,24 +24,47 @@ var exit = os.Exit // CLI テストしやすいようにCLIの出力先を差し替えられるようにしている type CLI struct { + inStream io.Reader outStream io.Writer errStream io.Writer } // CLIツールのエントリーポイント func main() { - cli := &CLI{outStream: os.Stdout, errStream: os.Stderr} + cli := &CLI{inStream: os.Stdin, outStream: os.Stdout, errStream: os.Stderr} exit(cli.Run(os.Args)) } // Run テスト用に実行ロジックを切り出した内容 func (c *CLI) Run(args []string) int { - q, err := typing.NewQuestioner() - fmt.Printf("%v %v", q, err) + game, err := typing.NewGame() + if err != nil { + fmt.Fprintf(c.outStream, "failed to initizalize game %v", err) + return ExitError + } - w, err := q.GetNewWord(1) - fmt.Printf("%v %v", w, err) + fmt.Fprintf(c.outStream, "start game 60 sec\n") + qCh, aCh, rCh := game.Run(10, c.inStream) + + for { + q, progress := <-qCh + fmt.Fprintf(c.outStream, "%v\n", q) + if !progress { + break + } + + fmt.Fprintf(c.outStream, ">") + + a, progress := <-aCh + fmt.Fprintf(c.outStream, "%v\n", a) + if !progress { + break + } + } + + r := <-rCh + fmt.Fprintf(c.outStream, "clear %v miss %v\n", r[0], r[1]) return ExitSuccess } diff --git a/kadai3-1/src/tomoyukikobayashi/typing/answers.go b/kadai3-1/src/tomoyukikobayashi/typing/answers.go deleted file mode 100644 index bf28877..0000000 --- a/kadai3-1/src/tomoyukikobayashi/typing/answers.go +++ /dev/null @@ -1 +0,0 @@ -package typing diff --git a/kadai3-1/src/tomoyukikobayashi/typing/game.go b/kadai3-1/src/tomoyukikobayashi/typing/game.go index bf28877..be72eff 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/game.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/game.go @@ -1 +1,107 @@ package typing + +import ( + "bufio" + "context" + "io" + "time" +) + +// Game タイピングゲームを制御するロジックを返す +type Game interface { + Run(int, io.Reader) (question <-chan string, + answer <-chan string, result <-chan [2]int) +} + +type constGame struct { + Questioner + // NOTE コンテキストに状態格納しようとしたがコピーして作り直すから、途中で書き換えてもルーチンをまたいでシェアされない模様 + // 結局共有変数に書いていることになるので、脆弱感がすごい + clear int + miss int + currentWord string +} + +// NewGame Gameのコンストラクタです +func NewGame() (Game, error) { + q, err := NewQuestioner() + if err != nil { + return nil, err + } + return &constGame{Questioner: q}, nil +} + +// Run ゲームを開始する +func (c *constGame) Run(timeout int, input io.Reader) (<-chan string, + <-chan string, <-chan [2]int) { + sc := bufio.NewScanner(input) + + qCh := make(chan string) + aCh := make(chan string) + routines := 2 + rCh := make(chan [2]int, routines) + + bc := context.Background() + tc := time.Duration(timeout) * time.Second + ctx, _ := context.WithTimeout(bc, tc) + // NOTE defer cancel() defer cancelするとDone条件閉じてる + + // TODO 読みづらいので関数に切り出す + go func() { + for { + word := c.GetNewWord(c.nextLevel()) + qCh <- word + c.currentWord = word + select { + case <-ctx.Done(): + // TODO once.DO が使えるかも + rCh <- [2]int{c.clear, c.miss} + close(qCh) + return + default: + //do nothing + } + } + }() + + // 読み込みしてaChに送る + go func() { + for { + if sc.Scan() { + ans := sc.Text() + aCh <- ans + if c.isCorrect(ans) { + c.clear = c.clear + 1 + } else { + c.miss = c.miss + 1 + } + } + + select { + case <-ctx.Done(): + rCh <- [2]int{c.clear, c.miss} + close(aCh) + return + default: + //do nothing + } + } + }() + + return qCh, aCh, rCh +} + +// HACK 成功した回数に応じて、使う語彙のレベルを決める。ここは決め打ちで書いてる +func (c *constGame) nextLevel() int { + if c.clear < 10 { + return 1 + } + if c.clear < 20 { + return 2 + } + return 3 +} + +func (c *constGame) isCorrect(word string) bool { + return c.currentWord == word +} diff --git a/kadai3-1/src/tomoyukikobayashi/typing/questions.go b/kadai3-1/src/tomoyukikobayashi/typing/questions.go index 37356ec..904b0cf 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/questions.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/questions.go @@ -6,6 +6,7 @@ import ( "math/rand" "os" "path/filepath" + "time" yamler "gopkg.in/yaml.v2" ) @@ -17,14 +18,14 @@ const ( // Questioner 質問で使うワードを提供します type Questioner interface { // GetNewWord 引数で与えた難易度に対して一つランダムにワードを返します - GetNewWord(level int) (string, error) + GetNewWord(level int) string } type constQuestioner struct { qs map[int][]string } -// NewQuestioner Questionerのコンストラクタです +// NewQuestioner Questionerのコンストラクタ func NewQuestioner() (Questioner, error) { q := &constQuestioner{ qs: map[int][]string{}, @@ -36,13 +37,14 @@ func NewQuestioner() (Questioner, error) { } func random(min int, max int) int { + rand.Seed(time.Now().UnixNano()) return rand.Intn(max-min) + min } func (q *constQuestioner) GetNewWord(level int) string { - rand := random(0, len(q.qs[level])) + rand := random(1, len(q.qs[level])) // HACK ほんとはmap okを見た方がいいけど、省略 - return "", q.qs[level][rand] + return q.qs[level][rand] } // Yaml HACK 使用しているパッケージの使用でしょうがなくpublicにしている @@ -52,7 +54,7 @@ type Yaml struct { Level3 []string `yaml:"Level3,flow"` } -// TOOD ファイル直呼びするんじゃなく、NewQuestionerでio.Reader渡すようにしておくとテストしやすいかな +// TOOD NewGameでyamlの型とio.Reader(ファイル)渡してやる方が、Questionerが汎用になる func (q *constQuestioner) loadWords() error { cur, _ := os.Getwd() // yamlファイルから語彙リストを読み出す @@ -67,8 +69,8 @@ func (q *constQuestioner) loadWords() error { return err } - // HACK レベルは今の所少ないのでとりあえずベタがき。 - // yamlの構成工夫して動的に取れた方がいい + // TODO レベルは今の所少ないのでとりあえずベタがき + // yamlの構成工夫 or interfaceとしてレベル別に取る関数生やして動的に撮れる方が良い q.qs[1] = y.Level1 q.qs[2] = y.Level2 q.qs[3] = y.Level3 From 2abbf61e7ec9fc0252e6bb902b30a6c060581fda Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Thu, 22 Nov 2018 09:48:36 +0900 Subject: [PATCH 15/39] =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai3-1/src/tomoyukikobayashi/main.go | 4 ++-- kadai3-1/src/tomoyukikobayashi/typing/questions.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kadai3-1/src/tomoyukikobayashi/main.go b/kadai3-1/src/tomoyukikobayashi/main.go index 899ee42..8a5b956 100644 --- a/kadai3-1/src/tomoyukikobayashi/main.go +++ b/kadai3-1/src/tomoyukikobayashi/main.go @@ -44,7 +44,8 @@ func (c *CLI) Run(args []string) int { return ExitError } - fmt.Fprintf(c.outStream, "start game 60 sec\n") + // TOOD 長いことテスト固めたくないので外から与えるようにする + fmt.Fprintf(c.outStream, "start game 10 sec\n") qCh, aCh, rCh := game.Run(10, c.inStream) for { @@ -55,7 +56,6 @@ func (c *CLI) Run(args []string) int { } fmt.Fprintf(c.outStream, ">") - a, progress := <-aCh fmt.Fprintf(c.outStream, "%v\n", a) if !progress { diff --git a/kadai3-1/src/tomoyukikobayashi/typing/questions.go b/kadai3-1/src/tomoyukikobayashi/typing/questions.go index 904b0cf..c1a303c 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/questions.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/questions.go @@ -54,7 +54,7 @@ type Yaml struct { Level3 []string `yaml:"Level3,flow"` } -// TOOD NewGameでyamlの型とio.Reader(ファイル)渡してやる方が、Questionerが汎用になる +// TOOD NewGameでinterfaceとio.Reader渡してやる方が、Questionerが汎用になる func (q *constQuestioner) loadWords() error { cur, _ := os.Getwd() // yamlファイルから語彙リストを読み出す From 3e212f5397b49d9d7e33dac0296b9bb18f94ef0e Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:54:52 +0900 Subject: [PATCH 16/39] Delete Makefile --- kadai2/Makefile | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 kadai2/Makefile diff --git a/kadai2/Makefile b/kadai2/Makefile deleted file mode 100644 index 0e9ae14..0000000 --- a/kadai2/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -.PHONY: deps -deps: - go get github.com/golang/lint/golint - -.PHONY: build -build: - go build -o imconv tomoyukikobayashi - -.PHONY: test -test: - go test -v -cover ./... - -.PHONY: cover -cover: - go test -coverprofile=main tomoyukikobayashi - go tool cover -html=main - go test -coverprofile=imconv tomoyukikobayashi/imconv - go tool cover -html=imconv - go test -coverprofile=file tomoyukikobayashi/file - go tool cover -html=file - -.PHONY: lint -lint: deps - go vet ./... - golint ./... - -.PHONY: fmt -fmt: deps - go fmt *.go From 8bfcc956e77103734a0f1af8b84367f898a19a46 Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:55:02 +0900 Subject: [PATCH 17/39] Delete README.md --- kadai2/README.md | 106 ----------------------------------------------- 1 file changed, 106 deletions(-) delete mode 100644 kadai2/README.md diff --git a/kadai2/README.md b/kadai2/README.md deleted file mode 100644 index 28370cf..0000000 --- a/kadai2/README.md +++ /dev/null @@ -1,106 +0,0 @@ -GoImageConverter -===== - -# Overview - -GoImageConverterはGolangで書かれた画像コンバーターです。 - -# SetUp - -下記のようにコマンドを叩くと、実行形式のimconvファイルが生成されます -``` -cd kadai2 -make build -``` - -# Usage - -実行時の引数は下記の通りです。 -オプションの後に、ディレクトリ名を引数として与えてください -``` -./imconv [OPTIONS] dirname - -f string - input image file format (default "jpg") - -t string - output image file format (default "png") -Supported formats are [jpg jpeg png] -``` - -# 課題に対するアプローチ -## 1回目の宿題のテストを作ってみて下さい -テーブル駆動テストを行う -基本的にほぼテーブル駆動でテストは作成 - -テストヘルパーを作ってみる -あまりヘルパーにしたいところが見当たらなかった -setup/teardown系は*testing.Tのヘルパー関数群を利用している - -テストのしやすさを考えてリファクタリングしてみる -https://mattn.kaoriya.net/software/lang/go/20161025113154.htm -https://deeeet.com/writing/2014/12/18/golang-cli-test/ -CLIのインターフェイス部がテストしづらくなっていたので、上記を参考に、リファクタリングを行った -主にやったことは -- Exit処理をテストから上書き可能にするために、エイリアスを与え、テストではExit処理をラップして、エラーコードを取れるようにした -- CLIの出力先に任意のio.Writerを与えられるように構造体を定義し、実行環境ではos.Std、テストではbytes.Bufferに出力するように変更している - -テストのカバレッジを取ってみる -make test > coverage がみれる -make cover > プロファイルで通っていないところがみれる -というコマンドを定義して確認した -概ね全体をカバーしたが、いくつか通しづらいところがあったので、こちらはプルリクでコメント補足する - -## io.Readerとio.Writerについて調べてみよう -標準パッケージでどのように使われているか -io.Readerとio.Writerがあることで -どういう利点があるのか具体例を挙げて考えてみる - -https://github.com/golang/go/tree/master/src -をio.Reader、io.Writerで検索してみると - -binary、csv、http、バッファ、XML、アーカイブファイル、標準入出力、etc -多様なIOフォーマットの読み書きで、基底となる共通のIFとして使われている - -例としてpngのreaderを取り出して見てみると、 - -image/png/reader.go - -// Decode reads a PNG image from r and returns it as an image.Image. -func Decode(r io.Reader) (image.Image, error) { -PNGを扱うライブラリを実装しているにも関わらず、入力をPNG特有のなにかとして扱っていない - -// 実際に処理しているところ d.r がio.Reader -n, err := io.ReadFull(d.r, d.tmp[:8]) -読み出しは、同じ規約(io.Reader)を持った標準ライブラリ関数に処理させている - -IOに関して固有のロジックを実装せずにすむ (読み出した後に、PNG特有のデータ構造として考える) -同じ規約を持ったライブラリに、まるっとIO処理を委譲できる(取り回しがしやすい) -そもそもここ、どういう風に引数作ったらいいんだろう?を悩む必要がない --> 作る側として便利 - -image/png/reader_test.go - -// PNGのテストだが、stringから読み出させている -const ( - ihdr = "\x00\x00\x00\x0dIHDR\x00\x00\x00\x01\x00\x00\x00\x02\x08\x00\x00\x00\x00\xbc\xea\xe9\xfb" - idat = "\x00\x00\x00\x0eIDAT\x78\x9c\x62\x62\x00\x04\x00\x00\xff\xff\x00\x06\x00\x03\xfa\xd0\x59\xae" - iend = "\x00\x00\x00\x00IEND\xae\x42\x60\x82" -) -Decode(strings.NewReader(pngHeader + ihdr + idatWhite + idatZero + iend)) - -// PNGのテストだが、バイト列から読み出させている -_, err := Decode(bytes.NewReader([]byte{ - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, - 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x08, 0x06, 0x00, 0x00, 0x00, 0x30, 0x57, 0xb3, - 0xfd, 0x00, 0x00, 0x00, 0x15, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x62, 0x62, 0x20, 0x12, 0x8c, - 0x2a, 0xa4, 0xb3, 0x42, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x13, 0x38, 0x00, 0x15, 0x2d, 0xef, - 0x5f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, -})) - -Readerインターフェイスを実装していればいいので、IO元/先をPNGファイルにこだわらずにすむ (テストしやすい) -IOする必要があるライブラリを使おうとするたびに個別の規約を覚える必要がない -同じIO IFを提供しているライブラリに読み出しを任せられる(取り回しがしやすい) --> 使う側として便利 - -何かを読んでデータをp []byteに書き込むという振る舞いが満たされていることを共通の規約にすることで、 -実装の詳細がどんな性質であるかを意識することなく、上記にあげたような諸々の便利さを手に入れることができている -func Read(p []byte) (n int, err error) From 1b37ffa7e431a0cfc5a2fd4a829d9d80622a9ed2 Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:55:12 +0900 Subject: [PATCH 18/39] Delete file_finder.go --- .../src/tomoyukikobayashi/file/file_finder.go | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 kadai2/src/tomoyukikobayashi/file/file_finder.go diff --git a/kadai2/src/tomoyukikobayashi/file/file_finder.go b/kadai2/src/tomoyukikobayashi/file/file_finder.go deleted file mode 100644 index 16c4bd6..0000000 --- a/kadai2/src/tomoyukikobayashi/file/file_finder.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -Package file provides methods and structs -that handle file and directory operations. -Almost all of them just wrap primitive methods -defined in golang native packages. -*/ -package file - -import ( - "fmt" - "io/ioutil" - "path/filepath" - "strings" -) - -// Find returns all filepaths in directory specified as dir. -// If you want to filter the paths by file extensions, use exts. -func Find(dir string, exts []string) ([]string, error) { - paths, err := findPaths(dir) - if err != nil { - return nil, err - } - - return paths.filter(exts), nil -} - -// find dirで指定したディレクトリ配下のファイルパス一覧を返却 -func findPaths(dir string) (paths, error) { - files, err := ioutil.ReadDir(dir) - if err != nil { - return nil, fmt.Errorf("could not open dir %s", dir) - } - - paths := paths{} - for _, file := range files { - path := filepath.Join(dir, file.Name()) - // FileInfoがディレクトリなら再帰処理 - if file.IsDir() { - dsc, err := findPaths(path) - if err != nil { - return nil, fmt.Errorf("could not open dir %s", path) - } - // ...を付けるとslice同士連結できるよう - paths = append(paths, dsc...) - continue - } - paths = append(paths, path) - } - - return paths, nil -} - -// paths ディレクトリパスの集合に対する操作を提供する -type paths []string - -// filter extsで指定した拡張子にマッチする結果を絞り込む -// filter(条件1).filter(条件2) のようにして順に絞り込んでもよい -func (p paths) filter(exts []string) paths { - paths := paths{} - for _, v := range p { - if matches(v, exts) { - paths = append(paths, v) - } - } - return paths -} - -// matches ignore caseでpathがextsで指定した拡張子にマッチするかを検証する -func matches(path string, exts []string) bool { - lpath := strings.ToLower(path) - for _, v := range exts { - if strings.HasSuffix(lpath, "."+strings.ToLower(v)) { - return true - } - } - return false -} From 788ceb55c5ffcd31df3efd34b2695d67ef67fa1d Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:55:21 +0900 Subject: [PATCH 19/39] Delete file_finder_test.go --- .../file/file_finder_test.go | 136 ------------------ 1 file changed, 136 deletions(-) delete mode 100644 kadai2/src/tomoyukikobayashi/file/file_finder_test.go diff --git a/kadai2/src/tomoyukikobayashi/file/file_finder_test.go b/kadai2/src/tomoyukikobayashi/file/file_finder_test.go deleted file mode 100644 index e65f127..0000000 --- a/kadai2/src/tomoyukikobayashi/file/file_finder_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package file - -import ( - "os" - "reflect" - "testing" -) - -// fixture -var ( - dataRoot = "../testdata" - jpg, jpeg, png, txt = "file1.jpg", "file2.jpeg", "file3.png", "file4.txt" - tmpRoot = dataRoot + "/root" - imgDir, txtDir = tmpRoot + "/dir1/img", tmpRoot + "/dir2/txt" -) - -// TODO 愚直にOS使ったが、finderでIF差し替えられるようにしておいて仮想osにやらせてもいいかも -// setup / teardown用のヘルパー関数 -func testSetup(t *testing.T) func() { - // testdataの存在確認 - if _, err := os.Stat(dataRoot); err != nil { - t.Fatal(err) - } - - // 元になるテストデータの存在確認 - for _, v := range []string{jpg, jpeg, png, txt} { - if _, err := os.Stat(dataRoot + "/" + v); err != nil { - t.Fatal(err) - } - } - - // テストデータを格納するフォルダの作成 - for _, v := range []string{imgDir, txtDir} { - if err := os.MkdirAll(v, 0777); err != nil { - t.Fatal(err) - } - } - - // ファイルの作成 - for _, v := range []string{imgDir + "/" + - jpg, imgDir + "/" + jpeg, imgDir + "/" + png, txtDir + "/" + txt} { - file, err := os.OpenFile(v, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - t.Fatal(err) - } - defer func() { - if err := file.Close(); err != nil { - t.Error(err) - } - }() - } - - return func() { - if err := os.RemoveAll(tmpRoot); err != nil { - t.Error(err) - } - } -} - -func Test_Find(t *testing.T) { - // 指定した条件でフォルダとファイルを配置する - tearDown := testSetup(t) - - tests := []struct { - name string - dir string - exts []string - want []string - isErr bool - }{ - { - name: "findAll", - dir: tmpRoot, - exts: []string{"jpg", "jpeg", "png", "txt"}, - want: []string{imgDir + "/" + jpg, - imgDir + "/" + jpeg, imgDir + "/" + png, txtDir + "/" + txt}, - isErr: false, - }, - { - name: "notExistExt", - dir: tmpRoot, - exts: []string{"notFound"}, - want: []string{}, - isErr: false, - }, - { - name: "cannotOpenDir", - dir: "notExist", - exts: []string{"jpg"}, - want: nil, - isErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Find(tt.dir, tt.exts) - // TODO error型定義していないので有無の判断だけになっている - if tt.isErr { - if err == nil { - t.Fatal(err) - } - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Fatalf("want = %#v, got = %#v", tt.want, got) - } - }) - } - - // 事前条件で作った構成を削除 - tearDown() -} - -// TODO ザ・テストのためのテストって感じで微妙 -func Test_findpaths_errcase(t *testing.T) { - tearDown := testSetup(t) - - // 書き込みだけ許して、読み込めないdirを作成 - newDir := dataRoot + "/unReadable" - if err := os.Mkdir(newDir, 0333); err != nil { - t.Fatal(err) - } - defer func() { - if err := os.RemoveAll(newDir); err != nil { - t.Error(err) - } - }() - - got, err := Find(dataRoot, []string{"jpg"}) - if err == nil { - t.Fatalf("expected to get err, but got=%#v", got) - } - - tearDown() -} From bf5cb5e81d4b3057614371ebea034c87a52ca25d Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:55:29 +0900 Subject: [PATCH 20/39] Delete formats.go --- .../src/tomoyukikobayashi/imconv/formats.go | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 kadai2/src/tomoyukikobayashi/imconv/formats.go diff --git a/kadai2/src/tomoyukikobayashi/imconv/formats.go b/kadai2/src/tomoyukikobayashi/imconv/formats.go deleted file mode 100644 index b004f39..0000000 --- a/kadai2/src/tomoyukikobayashi/imconv/formats.go +++ /dev/null @@ -1,54 +0,0 @@ -package imconv - -import "strings" - -// HACK 本当はimmutableにしたいけどどうもできなそう -// ポインタだししょうがないか。。。厳密にやるなら直接触らせないのがいいかな -var ( - jpgExts = []string{"jpg", "jpeg"} - pngExts = []string{"png"} - supportExts = [][]string{ - jpgExts, - pngExts, - } - allSupportExts = append(jpgExts, pngExts...) -) - -// Supported returns if the package can handle speficied file format -func Supported(ext string) bool { - for _, spext := range allSupportExts { - if strings.ToLower(ext) == spext { - return true - } - } - return false -} - -// SupportedExtensions returns image formats the package can handle -func SupportedExtensions() []string { - clone := make([]string, len(allSupportExts)) - copy(clone, allSupportExts) - return clone -} - -// GetFormatThesaurus returns image formats that are the same with arg ext -func GetFormatThesaurus(ext string) []string { - for _, spexts := range supportExts { - for _, spext := range spexts { - if strings.ToLower(ext) == spext { - return spexts - } - } - } - return nil -} - -func isSameFormat(from string, to string) bool { - th := GetFormatThesaurus(from) - for _, v := range th { - if strings.ToLower(to) == v { - return true - } - } - return false -} From 26db9b93e5a2f087491845806886e43edfce37b6 Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:55:36 +0900 Subject: [PATCH 21/39] Delete formats_test.go --- .../tomoyukikobayashi/imconv/formats_test.go | 80 ------------------- 1 file changed, 80 deletions(-) delete mode 100644 kadai2/src/tomoyukikobayashi/imconv/formats_test.go diff --git a/kadai2/src/tomoyukikobayashi/imconv/formats_test.go b/kadai2/src/tomoyukikobayashi/imconv/formats_test.go deleted file mode 100644 index 81f7976..0000000 --- a/kadai2/src/tomoyukikobayashi/imconv/formats_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package imconv - -import ( - "reflect" - "testing" -) - -func Test_Supported(t *testing.T) { - tests := []struct { - ext string - want bool - }{ - {"jpg", true}, - {"jpeg", true}, - {"JPG", true}, - {"png", true}, - {"invalid", false}, - {"", false}, - } - - for _, tt := range tests { - t.Run(tt.ext, func(t *testing.T) { - if got := Supported(tt.ext); got != tt.want { - t.Fatalf("want = %#v, got = %#v", tt.want, got) - } - }) - } -} - -func Test_SupportedExtensions(t *testing.T) { - got := SupportedExtensions() - if len(got) != len(jpgExts)+len(pngExts) { - t.Fatalf("unexpected exts got = %#v", got) - } -} - -func Test_GetFormatThesaurus(t *testing.T) { - tests := []struct { - ext string - want []string - }{ - {"jpg", jpgExts}, - {"JPG", jpgExts}, - {"jpeg", jpgExts}, - {"png", pngExts}, - {"invalid", nil}, - {"", nil}, - } - - for _, tt := range tests { - t.Run(tt.ext, func(t *testing.T) { - if got := GetFormatThesaurus(tt.ext); !reflect.DeepEqual(got, tt.want) { - t.Fatalf("want = %#v, got = %#v", tt.want, got) - } - }) - } -} - -func Test_isSameFormat(t *testing.T) { - tests := []struct { - from string - to string - want bool - }{ - {"jpg", "jpeg", true}, - {"JPG", "jpeg", true}, - {"jpeg", "jpeg", true}, - {"JPG", "png", false}, - {"png", "invalid", false}, - {"invalid", "invalid", false}, - } - - for _, tt := range tests { - t.Run(tt.from+"_"+tt.to, func(t *testing.T) { - if got := isSameFormat(tt.from, tt.to); got != tt.want { - t.Fatalf("want = %#v, got = %#v", tt.want, got) - } - }) - } -} From 74dd45a1af555c0199885e3fd1efd854a9929172 Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:55:43 +0900 Subject: [PATCH 22/39] Delete image_converter.go --- .../imconv/image_converter.go | 80 ------------------- 1 file changed, 80 deletions(-) delete mode 100644 kadai2/src/tomoyukikobayashi/imconv/image_converter.go diff --git a/kadai2/src/tomoyukikobayashi/imconv/image_converter.go b/kadai2/src/tomoyukikobayashi/imconv/image_converter.go deleted file mode 100644 index c0afe00..0000000 --- a/kadai2/src/tomoyukikobayashi/imconv/image_converter.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Package imconv provides image convert mathods and -some validation logics, like if the package can handle -input format or not. -*/ -package imconv - -import ( - "fmt" - "image" - "image/jpeg" - "image/png" - "log" - "os" - "strings" -) - -// Convert convert image from from fromat to to fromat -func Convert(path string, from string, to string) (string, error) { - if !Supported(from) { - return "", fmt.Errorf("can not handle %s", from) - } - - if !Supported(to) { - return "", fmt.Errorf("can not handle %s", to) - } - - if isSameFormat(from, to) { - return "", fmt.Errorf("%s and %s are the same format", from, to) - } - - parts := strings.Split(path, ".") - newPath := strings.Join(parts[:len(parts)-1], ".") + "." + to - - src, err := os.Open(path) - if err != nil { - log.Print(err) - return "", fmt.Errorf("cannot open file %s", path) - } - defer func() { - if err := src.Close(); err != nil { - log.Fatal(err) - } - }() - - img, _, err := image.Decode(src) - if err != nil { - log.Print(err) - return "", fmt.Errorf("cannot decode file %s", path) - } - - dst, err := os.Create(newPath) - if err != nil { - log.Print(err) - return "", fmt.Errorf("cannot create file %s", newPath) - } - defer func() { - if err := dst.Close(); err != nil { - log.Print(err) - } - }() - - // HACK caseにベタがきしているが、formats定義を展開した方が良い - // case条件にsliceを展開した物を使えないのだろうか。。。 - switch strings.ToLower(to) { - case "jpg", "jpeg": - if err := jpeg.Encode(dst, img, nil); err != nil { - return "", err - } - case "png": - if err := png.Encode(dst, img); err != nil { - return "", err - } - default: - log.Print(err) - return "", fmt.Errorf("reached to invaid condition") - } - - return newPath, nil -} From c074309edbd896661d3eb5101c22437a1599c61e Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:55:50 +0900 Subject: [PATCH 23/39] Delete image_converter_test.go --- .../imconv/image_converter_test.go | 136 ------------------ 1 file changed, 136 deletions(-) delete mode 100644 kadai2/src/tomoyukikobayashi/imconv/image_converter_test.go diff --git a/kadai2/src/tomoyukikobayashi/imconv/image_converter_test.go b/kadai2/src/tomoyukikobayashi/imconv/image_converter_test.go deleted file mode 100644 index cf2a7d1..0000000 --- a/kadai2/src/tomoyukikobayashi/imconv/image_converter_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package imconv - -import ( - "os" - "strings" - "testing" -) - -// fixture -var ( - dataRoot = "../testdata" - jpgF, jpegF, pngF, txtF = "file1.jpg", "file2.jpeg", "file3.png", "file4.txt" - invalidJpgF = "invalid.jpg" -) - -// 前提ファイルがあるかチェック -func checkPrecondition(t *testing.T) { - // testdataの存在確認 - if _, err := os.Stat(dataRoot); err != nil { - t.Fatal(err) - } - - // 元になるテストデータの存在確認 - for _, v := range []string{jpgF, jpegF, pngF, txtF, invalidJpgF} { - if _, err := os.Stat(dataRoot + "/" + v); err != nil { - t.Fatal(err) - } - } - - return -} - -func Test_Find(t *testing.T) { - // 指定した条件でフォルダとファイルを配置する - checkPrecondition(t) - - tests := []struct { - name string - path string - from string - to string - want string - isErr bool - }{ - // TOOD 毎回全埋めすると長くなるので、composition使ってデフォルト値+上書きにしてしまうと短くできるかも? - { - name: "jpgToPng", - path: path(jpgF), - from: "jpg", - to: "png", - want: newPath(jpgF, "png"), - isErr: false, - }, - { - name: "pngToJpg", - path: path(pngF), - from: "png", - to: "jpg", - want: newPath(pngF, "jpg"), - isErr: false, - }, - { - name: "unSupportedFrom", - path: path(jpgF), - from: "INVALID", - to: "png", - want: "", - isErr: true, - }, - { - name: "unSupportedTo", - path: path(jpgF), - from: "jpg", - to: "INVALID", - want: "", - isErr: true, - }, - { - name: "fromAndToAreEquals", - path: path(jpgF), - from: "jpg", - to: "jpg", - want: "", - isErr: true, - }, - { - name: "failedToOpenFile", - path: "notExistFile.jpg", - from: "jpg", - to: "png", - want: "", - isErr: true, - }, - { - name: "failedToDecodeFile", - path: path(invalidJpgF), - from: "jpg", - to: "png", - want: "", - isErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Convert(tt.path, tt.from, tt.to) - // TODO error型定義していないので有無の判断だけになっている - // TOOD ここちょっと長いのでGiven When Then (TearDown)の見通しが悪くなっている - if tt.isErr { - if err == nil { - t.Fatal(err) - } - return - } - // 終わったらゴミ掃除 - defer func() { - if err := os.Remove(got); err != nil { - t.Error(err) - } - }() - if got != tt.want { - t.Fatalf("want = %#v, got = %#v", tt.want, got) - } - }) - } -} - -func path(file string) string { - return dataRoot + "/" + file -} - -func newPath(file string, to string) string { - parts := strings.Split(file, ".") - newPath := dataRoot + "/" + strings.Join(parts[:len(parts)-1], ".") + "." + to - return newPath -} From d8e828883b459b0694cbb65aef50b4fac031bf69 Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:55:56 +0900 Subject: [PATCH 24/39] Delete main.go --- kadai2/src/tomoyukikobayashi/main.go | 109 --------------------------- 1 file changed, 109 deletions(-) delete mode 100644 kadai2/src/tomoyukikobayashi/main.go diff --git a/kadai2/src/tomoyukikobayashi/main.go b/kadai2/src/tomoyukikobayashi/main.go deleted file mode 100644 index 16afb07..0000000 --- a/kadai2/src/tomoyukikobayashi/main.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Pacakge main is the entry point of this project. -This mainly provides interaction logics and parameters -used in CLI intrerfaces. -*/ -package main - -import ( - "flag" - "fmt" - "io" - "os" - - "tomoyukikobayashi/file" - "tomoyukikobayashi/imconv" -) - -// CLIのExitコード -const ( - ExitSuccess = 0 - ExitInvalidArgs = iota - ExitError = iota -) - -// Exitしてもテスト落ちない操作するようにエイリアスにしている -var exit = os.Exit - -// CLI テストしやすいようにCLIの出力先を差し替えられるようにしている -type CLI struct { - outStream io.Writer - errStream io.Writer -} - -// CLIツールのエントリーポイント -func main() { - cli := &CLI{outStream: os.Stdout, errStream: os.Stderr} - exit(cli.Run(os.Args)) -} - -// Run テスト用に実行ロジックを切り出した内容 -func (c *CLI) Run(args []string) int { - flags := flag.NewFlagSet("imconv", flag.ContinueOnError) - flags.SetOutput(c.errStream) - - tmpf, tmpt := "", "" - f, t := &tmpf, &tmpt - flags.StringVar(t, "f", "jpg", "input image file format") - flags.StringVar(f, "t", "png", "output image file format") - - flags.Usage = func() { - fmt.Fprintf(c.errStream, "Usage: %s [OPTIONS] dirname \n", os.Args[0]) - flags.PrintDefaults() - fmt.Fprintf(c.errStream, "Supported formats are %v \n", imconv.SupportedExtensions()) - } - - if err := flags.Parse(args[1:]); err != nil { - return ExitInvalidArgs - } - - dir := flags.Arg(0) - - // 引数のバリデーション - if len(dir) < 1 { - // stream設定しているおかげで、こいつも指定したstreamに書いているよう - flags.Usage() - return ExitInvalidArgs - } - - if !imconv.Supported(*f) { - fmt.Fprintf(c.errStream, "supported formats are %v \n", imconv.SupportedExtensions()) - return ExitInvalidArgs - } - - if !imconv.Supported(*t) { - fmt.Fprintf(c.errStream, "supported formats are %v \n", imconv.SupportedExtensions()) - return ExitInvalidArgs - } - - if *f == *t { - fmt.Fprintf(c.errStream, "input format and output format are the same \n") - return ExitInvalidArgs - } - - // 条件にマッチするファイルパスを検索 - paths, err := file.Find(dir, imconv.GetFormatThesaurus(*f)) - if err != nil { - fmt.Fprintf(c.errStream, "%v", err) - return ExitError - } - - if len(paths) < 1 { - fmt.Fprintf(c.outStream, "no files matched") - return ExitSuccess - } - - // 画像を変換する - // TOOD 画像が大量になる場合はgoroutineで並列処理しても良さそう - for _, path := range paths { - fmt.Fprintf(c.outStream, "src:"+path+"\n") - newPath, err := imconv.Convert(path, *f, *t) - if err != nil { - fmt.Fprintf(c.errStream, "%v", err) - return ExitError - } - fmt.Fprintf(c.outStream, "dst:"+newPath+"\n") - } - - return ExitSuccess -} From 06c57851135319ae48808804a6866521ff210b9e Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 09:56:03 +0900 Subject: [PATCH 25/39] Delete main_test.go --- kadai2/src/tomoyukikobayashi/main_test.go | 182 ---------------------- 1 file changed, 182 deletions(-) delete mode 100644 kadai2/src/tomoyukikobayashi/main_test.go diff --git a/kadai2/src/tomoyukikobayashi/main_test.go b/kadai2/src/tomoyukikobayashi/main_test.go deleted file mode 100644 index 0211754..0000000 --- a/kadai2/src/tomoyukikobayashi/main_test.go +++ /dev/null @@ -1,182 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "fmt" - "os" - "strings" - "testing" -) - -// fixture -var ( - dataRoot = "testdata" - emptyDir = "testdata/empty" -) - -type ExitCode int - -func (e ExitCode) Error() string { - return fmt.Sprintf("exit code: %d", int(e)) -} - -func init() { - // mainロジックの exit を上書き - exit = func(n int) { - panic(ExitCode(n)) - } -} - -// 前提ファイルがあるかチェック -func checkPrecondition(t *testing.T) func() { - // testdataの存在確認 - if _, err := os.Stat(dataRoot); err != nil { - t.Fatal(err) - } - - // 空のフォルダの作成 - if err := os.Mkdir(emptyDir, 0777); err != nil { - t.Fatal(err) - } - - return func() { - if err := os.RemoveAll(emptyDir); err != nil { - t.Error(err) - } - } -} - -// exitに差し込まれたpanic(ExitCode)の内容が意図した通りか検証 -func testExit(code int, f func()) (err error) { - defer func() { - // exitでわざとpanicさせてrecoverからエラーを取る - e := recover() - switch t := e.(type) { - case ExitCode: - if int(t) == code { - err = nil - } else { - err = fmt.Errorf("expected exit %v got %v", code, e) - } - default: - err = fmt.Errorf("expected exit %v got %v", code, e) - } - }() - - f() - - return errors.New("not exit") -} -func Test_main(t *testing.T) { - tests := []struct { - name string - args []string - want int - }{ - { - name: "invalidArgs", - args: []string{"main", "-i=invalid", "dir"}, - want: ExitInvalidArgs, - }, - { - name: "validArgs", - args: []string{"main", "-f=jpg", "dir"}, - want: ExitError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - os.Args = tt.args - err := testExit(tt.want, func() { - main() - }) - if err != nil { - t.Fatal(err) - } - }) - } -} - -func Test_Run(t *testing.T) { - tearDown := checkPrecondition(t) - - tests := []struct { - name string - args []string - wantCode int - wantTxt string - }{ - { - name: "invalidArgs", - args: []string{"main", "-i=invalid", "dir"}, - wantCode: ExitInvalidArgs, - wantTxt: "Usage", - }, - { - name: "dirNotSpecified", - args: []string{"main"}, - wantCode: ExitInvalidArgs, - wantTxt: "Usage", - }, - { - name: "unsupported-f", - args: []string{"main", "-f=unsp", "dir"}, - wantCode: ExitInvalidArgs, - wantTxt: "supported", - }, - { - name: "unsupported-t", - args: []string{"main", "-t=unsp", "dir"}, - wantCode: ExitInvalidArgs, - wantTxt: "supported", - }, - { - name: "same-f-t", - args: []string{"main", "-f=jpg", "-t=jpg", "dir"}, - wantCode: ExitInvalidArgs, - wantTxt: "same", - }, - { - name: "dirNotFount", - args: []string{"main", "notfound"}, - wantCode: ExitError, - wantTxt: "open", - }, - { - name: "fileNotFount", - args: []string{"main", "testdata/empty"}, - wantCode: ExitSuccess, - wantTxt: "no file", - }, - { - name: "valid", - args: []string{"main", "testdata"}, - wantCode: ExitSuccess, - wantTxt: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // CLIの書き込みストリームを渡す - outStream, errStream := new(bytes.Buffer), new(bytes.Buffer) - cli := &CLI{outStream: outStream, errStream: errStream} - - got := cli.Run(tt.args) - if got != tt.wantCode { - t.Errorf("Exitcode got=%d, want %d", got, tt.wantCode) - } - - if len(tt.wantTxt) > 1 { - if !strings.Contains(errStream.String(), tt.wantTxt) && - !strings.Contains(outStream.String(), tt.wantTxt) { - t.Errorf("Text got=%q, want %q", errStream.String(), tt.wantTxt) - } - } - }) - } - - tearDown() -} From 00001a7f37422304209f9b56c0cd6e98de6a0536 Mon Sep 17 00:00:00 2001 From: baki33 Date: Thu, 22 Nov 2018 11:24:26 +0900 Subject: [PATCH 26/39] Update Makefile --- kadai3-1/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/kadai3-1/Makefile b/kadai3-1/Makefile index 69fcbf4..0c454b8 100644 --- a/kadai3-1/Makefile +++ b/kadai3-1/Makefile @@ -1,7 +1,6 @@ .PHONY: deps deps: go get github.com/golang/lint/golint - go get github.com/ameske/wordnik-go .PHONY: build build: From 4f8b436cc23951f74a2e5263be4807d53e4e8e38 Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Thu, 22 Nov 2018 22:57:58 +0900 Subject: [PATCH 27/39] refactor game --- kadai3-1/src/tomoyukikobayashi/main.go | 22 +++++++-- kadai3-1/src/tomoyukikobayashi/typing/game.go | 47 +++++++++++-------- .../src/tomoyukikobayashi/typing/questions.go | 14 +++--- 3 files changed, 52 insertions(+), 31 deletions(-) diff --git a/kadai3-1/src/tomoyukikobayashi/main.go b/kadai3-1/src/tomoyukikobayashi/main.go index 8a5b956..9e34472 100644 --- a/kadai3-1/src/tomoyukikobayashi/main.go +++ b/kadai3-1/src/tomoyukikobayashi/main.go @@ -6,6 +6,7 @@ used in CLI intrerfaces. package main import ( + "flag" "fmt" "io" "os" @@ -15,8 +16,9 @@ import ( // CLIのExitコード const ( - ExitSuccess = 0 - ExitError = 1 + ExitSuccess = 0 + ExitError = 1 + ExitInvalidArgs = 2 ) // Exitしてもテスト落ちない操作するようにエイリアスにしている @@ -38,15 +40,25 @@ func main() { // Run テスト用に実行ロジックを切り出した内容 func (c *CLI) Run(args []string) int { - game, err := typing.NewGame() + flags := flag.NewFlagSet("typing", flag.ContinueOnError) + flags.SetOutput(c.errStream) + + var t int + flags.IntVar(&t, "t", 30, "time to play (second) default=30s") + + if err := flags.Parse(args[1:]); err != nil { + return ExitInvalidArgs + } + + game, err := typing.NewGame(t, c.inStream) if err != nil { fmt.Fprintf(c.outStream, "failed to initizalize game %v", err) return ExitError } // TOOD 長いことテスト固めたくないので外から与えるようにする - fmt.Fprintf(c.outStream, "start game 10 sec\n") - qCh, aCh, rCh := game.Run(10, c.inStream) + fmt.Fprintf(c.outStream, "start game %d sec\n", t) + qCh, aCh, rCh := game.Run() for { q, progress := <-qCh diff --git a/kadai3-1/src/tomoyukikobayashi/typing/game.go b/kadai3-1/src/tomoyukikobayashi/typing/game.go index be72eff..bcd8cae 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/game.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/game.go @@ -9,13 +9,14 @@ import ( // Game タイピングゲームを制御するロジックを返す type Game interface { - Run(int, io.Reader) (question <-chan string, - answer <-chan string, result <-chan [2]int) + Run() (question <-chan string, answer <-chan string, result <-chan [2]int) } type constGame struct { Questioner - // NOTE コンテキストに状態格納しようとしたがコピーして作り直すから、途中で書き換えてもルーチンをまたいでシェアされない模様 + input io.Reader + timeout int + // NOTE コンテキストにゲーム状態格納しようとしたがコピーして作り直すから、途中で書き換えてもルーチンをまたいでシェアされない(親子関係で) // 結局共有変数に書いていることになるので、脆弱感がすごい clear int miss int @@ -23,30 +24,33 @@ type constGame struct { } // NewGame Gameのコンストラクタです -func NewGame() (Game, error) { +func NewGame(timeout int, input io.Reader) (Game, error) { q, err := NewQuestioner() if err != nil { return nil, err } - return &constGame{Questioner: q}, nil + return &constGame{Questioner: q, timeout: timeout, input: input}, nil } // Run ゲームを開始する -func (c *constGame) Run(timeout int, input io.Reader) (<-chan string, - <-chan string, <-chan [2]int) { - sc := bufio.NewScanner(input) +func (c *constGame) Run() (<-chan string, <-chan string, <-chan [2]int) { + tc := time.Duration(c.timeout) * time.Second + // NOTE defer cancel() cancelするとDone条件閉じてゲーム終わっちゃう + ctx, _ := context.WithTimeout(context.Background(), tc) - qCh := make(chan string) - aCh := make(chan string) + // TODO routine数にあわせてサイズ調整は死ねるのでonce.DO が使えるかも routines := 2 rCh := make(chan [2]int, routines) - bc := context.Background() - tc := time.Duration(timeout) * time.Second - ctx, _ := context.WithTimeout(bc, tc) - // NOTE defer cancel() defer cancelするとDone条件閉じてる + qCh := c.question(ctx, rCh) + aCh := c.answer(ctx, rCh) + + return qCh, aCh, rCh +} - // TODO 読みづらいので関数に切り出す +// 問題をqChに送る +func (c *constGame) question(ctx context.Context, rCh chan<- [2]int) <-chan string { + qCh := make(chan string) go func() { for { word := c.GetNewWord(c.nextLevel()) @@ -54,7 +58,6 @@ func (c *constGame) Run(timeout int, input io.Reader) (<-chan string, c.currentWord = word select { case <-ctx.Done(): - // TODO once.DO が使えるかも rCh <- [2]int{c.clear, c.miss} close(qCh) return @@ -63,8 +66,13 @@ func (c *constGame) Run(timeout int, input io.Reader) (<-chan string, } } }() + return qCh +} - // 読み込みしてaChに送る +// 回答をストリームから読み込みしてaChに送る +func (c *constGame) answer(ctx context.Context, rCh chan<- [2]int) <-chan string { + sc := bufio.NewScanner(c.input) + aCh := make(chan string) go func() { for { if sc.Scan() { @@ -78,6 +86,7 @@ func (c *constGame) Run(timeout int, input io.Reader) (<-chan string, } select { + // contextがtimeoutしたら結果を返却 case <-ctx.Done(): rCh <- [2]int{c.clear, c.miss} close(aCh) @@ -87,8 +96,7 @@ func (c *constGame) Run(timeout int, input io.Reader) (<-chan string, } } }() - - return qCh, aCh, rCh + return aCh } // HACK 成功した回数に応じて、使う語彙のレベルを決める。ここは決め打ちで書いてる @@ -102,6 +110,7 @@ func (c *constGame) nextLevel() int { return 3 } +// ワードの比較 func (c *constGame) isCorrect(word string) bool { return c.currentWord == word } diff --git a/kadai3-1/src/tomoyukikobayashi/typing/questions.go b/kadai3-1/src/tomoyukikobayashi/typing/questions.go index c1a303c..ebef432 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/questions.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/questions.go @@ -47,8 +47,8 @@ func (q *constQuestioner) GetNewWord(level int) string { return q.qs[level][rand] } -// Yaml HACK 使用しているパッケージの使用でしょうがなくpublicにしている -type Yaml struct { +// QuizSource HACK 使用しているパッケージの使用でしょうがなくpublicにしている +type QuizSource struct { Level1 []string `yaml:"Level1,flow"` Level2 []string `yaml:"Level2,flow"` Level3 []string `yaml:"Level3,flow"` @@ -63,17 +63,17 @@ func (q *constQuestioner) loadWords() error { return err } - var y Yaml - err = yamler.Unmarshal([]byte(data), &y) + var s QuizSource + err = yamler.Unmarshal([]byte(data), &s) if err != nil { return err } // TODO レベルは今の所少ないのでとりあえずベタがき // yamlの構成工夫 or interfaceとしてレベル別に取る関数生やして動的に撮れる方が良い - q.qs[1] = y.Level1 - q.qs[2] = y.Level2 - q.qs[3] = y.Level3 + q.qs[1] = s.Level1 + q.qs[2] = s.Level2 + q.qs[3] = s.Level3 return nil } From 89975a8bad4444e33ed46e5297e24dd57d95e58f Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Fri, 23 Nov 2018 02:32:41 +0900 Subject: [PATCH 28/39] =?UTF-8?q?=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF?= =?UTF-8?q?=E3=82=BF=E3=83=AA=E3=83=B3=E3=82=B0=E3=81=A8=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai3-1/README.md | 20 +++--- kadai3-1/src/tomoyukikobayashi/main.go | 17 +++++- kadai3-1/src/tomoyukikobayashi/typing/game.go | 16 ++--- .../src/tomoyukikobayashi/typing/questions.go | 61 +++---------------- 4 files changed, 46 insertions(+), 68 deletions(-) diff --git a/kadai3-1/README.md b/kadai3-1/README.md index edb5d9d..1dbd149 100644 --- a/kadai3-1/README.md +++ b/kadai3-1/README.md @@ -1,19 +1,25 @@ -GoImageConverter +GoTypingGame ===== # Overview -TODO +最高にいけてる楽しいタイピングゲーム +Goで書かれている # SetUp -下記のようにコマンドを叩くと、実行形式のTODOファイルが生成されます +下記のようにコマンドを叩くと、実行形式のtypeファイルが生成されます ``` -cd kadai2 make build ``` # Usage - - -# 課題に対するアプローチ +``` +type [OPTION] +``` +オプション +``` +Usage of typing: + -t int + time to play (second) default=30s (default 30) +``` diff --git a/kadai3-1/src/tomoyukikobayashi/main.go b/kadai3-1/src/tomoyukikobayashi/main.go index 9e34472..840a5da 100644 --- a/kadai3-1/src/tomoyukikobayashi/main.go +++ b/kadai3-1/src/tomoyukikobayashi/main.go @@ -10,10 +10,15 @@ import ( "fmt" "io" "os" + "path/filepath" "tomoyukikobayashi/typing" ) +const ( + wordFile = "words.yaml" +) + // CLIのExitコード const ( ExitSuccess = 0 @@ -50,13 +55,21 @@ func (c *CLI) Run(args []string) int { return ExitInvalidArgs } - game, err := typing.NewGame(t, c.inStream) + // yamlファイルから語彙リストを読み出す + cur, _ := os.Getwd() + file, err := os.Open(filepath.Join(cur, wordFile)) + if err != nil { + fmt.Fprintf(c.outStream, "failed to initizalize game %v", err) + return ExitError + } + + // gameを動作させるインターフェイスを初期化 + game, err := typing.NewGame(t, file, c.inStream) if err != nil { fmt.Fprintf(c.outStream, "failed to initizalize game %v", err) return ExitError } - // TOOD 長いことテスト固めたくないので外から与えるようにする fmt.Fprintf(c.outStream, "start game %d sec\n", t) qCh, aCh, rCh := game.Run() diff --git a/kadai3-1/src/tomoyukikobayashi/typing/game.go b/kadai3-1/src/tomoyukikobayashi/typing/game.go index bcd8cae..0ce53dc 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/game.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/game.go @@ -24,11 +24,13 @@ type constGame struct { } // NewGame Gameのコンストラクタです -func NewGame(timeout int, input io.Reader) (Game, error) { - q, err := NewQuestioner() +func NewGame(timeout int, source io.Reader, input io.Reader) (Game, error) { + // クイズデータを読み込む + d, err := NewQuizData(source) if err != nil { return nil, err } + q := NewQuestioner(d) return &constGame{Questioner: q, timeout: timeout, input: input}, nil } @@ -99,6 +101,11 @@ func (c *constGame) answer(ctx context.Context, rCh chan<- [2]int) <-chan string return aCh } +// ワードの比較 +func (c *constGame) isCorrect(word string) bool { + return c.currentWord == word +} + // HACK 成功した回数に応じて、使う語彙のレベルを決める。ここは決め打ちで書いてる func (c *constGame) nextLevel() int { if c.clear < 10 { @@ -109,8 +116,3 @@ func (c *constGame) nextLevel() int { } return 3 } - -// ワードの比較 -func (c *constGame) isCorrect(word string) bool { - return c.currentWord == word -} diff --git a/kadai3-1/src/tomoyukikobayashi/typing/questions.go b/kadai3-1/src/tomoyukikobayashi/typing/questions.go index ebef432..34239db 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/questions.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/questions.go @@ -2,17 +2,8 @@ package typing import ( - "io/ioutil" "math/rand" - "os" - "path/filepath" "time" - - yamler "gopkg.in/yaml.v2" -) - -const ( - wordFile = "words.yaml" ) // Questioner 質問で使うワードを提供します @@ -26,54 +17,20 @@ type constQuestioner struct { } // NewQuestioner Questionerのコンストラクタ -func NewQuestioner() (Questioner, error) { - q := &constQuestioner{ - qs: map[int][]string{}, +func NewQuestioner(data QuizData) Questioner { + qs := map[int][]string{} + for i := 1; i <= data.MaxLevel(); i++ { + qs[i] = data.WordsByLevel(i) } - if err := q.loadWords(); err != nil { - return nil, err + q := &constQuestioner{ + qs: qs, } - return q, nil -} - -func random(min int, max int) int { - rand.Seed(time.Now().UnixNano()) - return rand.Intn(max-min) + min + return q } func (q *constQuestioner) GetNewWord(level int) string { - rand := random(1, len(q.qs[level])) + rand.Seed(time.Now().UnixNano()) + rand := rand.Intn(len(q.qs[level])) // HACK ほんとはmap okを見た方がいいけど、省略 return q.qs[level][rand] } - -// QuizSource HACK 使用しているパッケージの使用でしょうがなくpublicにしている -type QuizSource struct { - Level1 []string `yaml:"Level1,flow"` - Level2 []string `yaml:"Level2,flow"` - Level3 []string `yaml:"Level3,flow"` -} - -// TOOD NewGameでinterfaceとio.Reader渡してやる方が、Questionerが汎用になる -func (q *constQuestioner) loadWords() error { - cur, _ := os.Getwd() - // yamlファイルから語彙リストを読み出す - data, err := ioutil.ReadFile(filepath.Join(cur, wordFile)) - if err != nil { - return err - } - - var s QuizSource - err = yamler.Unmarshal([]byte(data), &s) - if err != nil { - return err - } - - // TODO レベルは今の所少ないのでとりあえずベタがき - // yamlの構成工夫 or interfaceとしてレベル別に取る関数生やして動的に撮れる方が良い - q.qs[1] = s.Level1 - q.qs[2] = s.Level2 - q.qs[3] = s.Level3 - - return nil -} From 4b10c87d84335371c2c391d6449c1e7318af2a69 Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Fri, 23 Nov 2018 02:36:01 +0900 Subject: [PATCH 29/39] rename --- kadai3-1/src/tomoyukikobayashi/typing/questions.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kadai3-1/src/tomoyukikobayashi/typing/questions.go b/kadai3-1/src/tomoyukikobayashi/typing/questions.go index 34239db..0a5f7e7 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/questions.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/questions.go @@ -12,7 +12,7 @@ type Questioner interface { GetNewWord(level int) string } -type constQuestioner struct { +type questionContainer struct { qs map[int][]string } @@ -22,13 +22,13 @@ func NewQuestioner(data QuizData) Questioner { for i := 1; i <= data.MaxLevel(); i++ { qs[i] = data.WordsByLevel(i) } - q := &constQuestioner{ + q := &questionContainer{ qs: qs, } return q } -func (q *constQuestioner) GetNewWord(level int) string { +func (q *questionContainer) GetNewWord(level int) string { rand.Seed(time.Now().UnixNano()) rand := rand.Intn(len(q.qs[level])) // HACK ほんとはmap okを見た方がいいけど、省略 From 3dbe9fb8da887a37d2696fcd2cf624f6d2a0d965 Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Fri, 23 Nov 2018 02:38:32 +0900 Subject: [PATCH 30/39] add test --- .../src/tomoyukikobayashi/typing/game_test.go | 116 +++++++++++++++++ .../typing/questions_test.go | 57 ++++++++ .../src/tomoyukikobayashi/typing/quiz_data.go | 57 ++++++++ .../typing/quiz_data_test.go | 123 ++++++++++++++++++ 4 files changed, 353 insertions(+) create mode 100644 kadai3-1/src/tomoyukikobayashi/typing/game_test.go create mode 100644 kadai3-1/src/tomoyukikobayashi/typing/questions_test.go create mode 100644 kadai3-1/src/tomoyukikobayashi/typing/quiz_data.go create mode 100644 kadai3-1/src/tomoyukikobayashi/typing/quiz_data_test.go diff --git a/kadai3-1/src/tomoyukikobayashi/typing/game_test.go b/kadai3-1/src/tomoyukikobayashi/typing/game_test.go new file mode 100644 index 0000000..c276853 --- /dev/null +++ b/kadai3-1/src/tomoyukikobayashi/typing/game_test.go @@ -0,0 +1,116 @@ +package typing + +import ( + "io" + "os" + "strings" + "testing" +) + +func Test_NewGame(t *testing.T) { + tests := []struct { + name string + source io.Reader + hasErr bool + }{ + { + name: "valid", + source: strings.NewReader(` +Level1: +- hoge + +Level2: +- difficult + +Level3: +- test`), + hasErr: false, + }, + { + name: "invalid", + source: strings.NewReader(``), + hasErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := NewGame(1, tt.source, os.Stdin) + if err != nil && !tt.hasErr { + t.Fatalf("got err = %#v", err) + } + }) + } +} + +func Test_Run(t *testing.T) { + tests := []struct { + name string + source io.Reader + input io.Reader + hasErr bool + }{ + { + name: "valid", + source: strings.NewReader(` +Level1: +- hoge + +Level2: +- difficult + +Level3: +- testtestsete +`), + input: strings.NewReader(`hoge +hoge +hoge +hoge +hoge +hoge +hoge +hoge +miss +hoge +miss +hoge +difficult +difficult +`), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + game, err := NewGame(1, tt.source, tt.input) + if err != nil { + t.Fatalf("initialize game failed %v", game) + } + + var want [2]int + qCh, aCh, rCh := game.Run() + for { + q := <-qCh + if q == "" { + break + } + a := <-aCh + if a == "" { + break + } + + // TOOD 値が微妙に揺れる。。。テスト壊れる + if q == a { + want[0] = want[0] + 1 + } else { + want[1] = want[1] + 1 + } + } + + got := <-rCh + if want != got { + t.Fatalf("failed got = %v, want = %v", got, want) + } + }) + } +} diff --git a/kadai3-1/src/tomoyukikobayashi/typing/questions_test.go b/kadai3-1/src/tomoyukikobayashi/typing/questions_test.go new file mode 100644 index 0000000..e685c8b --- /dev/null +++ b/kadai3-1/src/tomoyukikobayashi/typing/questions_test.go @@ -0,0 +1,57 @@ +package typing + +import ( + "testing" +) + +type testData struct { +} + +func (td *testData) MaxLevel() int { + return 3 +} +func (td *testData) WordsByLevel(level int) []string { + switch level { + case 1: + return []string{"hoge", "foo", "baz"} + case 2: + return []string{"difficult", "anymatch", "haeeee"} + case 3: + return []string{"test"} + } + return []string{} +} + +func Test_GetNewWord(t *testing.T) { + q := testData{} + qst := NewQuestioner(&q) + + tests := []struct { + name string + level int + wantIn []string + }{ + { + name: "level1", + level: 1, + wantIn: q.WordsByLevel(1), + }, + { + name: "level2", + level: 2, + wantIn: q.WordsByLevel(2), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := qst.GetNewWord(tt.level) + for _, v := range tt.wantIn { + if got == v { + return + } + } + t.Fatalf("want = %#v, got = %#v", tt.wantIn, got) + }) + } +} diff --git a/kadai3-1/src/tomoyukikobayashi/typing/quiz_data.go b/kadai3-1/src/tomoyukikobayashi/typing/quiz_data.go new file mode 100644 index 0000000..9f3c4bb --- /dev/null +++ b/kadai3-1/src/tomoyukikobayashi/typing/quiz_data.go @@ -0,0 +1,57 @@ +package typing + +import ( + "io" + "io/ioutil" + + yamler "gopkg.in/yaml.v2" +) + +// QuizData クイズデータを読み出す +type QuizData interface { + MaxLevel() int + WordsByLevel(int) []string +} + +// QuizSource HACK 使用しているパッケージの使用でしょうがなくpublicにしている +type QuizSource struct { + Level1 []string `yaml:"Level1,flow"` + Level2 []string `yaml:"Level2,flow"` + Level3 []string `yaml:"Level3,flow"` +} + +// NewQuizData クイズデータを生成する +func NewQuizData(source io.Reader) (QuizData, error) { + data, err := ioutil.ReadAll(source) + if err != nil { + return nil, err + } + + var s QuizSource + err = yamler.Unmarshal([]byte(data), &s) + if err != nil { + return nil, err + } + + return &s, nil +} + +// MaxLevel クイズの最高難易度を返す +func (q *QuizSource) MaxLevel() int { + // HACK 決め打ち + return 3 +} + +// WordsByLevel 指定されたレベルの語彙を返す +func (q *QuizSource) WordsByLevel(level int) []string { + // HACK + switch level { + case 1: + return q.Level1 + case 2: + return q.Level2 + case 3: + return q.Level3 + } + return nil +} diff --git a/kadai3-1/src/tomoyukikobayashi/typing/quiz_data_test.go b/kadai3-1/src/tomoyukikobayashi/typing/quiz_data_test.go new file mode 100644 index 0000000..0e370d4 --- /dev/null +++ b/kadai3-1/src/tomoyukikobayashi/typing/quiz_data_test.go @@ -0,0 +1,123 @@ +package typing + +import ( + "io" + "reflect" + "strings" + "testing" +) + +func Test_NewQuizData(t *testing.T) { + tests := []struct { + name string + reader io.Reader + want QuizSource + hasErr bool + }{ + { + name: "valid", + reader: strings.NewReader(` +Level1: +- hoge + +Level2: +- difficult + +Level3: +- test`), + want: QuizSource{ + Level1: []string{"hoge"}, + Level2: []string{"difficult"}, + Level3: []string{"test"}, + }, + hasErr: false, + }, + { + name: "invalidFormat", + reader: strings.NewReader(``), + want: QuizSource{}, + hasErr: true, + }, + { + name: "invalidStruct", + reader: strings.NewReader(` +LevelA: +- hoge +`), + want: QuizSource{}, + hasErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewQuizData(tt.reader) + if err != nil && !tt.hasErr { + t.Fatalf("got err = %#v", err) + } + if !reflect.DeepEqual(got, &tt.want) { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } +} + +func Test_MaxLevel(t *testing.T) { + q := QuizSource{ + Level1: []string{"hoge"}, + Level2: []string{"difficult"}, + Level3: []string{"test"}, + } + + // HACK 決め打ち + want := 3 + got := q.MaxLevel() + if want != got { + t.Fatalf("want = %d, got = %d", want, got) + } + +} + +func Test_WordsByLevel(t *testing.T) { + q := QuizSource{ + Level1: []string{"hoge"}, + Level2: []string{"difficult"}, + Level3: []string{"test"}, + } + + tests := []struct { + name string + level int + want []string + }{ + { + name: "level1", + level: 1, + want: []string{"hoge"}, + }, + { + name: "level2", + level: 2, + want: []string{"difficult"}, + }, + { + name: "level3", + level: 3, + want: []string{"test"}, + }, + { + name: "outOfRange", + level: 4, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := q.WordsByLevel(tt.level) + if !reflect.DeepEqual(got, tt.want) { + t.Fatalf("want = %#v, got = %#v", tt.want, got) + } + }) + } +} From 5decc01ddc0a9413d61836a18c7ce30af3d3a757 Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Fri, 23 Nov 2018 23:52:13 +0900 Subject: [PATCH 31/39] =?UTF-8?q?range=E3=81=AE=E5=8B=95=E4=BD=9C=E7=A2=BA?= =?UTF-8?q?=E8=AA=8D=E3=81=8C=E3=81=A7=E3=81=8D=E3=81=A6=E3=82=8B=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai3-2/Makefile | 26 ++++++ kadai3-2/README.md | 19 ++++ kadai3-2/src/tomoyukikobayashi/main.go | 122 +++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 kadai3-2/Makefile create mode 100644 kadai3-2/README.md create mode 100644 kadai3-2/src/tomoyukikobayashi/main.go diff --git a/kadai3-2/Makefile b/kadai3-2/Makefile new file mode 100644 index 0000000..163737a --- /dev/null +++ b/kadai3-2/Makefile @@ -0,0 +1,26 @@ +.PHONY: deps +deps: + go get golang.org/x/sync/errgourp + go get github.com/golang/lint/golint + +.PHONY: build +build: + go build -o codehexisgod tomoyukikobayashi + +.PHONY: test +test: + go test -v -cover ./... + +.PHONY: cover +cover: + go test -coverprofile=mainprof tomoyukikobayashi + go tool cover -html=mainprof + +.PHONY: lint +lint: deps + go vet ./... + golint ./... + +.PHONY: fmt +fmt: deps + go fmt *.go diff --git a/kadai3-2/README.md b/kadai3-2/README.md new file mode 100644 index 0000000..c83f9fa --- /dev/null +++ b/kadai3-2/README.md @@ -0,0 +1,19 @@ +CodeHexIsGod +===== + +# Overview + +CodeHexさんくん +すごいやという気持ちで写経したやつ + +# SetUp + +下記のようにコマンドを叩くと、実行形式のtypeファイルが生成されます +``` +make build +``` + +# Usage +``` +type [OPTION] URL +``` diff --git a/kadai3-2/src/tomoyukikobayashi/main.go b/kadai3-2/src/tomoyukikobayashi/main.go new file mode 100644 index 0000000..a14ebb9 --- /dev/null +++ b/kadai3-2/src/tomoyukikobayashi/main.go @@ -0,0 +1,122 @@ +/* +Pacakge main is the entry point of this project. +This mainly provides interaction logics and parameters +used in CLI intrerfaces. +*/ +package main + +import ( + "crypto/sha1" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "strconv" +) + +// CLIのExitコード +const ( + ExitSuccess = 0 + ExitError = 1 + ExitInvalidArgs = 2 +) + +// HACK 雑に作ったので弱いバリデーション、パラメタ決め打ちとか、処理されないerrがある +// mainに全部押し込んで、テストもない + +// CLIツールのエントリーポイント +func main() { + if len(os.Args) < 2 { + fmt.Printf("missing URL parameter") + os.Exit(ExitInvalidArgs) + } + + url := os.Args[1] + fmt.Printf("start to download %v\n", url) + + // HEADで取れた情報から、対象のAccept-Ranges対応確認と、ファイルサイズの取得 + hRes, err := http.Head(url) + if err != nil { + fmt.Printf("cannot get header %v from %v\n", err, url) + os.Exit(ExitError) + } + if _, ok := hRes.Header["Accept-Ranges"]; !ok { + fmt.Printf("range access does not supported") + os.Exit(ExitError) + } + // HACK とりあえず決めうち、本当は環境or引数からProc数決めるのが良い + procs := 3 + size, _ := strconv.Atoi(hRes.Header["Content-Length"][0]) + fmt.Printf("donwload size %v, parallel %v", size, procs) + + // 一時ファイルを格納するディレクトリ名(とりあえずハッシュ)を決める + h := sha1.Sum([]byte(url)) + hash := fmt.Sprintf("%x", h) + + // DL共通のtmpディレクトリと、個別のDL用のhash名のディレクトリを掘る + cur, _ := os.Getwd() + // tmpフォルダがなければ作る + if _, err := os.Stat(filepath.Join(cur, "tmp")); os.IsNotExist(err) { + err := os.Mkdir("tmp", 0777) + if err != nil { + fmt.Printf("failed to create tmp file dir") + os.Exit(ExitError) + } + } + // tmp配下にハッシュにマッチするフォルダがなければ作る + dlTmp := filepath.Join("tmp", hash) + if _, err := os.Stat(filepath.Join(cur, dlTmp)); os.IsNotExist(err) { + err := os.Mkdir(dlTmp, 0777) + if err != nil { + fmt.Printf("failed to create tmp file dir") + os.Exit(ExitError) + } + } + + // goroutine起こして並列ダウンロード + // bytes のレンジに指定するバイト位置 + from, to := 0, 0 + for i := 0; i < procs; i++ { + // 各分割リクエストの取得バイト範囲を決める + to = from + size/procs + if i == procs-1 { + to = to + size%procs + } + + // TODO ファイル存在確認して、いたらDLしない + // TOOD waitして最後にファイルを結合 + // TOOD erros使う + dReq, _ := http.NewRequest("GET", url, nil) + dReq.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", from, to)) + dRes, _ := http.DefaultClient.Do(dReq) + buf, _ := ioutil.ReadAll(dRes.Body) + // TODO 存在とファイルサイズ確認 tmpフォルダ作成 + fn := filepath.Join(dlTmp, strconv.FormatInt(int64(i), 10)) + file, _ := os.Create(fn) + defer file.Close() + + _, _ = file.Write(buf) + + from = to + 1 + } + + // DLした部分ファイルを結合する + files := make([]io.Reader, procs) + for i := 0; i < procs; i++ { + file, _ := os.Open(filepath.Join(dlTmp, strconv.FormatInt(int64(i), 10))) + files[i] = file + } + + reader := io.MultiReader(files...) + // TOOD urlから作る + file, _ := os.Create("out.jpg") + defer file.Close() + b, _ := ioutil.ReadAll(reader) + _, _ = file.Write(b) + + // TODO tmpファイルの掃除 + + os.Exit(ExitSuccess) +} From 1a4401c8429861e62a4fe656d78766e8754358d3 Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Sat, 24 Nov 2018 09:04:47 +0900 Subject: [PATCH 32/39] =?UTF-8?q?range=E3=83=80=E3=82=A6=E3=83=B3=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=81=A7=E3=81=8D=E3=82=8B=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai3-2/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kadai3-2/README.md b/kadai3-2/README.md index c83f9fa..e3f4fbf 100644 --- a/kadai3-2/README.md +++ b/kadai3-2/README.md @@ -3,8 +3,7 @@ CodeHexIsGod # Overview -CodeHexさんくん -すごいやという気持ちで写経したやつ +CodeHex is God # SetUp @@ -15,5 +14,5 @@ make build # Usage ``` -type [OPTION] URL +codehexisgod [OPTION] URL ``` From d4abbe765cf668e241b90ce14f4738c66a5fac8f Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Sat, 24 Nov 2018 23:05:47 +0900 Subject: [PATCH 33/39] work version --- kadai3-2/src/tomoyukikobayashi/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kadai3-2/src/tomoyukikobayashi/main.go b/kadai3-2/src/tomoyukikobayashi/main.go index a14ebb9..3b61533 100644 --- a/kadai3-2/src/tomoyukikobayashi/main.go +++ b/kadai3-2/src/tomoyukikobayashi/main.go @@ -75,7 +75,8 @@ func main() { } } - // goroutine起こして並列ダウンロード + /// goroutine起こして並列ダウンロード + var eg errgroup.Group // bytes のレンジに指定するバイト位置 from, to := 0, 0 for i := 0; i < procs; i++ { From ce761c145ee469069bc129fdd57c6c2185d3d594 Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Sat, 24 Nov 2018 23:07:57 +0900 Subject: [PATCH 34/39] work version --- kadai3-2/README.md | 3 +- kadai3-2/src/tomoyukikobayashi/main.go | 121 ++++++++++++++++++++----- 2 files changed, 99 insertions(+), 25 deletions(-) diff --git a/kadai3-2/README.md b/kadai3-2/README.md index e3f4fbf..c28c5e2 100644 --- a/kadai3-2/README.md +++ b/kadai3-2/README.md @@ -7,12 +7,13 @@ CodeHex is God # SetUp -下記のようにコマンドを叩くと、実行形式のtypeファイルが生成されます +下記のようにコマンドを叩くと、実行形式のgodehexisgodファイルが生成されます ``` make build ``` # Usage +※Rangeダウンロードをサポートしているサイトに対してのみ、機能します ``` codehexisgod [OPTION] URL ``` diff --git a/kadai3-2/src/tomoyukikobayashi/main.go b/kadai3-2/src/tomoyukikobayashi/main.go index 3b61533..7a09876 100644 --- a/kadai3-2/src/tomoyukikobayashi/main.go +++ b/kadai3-2/src/tomoyukikobayashi/main.go @@ -14,6 +14,8 @@ import ( "os" "path/filepath" "strconv" + + "golang.org/x/sync/errgroup" ) // CLIのExitコード @@ -23,7 +25,7 @@ const ( ExitInvalidArgs = 2 ) -// HACK 雑に作ったので弱いバリデーション、パラメタ決め打ちとか、処理されないerrがある +// HACK 雑に作ったので弱いバリデーション、パラメタ決め打ちと、処理されないエラーがある // mainに全部押し込んで、テストもない // CLIツールのエントリーポイント @@ -36,6 +38,7 @@ func main() { url := os.Args[1] fmt.Printf("start to download %v\n", url) + /// Rangeアクセスの準備 // HEADで取れた情報から、対象のAccept-Ranges対応確認と、ファイルサイズの取得 hRes, err := http.Head(url) if err != nil { @@ -46,11 +49,15 @@ func main() { fmt.Printf("range access does not supported") os.Exit(ExitError) } + + /// 処理するプロセス数を決める // HACK とりあえず決めうち、本当は環境or引数からProc数決めるのが良い - procs := 3 + procs := 4 size, _ := strconv.Atoi(hRes.Header["Content-Length"][0]) fmt.Printf("donwload size %v, parallel %v", size, procs) + /// 一次ファイルを格納するフォルダの作成 + // 一時ファイルを格納するディレクトリ名(とりあえずハッシュ)を決める h := sha1.Sum([]byte(url)) hash := fmt.Sprintf("%x", h) @@ -86,38 +93,104 @@ func main() { to = to + size%procs } - // TODO ファイル存在確認して、いたらDLしない - // TOOD waitして最後にファイルを結合 - // TOOD erros使う - dReq, _ := http.NewRequest("GET", url, nil) - dReq.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", from, to)) - dRes, _ := http.DefaultClient.Do(dReq) - buf, _ := ioutil.ReadAll(dRes.Body) - // TODO 存在とファイルサイズ確認 tmpフォルダ作成 - fn := filepath.Join(dlTmp, strconv.FormatInt(int64(i), 10)) - file, _ := os.Create(fn) - defer file.Close() - - _, _ = file.Write(buf) + // 平行にDLをする + i := i + 1 + eg.Go(func() error { + fn := filepath.Join(dlTmp, strconv.FormatInt(int64(i), 10)) + + // ファイルが存在していたらスキップする + // TODO 本当はファイルサイズがto - fromと一致するかも見た方が良い + if _, err := os.Stat(fn); err == nil { + return nil + } + + // Rangeを指定してGETリクエスト作成 + dReq, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + dReq.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", from, to)) + + // DLリクエスト + dRes, err := http.DefaultClient.Do(dReq) + if err != nil { + return err + } + + // 取得したデータをBufに読み出し + buf, err := ioutil.ReadAll(dRes.Body) + if err != nil { + return err + } + + // 分割ファイルの保存 + file, err := os.Create(fn) + if err != nil { + return err + } + defer file.Close() + + // TODO ここで落ちるとファイルが存在するけどDLできていないのにスキップしてしまう + _, err = file.Write(buf) + if err != nil { + return err + } + + return nil + }) from = to + 1 } + // 全プロセスがDL終わるまで待つ + if err := eg.Wait(); err != nil { + fmt.Printf("error occurred while downloading %v", err) + os.Exit(ExitError) + } - // DLした部分ファイルを結合する + /// DLした部分ファイルを結合する + + // 複数ファイルを一気に読み出すReaderを作成 files := make([]io.Reader, procs) for i := 0; i < procs; i++ { - file, _ := os.Open(filepath.Join(dlTmp, strconv.FormatInt(int64(i), 10))) + file, err := os.Open(filepath.Join(dlTmp, strconv.FormatInt(int64(i+1), 10))) + if err != nil { + fmt.Printf("error occurred while reading tmp file %v", err) + os.Exit(ExitError) + } files[i] = file } - reader := io.MultiReader(files...) - // TOOD urlから作る - file, _ := os.Create("out.jpg") - defer file.Close() - b, _ := ioutil.ReadAll(reader) - _, _ = file.Write(b) + b, err := ioutil.ReadAll(reader) + if err != nil { + fmt.Printf("error occurred while creating dl file %v", err) + os.Exit(ExitError) + } + + // deferをos.Existと同じブロックに置かないように無名関数にしている + // (defer使わないでシーケンシャルにやるのと変わらんなこれだと) + err = func() error { + /// tmpフォルダのゴミ掃除(失敗しても特に何もしない)) + defer os.RemoveAll(dlTmp) - // TODO tmpファイルの掃除 + // ファイルを結合する + file, err := os.Create(filepath.Base(url)) + if err != nil { + return err + } + defer file.Close() + + _, err = file.Write(b) + if err != nil { + return err + } + + return nil + }() + + if err != nil { + fmt.Printf("error occurred while creating dl file %v", err) + os.Exit(ExitError) + } os.Exit(ExitSuccess) } From 7a6cd63b49d402ac60a99069e826f34aa883430a Mon Sep 17 00:00:00 2001 From: baki33 Date: Sat, 24 Nov 2018 23:51:27 +0900 Subject: [PATCH 35/39] Delete file4.txt --- kadai2/src/tomoyukikobayashi/testdata/file4.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 kadai2/src/tomoyukikobayashi/testdata/file4.txt diff --git a/kadai2/src/tomoyukikobayashi/testdata/file4.txt b/kadai2/src/tomoyukikobayashi/testdata/file4.txt deleted file mode 100755 index e69de29..0000000 From 28b3f2268545db6d92e5853880954052954c159d Mon Sep 17 00:00:00 2001 From: baki33 Date: Sat, 24 Nov 2018 23:51:33 +0900 Subject: [PATCH 36/39] Delete invalid.jpg --- kadai2/src/tomoyukikobayashi/testdata/invalid.jpg | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 kadai2/src/tomoyukikobayashi/testdata/invalid.jpg diff --git a/kadai2/src/tomoyukikobayashi/testdata/invalid.jpg b/kadai2/src/tomoyukikobayashi/testdata/invalid.jpg deleted file mode 100755 index e69de29..0000000 From acefdc78df3948e37dde91e88f418654f5e06c8f Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Sun, 25 Nov 2018 08:58:15 +0900 Subject: [PATCH 37/39] =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=82=AF=E3=83=AD=E3=83=BC=E3=82=BA=E3=81=8C=E6=BC=8F=E3=82=8C?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=9F=E3=81=AE=E3=81=A7=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai3-1/src/tomoyukikobayashi/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kadai3-1/src/tomoyukikobayashi/main.go b/kadai3-1/src/tomoyukikobayashi/main.go index 840a5da..3f1b8aa 100644 --- a/kadai3-1/src/tomoyukikobayashi/main.go +++ b/kadai3-1/src/tomoyukikobayashi/main.go @@ -62,6 +62,8 @@ func (c *CLI) Run(args []string) int { fmt.Fprintf(c.outStream, "failed to initizalize game %v", err) return ExitError } + // クローズできなくても実害ないので、エラー処理は省略 + defer file.Close() // gameを動作させるインターフェイスを初期化 game, err := typing.NewGame(t, file, c.inStream) From e06f9a4d876266266a97059650db38972ac54b38 Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Sun, 25 Nov 2018 09:10:18 +0900 Subject: [PATCH 38/39] add defer --- kadai3-2/src/tomoyukikobayashi/main.go | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/kadai3-2/src/tomoyukikobayashi/main.go b/kadai3-2/src/tomoyukikobayashi/main.go index 7a09876..7015308 100644 --- a/kadai3-2/src/tomoyukikobayashi/main.go +++ b/kadai3-2/src/tomoyukikobayashi/main.go @@ -148,27 +148,27 @@ func main() { } /// DLした部分ファイルを結合する - - // 複数ファイルを一気に読み出すReaderを作成 - files := make([]io.Reader, procs) - for i := 0; i < procs; i++ { - file, err := os.Open(filepath.Join(dlTmp, strconv.FormatInt(int64(i+1), 10))) + // deferをos.Existと同じブロックに置かないように無名関数にしている + // (でもdefer使わないでシーケンシャルにやるのと変わらんなこれだと) + err = func() error { + // 複数ファイルを一気に読み出すReaderを作成 + files := make([]io.Reader, procs) + for i := 0; i < procs; i++ { + file, err := os.Open(filepath.Join(dlTmp, strconv.FormatInt(int64(i+1), 10))) + // 閉じるの失敗しても実害ないので、エラー処理なし + defer file.Close() + if err != nil { + fmt.Printf("error occurred while reading tmp file %v", err) + os.Exit(ExitError) + } + files[i] = file + } + reader := io.MultiReader(files...) + b, err := ioutil.ReadAll(reader) if err != nil { - fmt.Printf("error occurred while reading tmp file %v", err) - os.Exit(ExitError) + return err } - files[i] = file - } - reader := io.MultiReader(files...) - b, err := ioutil.ReadAll(reader) - if err != nil { - fmt.Printf("error occurred while creating dl file %v", err) - os.Exit(ExitError) - } - // deferをos.Existと同じブロックに置かないように無名関数にしている - // (defer使わないでシーケンシャルにやるのと変わらんなこれだと) - err = func() error { /// tmpフォルダのゴミ掃除(失敗しても特に何もしない)) defer os.RemoveAll(dlTmp) From 67258feec3adad68dba2416a4a73e9970ddd026c Mon Sep 17 00:00:00 2001 From: Tomoyuki Kobayashi Date: Tue, 27 Nov 2018 21:48:30 +0900 Subject: [PATCH 39/39] fix for review comments --- kadai3-1/src/tomoyukikobayashi/main.go | 9 +++- kadai3-1/src/tomoyukikobayashi/typing/game.go | 36 +++++-------- .../src/tomoyukikobayashi/typing/game_test.go | 53 +++++++++++-------- .../src/tomoyukikobayashi/typing/questions.go | 2 +- .../src/tomoyukikobayashi/typing/quiz_data.go | 9 +--- .../typing/quiz_data_test.go | 7 +-- kadai3-2/src/tomoyukikobayashi/main.go | 19 ++----- 7 files changed, 58 insertions(+), 77 deletions(-) diff --git a/kadai3-1/src/tomoyukikobayashi/main.go b/kadai3-1/src/tomoyukikobayashi/main.go index 3f1b8aa..88138c3 100644 --- a/kadai3-1/src/tomoyukikobayashi/main.go +++ b/kadai3-1/src/tomoyukikobayashi/main.go @@ -6,11 +6,13 @@ used in CLI intrerfaces. package main import ( + "context" "flag" "fmt" "io" "os" "path/filepath" + "time" "tomoyukikobayashi/typing" ) @@ -66,14 +68,17 @@ func (c *CLI) Run(args []string) int { defer file.Close() // gameを動作させるインターフェイスを初期化 - game, err := typing.NewGame(t, file, c.inStream) + game, err := typing.NewGame(file, c.inStream) if err != nil { fmt.Fprintf(c.outStream, "failed to initizalize game %v", err) return ExitError } fmt.Fprintf(c.outStream, "start game %d sec\n", t) - qCh, aCh, rCh := game.Run() + tc := time.Duration(t) * time.Second + ctx, cancel := context.WithTimeout(context.Background(), tc) + defer cancel() + qCh, aCh, rCh := game.Run(ctx) for { q, progress := <-qCh diff --git a/kadai3-1/src/tomoyukikobayashi/typing/game.go b/kadai3-1/src/tomoyukikobayashi/typing/game.go index 0ce53dc..51c0a02 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/game.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/game.go @@ -4,18 +4,16 @@ import ( "bufio" "context" "io" - "time" ) // Game タイピングゲームを制御するロジックを返す type Game interface { - Run() (question <-chan string, answer <-chan string, result <-chan [2]int) + Run(context.Context) (question <-chan string, answer <-chan string, result <-chan [2]int) } type constGame struct { Questioner - input io.Reader - timeout int + input io.Reader // NOTE コンテキストにゲーム状態格納しようとしたがコピーして作り直すから、途中で書き換えてもルーチンをまたいでシェアされない(親子関係で) // 結局共有変数に書いていることになるので、脆弱感がすごい clear int @@ -24,22 +22,18 @@ type constGame struct { } // NewGame Gameのコンストラクタです -func NewGame(timeout int, source io.Reader, input io.Reader) (Game, error) { +func NewGame(source, input io.Reader) (Game, error) { // クイズデータを読み込む d, err := NewQuizData(source) if err != nil { return nil, err } q := NewQuestioner(d) - return &constGame{Questioner: q, timeout: timeout, input: input}, nil + return &constGame{Questioner: q, input: input}, nil } // Run ゲームを開始する -func (c *constGame) Run() (<-chan string, <-chan string, <-chan [2]int) { - tc := time.Duration(c.timeout) * time.Second - // NOTE defer cancel() cancelするとDone条件閉じてゲーム終わっちゃう - ctx, _ := context.WithTimeout(context.Background(), tc) - +func (c *constGame) Run(ctx context.Context) (<-chan string, <-chan string, <-chan [2]int) { // TODO routine数にあわせてサイズ調整は死ねるのでonce.DO が使えるかも routines := 2 rCh := make(chan [2]int, routines) @@ -56,15 +50,13 @@ func (c *constGame) question(ctx context.Context, rCh chan<- [2]int) <-chan stri go func() { for { word := c.GetNewWord(c.nextLevel()) - qCh <- word - c.currentWord = word select { + case qCh <- word: + c.currentWord = word case <-ctx.Done(): rCh <- [2]int{c.clear, c.miss} close(qCh) return - default: - //do nothing } } }() @@ -77,24 +69,22 @@ func (c *constGame) answer(ctx context.Context, rCh chan<- [2]int) <-chan string aCh := make(chan string) go func() { for { - if sc.Scan() { - ans := sc.Text() - aCh <- ans + if !sc.Scan() { + continue + } + ans := sc.Text() + select { + case aCh <- ans: if c.isCorrect(ans) { c.clear = c.clear + 1 } else { c.miss = c.miss + 1 } - } - - select { // contextがtimeoutしたら結果を返却 case <-ctx.Done(): rCh <- [2]int{c.clear, c.miss} close(aCh) return - default: - //do nothing } } }() diff --git a/kadai3-1/src/tomoyukikobayashi/typing/game_test.go b/kadai3-1/src/tomoyukikobayashi/typing/game_test.go index c276853..67deba2 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/game_test.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/game_test.go @@ -1,10 +1,12 @@ package typing import ( + "context" "io" "os" "strings" "testing" + "time" ) func Test_NewGame(t *testing.T) { @@ -35,7 +37,7 @@ Level3: for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := NewGame(1, tt.source, os.Stdin) + _, err := NewGame(tt.source, os.Stdin) if err != nil && !tt.hasErr { t.Fatalf("got err = %#v", err) } @@ -77,39 +79,46 @@ hoge difficult difficult `), + hasErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - game, err := NewGame(1, tt.source, tt.input) + game, err := NewGame(tt.source, tt.input) if err != nil { t.Fatalf("initialize game failed %v", game) } + tc := time.Duration(100) * time.Millisecond + ctx, cancel := context.WithTimeout(context.Background(), tc) + defer cancel() + var want [2]int - qCh, aCh, rCh := game.Run() + qCh, aCh, rCh := game.Run(ctx) + var tmpQ, tmpA string for { - q := <-qCh - if q == "" { - break + select { + case a := <-aCh: + t.Errorf("a %v\n", a) + tmpA = a + // TODO 固まることがある + // TOOD 値が微妙に揺れる。。。 + if tmpQ == tmpA { + want[0] = want[0] + 1 + } else { + want[1] = want[1] + 1 + } + case q := <-qCh: + t.Errorf("q %v\n", q) + tmpQ = q + case got := <-rCh: + if want != got { + t.Fatalf("failed got = %v, want = %v", got, want) + } + default: + continue } - a := <-aCh - if a == "" { - break - } - - // TOOD 値が微妙に揺れる。。。テスト壊れる - if q == a { - want[0] = want[0] + 1 - } else { - want[1] = want[1] + 1 - } - } - - got := <-rCh - if want != got { - t.Fatalf("failed got = %v, want = %v", got, want) } }) } diff --git a/kadai3-1/src/tomoyukikobayashi/typing/questions.go b/kadai3-1/src/tomoyukikobayashi/typing/questions.go index 0a5f7e7..35a95bb 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/questions.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/questions.go @@ -25,11 +25,11 @@ func NewQuestioner(data QuizData) Questioner { q := &questionContainer{ qs: qs, } + rand.Seed(time.Now().UnixNano()) return q } func (q *questionContainer) GetNewWord(level int) string { - rand.Seed(time.Now().UnixNano()) rand := rand.Intn(len(q.qs[level])) // HACK ほんとはmap okを見た方がいいけど、省略 return q.qs[level][rand] diff --git a/kadai3-1/src/tomoyukikobayashi/typing/quiz_data.go b/kadai3-1/src/tomoyukikobayashi/typing/quiz_data.go index 9f3c4bb..ed182cb 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/quiz_data.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/quiz_data.go @@ -2,7 +2,6 @@ package typing import ( "io" - "io/ioutil" yamler "gopkg.in/yaml.v2" ) @@ -22,14 +21,8 @@ type QuizSource struct { // NewQuizData クイズデータを生成する func NewQuizData(source io.Reader) (QuizData, error) { - data, err := ioutil.ReadAll(source) - if err != nil { - return nil, err - } - var s QuizSource - err = yamler.Unmarshal([]byte(data), &s) - if err != nil { + if err := yamler.NewDecoder(source).Decode(&s); err != nil { return nil, err } diff --git a/kadai3-1/src/tomoyukikobayashi/typing/quiz_data_test.go b/kadai3-1/src/tomoyukikobayashi/typing/quiz_data_test.go index 0e370d4..eafe3b1 100644 --- a/kadai3-1/src/tomoyukikobayashi/typing/quiz_data_test.go +++ b/kadai3-1/src/tomoyukikobayashi/typing/quiz_data_test.go @@ -32,12 +32,7 @@ Level3: }, hasErr: false, }, - { - name: "invalidFormat", - reader: strings.NewReader(``), - want: QuizSource{}, - hasErr: true, - }, + // TOOD 適当な文字列入れるとエラーなしでnil帰る { name: "invalidStruct", reader: strings.NewReader(` diff --git a/kadai3-2/src/tomoyukikobayashi/main.go b/kadai3-2/src/tomoyukikobayashi/main.go index 7015308..22a3487 100644 --- a/kadai3-2/src/tomoyukikobayashi/main.go +++ b/kadai3-2/src/tomoyukikobayashi/main.go @@ -9,7 +9,6 @@ import ( "crypto/sha1" "fmt" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -117,12 +116,6 @@ func main() { return err } - // 取得したデータをBufに読み出し - buf, err := ioutil.ReadAll(dRes.Body) - if err != nil { - return err - } - // 分割ファイルの保存 file, err := os.Create(fn) if err != nil { @@ -131,7 +124,7 @@ func main() { defer file.Close() // TODO ここで落ちるとファイルが存在するけどDLできていないのにスキップしてしまう - _, err = file.Write(buf) + _, err = io.Copy(file, dRes.Body) if err != nil { return err } @@ -155,19 +148,15 @@ func main() { files := make([]io.Reader, procs) for i := 0; i < procs; i++ { file, err := os.Open(filepath.Join(dlTmp, strconv.FormatInt(int64(i+1), 10))) - // 閉じるの失敗しても実害ないので、エラー処理なし - defer file.Close() if err != nil { fmt.Printf("error occurred while reading tmp file %v", err) os.Exit(ExitError) } + // 閉じるの失敗しても実害ないので、エラー処理なし + defer file.Close() files[i] = file } reader := io.MultiReader(files...) - b, err := ioutil.ReadAll(reader) - if err != nil { - return err - } /// tmpフォルダのゴミ掃除(失敗しても特に何もしない)) defer os.RemoveAll(dlTmp) @@ -179,7 +168,7 @@ func main() { } defer file.Close() - _, err = file.Write(b) + _, err = io.Copy(file, reader) if err != nil { return err }