diff --git a/kadai2/sintan23/.gitkeep b/kadai2/sintan23/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/kadai2/sintan23/Makefile b/kadai2/sintan23/Makefile new file mode 100644 index 0000000..6a651d2 --- /dev/null +++ b/kadai2/sintan23/Makefile @@ -0,0 +1,37 @@ +NAME := convertImage +VERSION := 0.0.1 + +SRC_PATH := ./src +SRC := $(SRC_PATH)/*.go +SRC_TEST := $(SRC_PATH)/convert +BIN_PATH := ./bin +OUTPUT_BIN := $(BIN_PATH)/$(NAME) + +.PHONY: $(/bin/bash egrep -o ^[a-zA-Z_-]+: $(MAKEFILE_LIST) | sed 's/://') + + +all: test build + +.PHONY: test +test: + ## test + go test -v ${SRC_TEST} -coverprofile cover.out + go tool cover -func cover.out + +.PHONY: build +build: + ## build + mkdir -p $(BIN_PATH) + go build -o $(OUTPUT_BIN) $(SRC) + chmod 755 $(OUTPUT_BIN) + +.PHONY: run +run: + ## run + rm -f ./_data/jpg*.png + go run $(SRC) + +.PHONY: clean +clean: + ## clean + rm -rf $(BIN_PATH)/* diff --git a/kadai2/sintan23/README.md b/kadai2/sintan23/README.md new file mode 100644 index 0000000..9f433cd --- /dev/null +++ b/kadai2/sintan23/README.md @@ -0,0 +1,28 @@ +# Gopher Dojo 3 - Kadai 2 + +# io.Readerとio.Writerについて調べてみよう + +## 標準パッケージでどのように使われているか +* 標準パーケージに多く使われている。 +* 入出力が必要なところは、ほとんどio.Readerとio.Writerを使っている。 + +## io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる +* 関数に渡す事により、入出力を抽象化している。 +* ファイルやDBなど意識せず扱うことができる。 +* 全体的に実装することにより、サービスによって挙動を変えることができる。 +* テストする際にモックなどの実装がしやすくなる。 + +# テストを書いてみよう +* [convert_test.go]をご参照ください。 + +# テストのしやすさを考えてリファクタリングしてみる +* もっとできそうですが…取り急ぎUPします。 + +# テストのカバレッジを取ってみる +* make testで実行されるtestでカバレッジが表示されるようにしました。 + +# テーブル駆動テストを行う +* [convert_test.go]でテーブル駆動テストを実装しました。 + +# テストヘルパーを作ってみる +* [convert_test.go]でヘルパーが動作するように別関数にしてみました。 \ No newline at end of file diff --git a/kadai2/sintan23/_data/gif1.gif b/kadai2/sintan23/_data/gif1.gif new file mode 100644 index 0000000..844d1dd Binary files /dev/null and b/kadai2/sintan23/_data/gif1.gif differ diff --git a/kadai2/sintan23/_data/jpg1.jpg b/kadai2/sintan23/_data/jpg1.jpg new file mode 100644 index 0000000..17af886 Binary files /dev/null and b/kadai2/sintan23/_data/jpg1.jpg differ diff --git a/kadai2/sintan23/_data/jpg2.jpg b/kadai2/sintan23/_data/jpg2.jpg new file mode 100644 index 0000000..5bf8e88 Binary files /dev/null and b/kadai2/sintan23/_data/jpg2.jpg differ diff --git a/kadai2/sintan23/_data/jpg3.jpg b/kadai2/sintan23/_data/jpg3.jpg new file mode 100644 index 0000000..fdce5b2 Binary files /dev/null and b/kadai2/sintan23/_data/jpg3.jpg differ diff --git a/kadai2/sintan23/_data/png1.png b/kadai2/sintan23/_data/png1.png new file mode 100644 index 0000000..cdf67c4 Binary files /dev/null and b/kadai2/sintan23/_data/png1.png differ diff --git a/kadai2/sintan23/_data/png2.png b/kadai2/sintan23/_data/png2.png new file mode 100644 index 0000000..7455d67 Binary files /dev/null and b/kadai2/sintan23/_data/png2.png differ diff --git a/kadai2/sintan23/bin/convertImage b/kadai2/sintan23/bin/convertImage new file mode 100755 index 0000000..65cee8f Binary files /dev/null and b/kadai2/sintan23/bin/convertImage differ diff --git a/kadai2/sintan23/cover.out b/kadai2/sintan23/cover.out new file mode 100644 index 0000000..ef735ae --- /dev/null +++ b/kadai2/sintan23/cover.out @@ -0,0 +1,32 @@ +mode: set +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:46.31,50.34 3 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:66.2,66.15 1 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:50.34,51.16 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:51.16,54.14 2 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:54.14,58.5 3 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:59.9,62.4 2 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:66.15,68.3 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:71.54,74.9 2 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:79.2,81.12 2 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:75.49,76.28 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:84.51,90.2 4 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:92.42,97.2 3 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:99.31,107.27 5 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:117.2,117.14 1 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:107.27,115.3 3 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:120.36,121.27 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:121.27,122.10 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:128.3,128.47 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:123.31,124.16 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:125.35,126.20 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:132.25,134.2 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:136.23,138.2 1 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:140.35,142.2 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:144.28,146.2 1 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:148.33,150.2 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:152.26,154.2 1 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:157.24,158.16 1 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:158.16,160.3 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:163.25,164.11 1 1 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convert.go:164.11,166.3 1 0 +gopher-dojo/dojo3/kadai2/sintan23/src/convert/convertToPng.go:9.63,21.2 6 1 diff --git a/kadai2/sintan23/src/convert/convert.go b/kadai2/sintan23/src/convert/convert.go new file mode 100644 index 0000000..412e6d0 --- /dev/null +++ b/kadai2/sintan23/src/convert/convert.go @@ -0,0 +1,167 @@ +/* +ConvertImage +*/ +package convert + +import ( + "fmt" + "io" + "log" + "os" + "path/filepath" + "strconv" + "sync" +) + +var ( + toExt string +) + +type Converter struct { + src string + toExt string + reader io.Reader +} + +type Converters []Converter + +var ( + // デバックフラグ true:デバックコメント表示 + DEBUG = false + + // Goルーチンフラグ true:並列処理する + GOROUTINE = false + + // ベースパス + basePath = "" + + // デフォルト拡張子 + baseExtension = "" + + // フォルダから抽出する正規表現パターン + // (最後の/除去はGlobの方で実装済みだった) + basePattern = "/*." +) + +func ConvertAll(toExt string) { + debug("- Convert From: " + getExtension() + " To: " + toExt) + + var waitGroup sync.WaitGroup + for _, c := range getFileList() { + if GOROUTINE { + // goルーチン + waitGroup.Add(1) + go func() { + defer waitGroup.Done() + err := c.Convert(toExt) + logErr(err) + }() + } else { + err := c.Convert(toExt) + logErr(err) + } + } + + // チャネルを待つ + if GOROUTINE { + waitGroup.Wait() + } +} + +func (c Converter) Convert(toExt string) (err error) { + debug("-- Doing... " + filepath.Ext(c.src) + " → " + toExt) + + switch { + case getExtension() == "jpg" && toExt == "png": + c.ConvertJpegToPng(toExt) + } + + debug("--- Output: " + c.getFilename(toExt)) + + return err +} + +func (c Converter) getFilename(ext string) string { + d, f := filepath.Split(c.src) + f = filepath.Base(f[:len(f)-len(filepath.Ext(f))]) + path := filepath.Join(d, f+"."+ext) + + return path +} + +func Open(src string) (io.Reader, error) { + r, err := os.Open(src) + logErr(err) + + return r, err +} + +func getFileList() Converters { + var files Converters + + debug("- Find Pattern: ", getPath()+getPattern()) + + list, err := filepath.Glob(getPath() + getPattern()) + logErr(err) + + for _, src := range list { + r, err := Open(src) + logErr(err) + files = append(files, Converter{ + src, + toExt, + r, + }) + } + + return files +} + +func SetOpts(opts map[string]bool) { + for k, flg := range opts { + switch { + case k == "debugFlg" && flg: + DEBUG = true + case k == "goroutineFlg" && flg: + GOROUTINE = true + } + debug("Option:", k, strconv.FormatBool(flg)) + } +} + +func SetPath(d *string) { + basePath = *d +} + +func getPath() string { + return basePath +} + +func SetExtension(fromExt string) { + baseExtension = fromExt +} + +func getExtension() string { + return baseExtension +} + +func SetPattern(pattern string) { + basePattern = pattern +} + +func getPattern() string { + return basePattern + getExtension() +} + +// Errorチェック +func logErr(err error) { + if err != nil { + log.Fatal(err) + } +} + +func debug(m ...string) { + if DEBUG { + fmt.Printf("%+v\n", m) + } +} diff --git a/kadai2/sintan23/src/convert/convertToPng.go b/kadai2/sintan23/src/convert/convertToPng.go new file mode 100644 index 0000000..7edbae0 --- /dev/null +++ b/kadai2/sintan23/src/convert/convertToPng.go @@ -0,0 +1,21 @@ +package convert + +import ( + "image/jpeg" + "image/png" + "os" +) + +func (c Converter) ConvertJpegToPng(toExt string) (err error) { + img, err := jpeg.Decode(c.reader) + logErr(err) + + // 空ファイル作成 + out, err := os.Create(c.getFilename(toExt)) + logErr(err) + + // pngフォーマットで書き込み + err = png.Encode(out, img) + + return err +} diff --git a/kadai2/sintan23/src/convert/convert_test.go b/kadai2/sintan23/src/convert/convert_test.go new file mode 100644 index 0000000..234879d --- /dev/null +++ b/kadai2/sintan23/src/convert/convert_test.go @@ -0,0 +1,104 @@ +/* +ConvertImage Test +*/ +package convert + +import ( + "os" + "testing" +) + +type Case = struct { + name string + src string + toExt string +} + +func TestConvertAll(t *testing.T) { + var convertTests = []struct { + caseName string + toExt string + }{ + {caseName: "Test Convert Jpg to Png 1", toExt: "png"}, + {caseName: "Test Convert Jpg to Png 2", toExt: "png"}, + } + for _, c := range convertTests { + t.Run( + c.caseName, func(t *testing.T) { + ConvertAll("png") + }) + } +} + +func TestConvert(t *testing.T) { + var convertTests = []Case{ + {name: "Test Convert Jpg to Png", src: "../../_data/jpg1.jpg", toExt: "png"}, + } + + for _, tt := range convertTests { + testConvert(t, tt) + } +} + +func testConvert(t *testing.T, c Case) { + t.Helper() + + r, err := os.Open(c.src) + if err != nil { + t.Fatalf("Open error = %v", err) + } + + ct := Converter{ + c.src, + c.toExt, + r, + } + + t.Run(c.name, func(t *testing.T) { + err := ct.Convert(ct.toExt) + if err != nil { + t.Errorf("Convert error = %v", err) + } + }) +} + +func TestConvertJpegToPng(t *testing.T) { + var convertTests = []struct { + caseName string + src string + toExt string + }{ + {caseName: "Test ConvertJpegToPng", src: "../../_data/jpg1.jpg", toExt: "png"}, + } + + for _, c := range convertTests { + t.Run( + c.caseName, func(t *testing.T) { + r, err := Open(c.src) + logErr(err) + ct := Converter{ + c.src, + c.toExt, + r, + } + ct.ConvertJpegToPng(c.toExt) + }) + } +} + +func TestLogErr(t *testing.T) { + var convertTests = []struct { + caseName string + src string + }{ + {caseName: "Test Open+LogErr", src: "../../_data/jpg1.jpg"}, + } + + for _, c := range convertTests { + t.Run( + c.caseName, func(t *testing.T) { + _, err := Open(c.src) + logErr(err) + }) + } +} diff --git a/kadai2/sintan23/src/main.go b/kadai2/sintan23/src/main.go new file mode 100644 index 0000000..40b1ac5 --- /dev/null +++ b/kadai2/sintan23/src/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "flag" + + "./convert" +) + +var ( + src = flag.String("s", "", "変換するフォルダパスを指定") + fromExt = flag.String("i", "jpg", "変換元の画像拡張子を指定(default: jpg)") + toExt = flag.String("o", "png", "変換後の画像拡張子を指定(default: png)") + debugFlg = flag.Bool("debug", false, "デバック用: 指定でログが標準出力される") + goroutineFlg = flag.Bool("multi", false, "Goルーチン用: 指定すると並列処理する") +) + +func main() { + flag.Parse() + opts := map[string]bool{ + "debugFlg": *debugFlg, + "goroutineFlg": *goroutineFlg, + } + + convert.SetPath(src) + convert.SetOpts(opts) + convert.SetExtension(*fromExt) + convert.ConvertAll(*toExt) +}