diff --git a/kadai2/miyahara/.gitignore b/kadai2/miyahara/.gitignore new file mode 100644 index 0000000..f32e31a --- /dev/null +++ b/kadai2/miyahara/.gitignore @@ -0,0 +1,2 @@ +.idea/ +.DS_Store diff --git a/kadai2/miyahara/README.md b/kadai2/miyahara/README.md new file mode 100644 index 0000000..41417e9 --- /dev/null +++ b/kadai2/miyahara/README.md @@ -0,0 +1,22 @@ +# 画像変換コマンド +## 課題内容 +- 次の仕様を満たすコマンドを作って下さい + - ディレクトリを指定する + - 指定したディレクトリ以下のJPGファイルをPNGに変換(デフォルト) + - ディレクトリ以下は再帰的に処理する + - 変換前と変換後の画像形式を指定できる(オプション) +- 以下を満たすように開発してください + - mainパッケージと分離する + - 自作パッケージと標準パッケージと準標準パッケージのみ使う + - 準標準パッケージ:golang.org/x以下のパッケージ + - ユーザ定義型を作ってみる + - GoDocを生成してみる + +## 実行方法 +実行するために以下のコマンドを実行してください。 +``` +go build -o conv +./conv -i=[input file type] -o=[output file type] [target directory] + +``` +`input file type` と`output file type` はjpgかpngを選択してください。 \ No newline at end of file diff --git a/kadai2/miyahara/converter/TestData/jpg/testData.jpg b/kadai2/miyahara/converter/TestData/jpg/testData.jpg new file mode 100755 index 0000000..0893cbf Binary files /dev/null and b/kadai2/miyahara/converter/TestData/jpg/testData.jpg differ diff --git a/kadai2/miyahara/converter/TestData/png/testData.png b/kadai2/miyahara/converter/TestData/png/testData.png new file mode 100755 index 0000000..30081ce Binary files /dev/null and b/kadai2/miyahara/converter/TestData/png/testData.png differ diff --git a/kadai2/miyahara/converter/converter.go b/kadai2/miyahara/converter/converter.go new file mode 100644 index 0000000..d3655f5 --- /dev/null +++ b/kadai2/miyahara/converter/converter.go @@ -0,0 +1,139 @@ +package converter + +import ( + "errors" + "fmt" + "image" + "image/jpeg" + "image/png" + "os" + "path/filepath" + "strings" +) + +// Converter Convert jpg to png or png to jpg. +// In represent a input file type. +// Out represent a output file type. +// Directory represent a target directory. +type Converter struct { + in string + out string + directory string +} + +// FileSet is Set of input file name and output file name. +type FileSet struct { + inputFileName string + outputFileName string +} + +// New return a new Converter. +func New(in, out, directory string) *Converter { + return &Converter{in: in, out: out, directory: directory} +} + +// Run execute image convert function. +func (c Converter) Run() int { + var err error + fileSetSlice, err := c.dirWalk() + if err != nil { + fmt.Println(err) + return 1 + } + err = c.convert(fileSetSlice) + if err != nil { + fmt.Println(err) + return 1 + } + return 0 +} + +// dirWalk returns file set of input file name and output file in target directory. +func (c Converter) dirWalk() ([]FileSet, error) { + fileSetSlice := make([]FileSet, 0, 50) + err := filepath.Walk(c.directory, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if filepath.Ext(info.Name()) == ("." + c.in) { + inputFileName := path + outputFileName := c.outputFilePath(inputFileName) + fileSet := FileSet{inputFileName: inputFileName, outputFileName: outputFileName} + fmt.Println(inputFileName, outputFileName) + fileSetSlice = append(fileSetSlice, fileSet) + } + return nil + }) + + if err != nil { + return nil, err + } + + return fileSetSlice, nil +} + +// convert calls execute function every file set. +func (c Converter) convert(fileSetSlice []FileSet) error { + var err error + + for _, fileSet := range fileSetSlice { + err = c.execute(fileSet) + if err != nil { + break + } + } + + return err +} + +// execute function open file and calls encode function. +func (c Converter) execute(fileset FileSet) error { + var err error + + inputFile, err := os.Open(fileset.inputFileName) + defer inputFile.Close() + if err != nil { + return err + } + + outputFile, err := os.Create(fileset.outputFileName) + defer outputFile.Close() + if err != nil { + return err + } + + img, _, err := image.Decode(inputFile) + if err != nil { + return err + } + + err = c.encode(outputFile, img) + if err != nil { + return err + } + + return nil +} + +// encode returns error by encode function. +// if output type is not support, Encode returns error. +func (c Converter) encode(file *os.File, image image.Image) error { + switch c.out { + case "jpg", "jpeg": + err := jpeg.Encode(file, image, &jpeg.Options{Quality: 80}) + return err + case "png": + err := png.Encode(file, image) + return err + default: + return errors.New("invalid output type") + } +} + +// outputFilePath returns output file path to correspond input file path. +func (c Converter) outputFilePath(inputFileName string) string { + stringSlice := strings.Split(inputFileName, ".") + outputFileName := stringSlice[0] + "." + c.out + return outputFileName +} diff --git a/kadai2/miyahara/converter/converter_test.go b/kadai2/miyahara/converter/converter_test.go new file mode 100644 index 0000000..8a42ead --- /dev/null +++ b/kadai2/miyahara/converter/converter_test.go @@ -0,0 +1,315 @@ +package converter + +import ( + "image" + "os" + "reflect" + "testing" +) + +func TestNew(t *testing.T) { + cases := map[string]struct { + in string + out string + directory string + }{ + "success": { + in: "inputFileName", + out: "outputFileName", + directory: "targetDirectory", + }, + } + + for n, tc := range cases { + tc := tc + t.Run(n, func(t *testing.T) { + converter := New(tc.in, tc.out, tc.directory) + if converter.in != tc.in { + t.Errorf("want %s,but actual %s\n", tc.in, converter.in) + } + + if converter.out != tc.out { + t.Errorf("want %s,but actual %s\n", tc.in, converter.out) + } + + if converter.directory != tc.directory { + t.Errorf("want %s, but actual %s\n", tc.directory, converter.directory) + } + }) + } +} + +func TestConverter_Run(t *testing.T) { + cases := map[string]struct { + converter Converter + expectInt int + }{ + "success": { + converter: Converter{in: "jpg", out: "png", directory: "TestData/jpg"}, + expectInt: 0, + }, + "failed": { + converter: Converter{in: "jpg", out: "png", directory: "test"}, + expectInt: 1, + }, + } + + for n, tc := range cases { + tc := tc + t.Run(n, func(t *testing.T) { + actualInt := tc.converter.Run() + if actualInt != tc.expectInt { + t.Errorf("want %d, but actual %d", tc.expectInt, actualInt) + } + }) + } +} + +func TestConverter_Convert(t *testing.T) { + cases := map[string]struct { + converter Converter + fileSet []FileSet + errBool bool + }{ + "success": { + converter: Converter{in: "jpg", out: "png"}, + fileSet: []FileSet{ + { + inputFileName: "TestData/jpg/testData.jpg", + outputFileName: "TestData/jpg/testOutput.png", + }, + }, + errBool: false, + }, + "error": { + converter: Converter{in: "jpg", out: "png"}, + fileSet: []FileSet{ + { + inputFileName: "test", + outputFileName: "test", + }, + }, + errBool: true, + }, + } + + for n, tc := range cases { + tc := tc + t.Run(n, func(t *testing.T) { + err := tc.converter.convert(tc.fileSet) + actualErrBool := err != nil + if actualErrBool != tc.errBool { + t.Errorf("want %t,but actual %t\n", tc.errBool, actualErrBool) + } + }) + } +} + +func TestConverter_Execute(t *testing.T) { + cases := map[string]struct { + converter Converter + fileSet FileSet + errBool bool + }{ + "success": { + converter: Converter{in: "jpg", out: "png"}, + fileSet: FileSet{ + inputFileName: "TestData/jpg/testData.jpg", + outputFileName: "TestData/jpg/testOutput.png", + }, + errBool: false, + }, + "failed to open input file": { + converter: Converter{in: "jpg", out: "png"}, + fileSet: FileSet{ + inputFileName: "test", + outputFileName: "testData/jpg/testOutput.png", + }, + errBool: true, + }, + "failed to open output file": { + converter: Converter{in: "jpg", out: "png"}, + fileSet: FileSet{ + inputFileName: "test", + outputFileName: "testData/jpg/testOutput.png", + }, + errBool: true, + }, + } + + for n, tc := range cases { + tc := tc + t.Run(n, func(t *testing.T) { + err := tc.converter.execute(tc.fileSet) + actualErrBool := err != nil + if actualErrBool != tc.errBool { + t.Errorf("want %t,but actual %t\n", tc.errBool, actualErrBool) + } + }) + } +} + +func TestConverter_Encode(t *testing.T) { + cases := map[string]struct { + converter Converter + inputFileName string + outputFileName string + }{ + "jpg to png": { + converter: Converter{out: "png"}, + inputFileName: "TestData/jpg/testData.jpg", + outputFileName: "TestData/jpg/testOutput.png", + }, + "png to jpeg": { + converter: Converter{out: "jpeg"}, + inputFileName: "TestData/png/testData.png", + outputFileName: "TestData/png/testOutput.jpg", + }, + } + + for n, tc := range cases { + tc := tc + t.Run(n, func(t *testing.T) { + var err error + + outputFile := testCreateFile(t, tc.outputFileName) + defer outputFile.Close() + + inputFile := testOpenFile(t, tc.inputFileName) + defer inputFile.Close() + + convertImage, _, err := image.Decode(inputFile) + if err != nil { + t.Error(err) + } + + err = tc.converter.encode(outputFile, convertImage) + if err != nil { + t.Error(err) + } + + file := testOpenFile(t, tc.outputFileName) + defer file.Close() + + _, actual, err := image.Decode(file) + if err != nil { + t.Error(err) + } + + if tc.converter.out != actual { + t.Errorf("want %s, but actual %s\n", tc.converter.out, actual) + } + }) + } +} + +func TestConverter_EncodeErr(t *testing.T) { + cases := map[string]struct { + converter Converter + expectErr string + }{ + "error": { + converter: Converter{out: "error"}, + expectErr: "invalid output type", + }, + } + + for n, tc := range cases { + tc := tc + t.Run(n, func(t *testing.T) { + err := tc.converter.encode(nil, nil) + if err.Error() != tc.expectErr { + t.Errorf("want %s,but actual %s\n", tc.expectErr, err.Error()) + } + }) + } + +} + +func TestConverter_OutputFilePath(t *testing.T) { + cases := map[string]struct { + converter Converter + inputFileName string + outputFileName string + }{ + "jpg to png": { + converter: Converter{out: "png"}, + inputFileName: "/test/test.jpg", + outputFileName: "/test/test.png", + }, + "png to jpg": { + converter: Converter{out: "jpg"}, + inputFileName: "test/test.png", + outputFileName: "test/test.jpg", + }, + } + + for n, tc := range cases { + tc := tc + t.Run(n, func(t *testing.T) { + actual := tc.converter.outputFilePath(tc.inputFileName) + if tc.outputFileName != actual { + t.Errorf("want %s, but actual %s\n", tc.outputFileName, actual) + } + }) + } + +} + +func TestConverter_DirWalk(t *testing.T) { + cases := map[string]struct { + converter Converter + fileSetSlice []FileSet + errBool bool + }{ + "testData Dir": { + converter: Converter{in: "jpg", out: "png", directory: "./TestData/jpg"}, + fileSetSlice: []FileSet{ + { + inputFileName: "TestData/jpg/testData.jpg", + outputFileName: "TestData/jpg/testOutput.png", + }, + }, + errBool: false, + }, + "not exist directory": { + converter: Converter{in: "jpg", out: "png", directory: "test"}, + fileSetSlice: nil, + errBool: true, + }, + } + + for n, tc := range cases { + tc := tc + t.Run(n, func(t *testing.T) { + actual, err := tc.converter.dirWalk() + if tc.fileSetSlice != nil { + if !reflect.DeepEqual(tc.fileSetSlice[0], actual[0]) { + t.Errorf("want %v ,but actual %v\n", tc.fileSetSlice[0], actual[0]) + } + } + actualErrBool := err != nil + if tc.errBool != actualErrBool { + t.Errorf("want %t, but actual %t\n", tc.errBool, actualErrBool) + } + }) + } +} + +func testOpenFile(t *testing.T, fileName string) *os.File { + t.Helper() + file, err := os.Open(fileName) + if err != nil { + t.Fatal(err) + } + return file +} + +func testCreateFile(t *testing.T, fileName string) *os.File { + t.Helper() + file, err := os.Create(fileName) + if err != nil { + t.Fatal(err) + } + return file +} diff --git a/kadai2/miyahara/main.go b/kadai2/miyahara/main.go new file mode 100644 index 0000000..1c861e5 --- /dev/null +++ b/kadai2/miyahara/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "flag" + "fmt" + "github.com/gopherdojo/dojo4/kadai2/miyahara/converter" + "os" +) + +var ( + inputFile = flag.String("i", "jpg", "input image type") + outputFile = flag.String("o", "png", "output image type") +) + +func usage() { + fmt.Fprint(os.Stderr, "usage: conv -i=[input file type] -o=[output file type] [target directory]\n") + flag.PrintDefaults() +} + +func main() { + flag.Usage = usage + flag.Parse() + + if flag.NArg() == 0 { + fmt.Fprint(os.Stderr, "error: specify target directory\n") + os.Exit(2) + } + + converter := converter.New(*inputFile, *outputFile, flag.Arg(0)) + os.Exit(converter.Run()) +}