Skip to content

【課題2】interfaceとテスト #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f29efad
単一ファイルの変換
kz23szk Sep 2, 2019
8e35577
パッケージ分離
kz23szk Sep 3, 2019
9df4d0d
空readme作成
kz23szk Sep 3, 2019
d0d2f02
mainを一旦きれいに
kz23szk Sep 3, 2019
b5a748f
Merge remote-tracking branch 'origin/master' into kadai1-su-san
kz23szk Sep 3, 2019
ea4be2f
単品ファイルを変換できるようになった
kz23szk Sep 3, 2019
8187cb1
オプション用のコード追加
kz23szk Sep 3, 2019
eb9bd14
再帰的に変換後のファイルを生成
kz23szk Sep 3, 2019
47a435e
コメントと変換成功時に元ファイルの削除
kz23szk Sep 3, 2019
15920f0
拡張子による変換できるように
kz23szk Sep 3, 2019
b7bcfeb
コマンドライン引数を受け取る
kz23szk Sep 4, 2019
09066b9
拡張子を引数から取得できるように
kz23szk Sep 4, 2019
74ffcff
fmt,vet,lint適用
kz23szk Sep 4, 2019
798b95b
返還前後のフォーマットが同じなら処理をしない
kz23szk Sep 4, 2019
51508ea
コメントにスペース挿入
kz23szk Sep 4, 2019
47a2d34
対応フォーマットか確認する
kz23szk Sep 4, 2019
e543fde
godoc生成
kz23szk Sep 4, 2019
7ad4ca9
readmeに課題記載
kz23szk Sep 4, 2019
fec0bc1
godocの回答補足
kz23szk Sep 4, 2019
b8d661f
バイナリ作成
kz23szk Sep 4, 2019
239202a
使い方記述
kz23szk Sep 4, 2019
92afb4c
タイポ修正
kz23szk Sep 4, 2019
a2bda31
説明文整形
kz23szk Sep 4, 2019
5c601ca
ファイルの存在チェックが重複していたので1箇所に修正
kz23szk Sep 5, 2019
03f9b5a
ファイルパスの探索方法をfilepath.Walkに変更
kz23szk Sep 5, 2019
d450fc9
致命的エラーの場合は終了ステータス1を返す
kz23szk Sep 5, 2019
500f21f
標準エラー出力にエラーを出す
kz23szk Sep 5, 2019
35bd7d1
指摘事項修正
kz23szk Sep 12, 2019
2b65325
エラーメッセージに改行
kz23szk Sep 13, 2019
4377957
コメント追加
kz23szk Sep 13, 2019
96ad6be
defer内にエラーがある場合の処理追加
kz23szk Sep 15, 2019
1f57deb
ディレクトリ移動
kz23szk Sep 15, 2019
1404ff7
ディレクトリ構成変更
kz23szk Sep 15, 2019
7614a37
単一関数の役割を減らす
kz23szk Sep 16, 2019
2654fba
テストカバレッジを高める
kz23szk Sep 16, 2019
946070a
パッケージ名分ける
kz23szk Sep 16, 2019
baf9079
io.Readerの回答
kz23szk Sep 16, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file removed kadai1/.gitkeep
Empty file.
53 changes: 53 additions & 0 deletions kadai2/su-san/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 課題2

## io.Readerとio.Writerについて調べてみよう
- 標準パッケージでどのように使われているか
- 今回使った標準imageパッケージでも使われている。
https://github.com/golang/go/blob/75da700d0ae307ebfd4a3493b53e8f361c16f481/src/image/gif/reader.go#L218-L225


- io.Readerとio.Writerがあることで
どういう利点があるのか具体例を挙げて考えてみる
https://github.com/golang/go/blob/75da700d0ae307ebfd4a3493b53e8f361c16f481/src/image/gif/reader.go#L218-L225

上記のように入力をio.Readerとすることでファイルだけでなく他の入力方法(カメラからのリアルタイムデータ、通信経由のデータなど)も同じメソッドで扱うことが
できる。このコードのように型判定はそのメソッドで行う。


## テストを書いてみよう

- 1回目の宿題のテストを作ってみて下さい
- [x] テストのしやすさを考えてリファクタリングしてみる
- 拡張子変換メソッドから拡張子が正しいかの判定とファイルの削除処理を外した
- [x] テストのカバレッジを取ってみる
- `go test -cover` とhtml出力やってみた
- [x] テーブル駆動テストを行う
- サブテスト化まで完了。すごくわかりやすい!
- [x] テストヘルパーを作ってみる
- 取ってつけたがまだメリットがよくわかっていない。別のメソッドを呼んでテストする際にエラー箇所がわかりやすくなる?

## 疑問点
- テストヘルパーのメリット
- ファイルが絡むテストのモック

## カバレッジのとり方

`go test -cover`

出力例

```
PASS
coverage: 93.8% of statements
ok github.com/gopherdojo/dojo7/kadai2/su-san 0.127s
```

カバレッジの内容をhtmlで確認する方法

```bash
go test -coverprofile=cover.out
go tool cover -html=cover.out -o cover.html
```



112 changes: 112 additions & 0 deletions kadai2/su-san/cmd/imgconv/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package main

import (
"flag"
"fmt"
"os"
"path/filepath"

"github.com/gopherdojo/dojo7/kadai2/su-san"
)

func main() {

inputExt := flag.String("i", "jpg", " extension to be converted ")
outputExt := flag.String("o", "png", " extension after conversion")

// Usageメッセージ
flag.Usage = func() {
fmt.Fprintln(os.Stderr, "usage : cmd [-i] [-o] target_dir")
flag.PrintDefaults()
}

flag.Parse()
args := flag.Args()

if flag.NArg() == 0 {
flag.Usage()
return
}

targetDir := args[0]

// 変換対象のフォーマットと変換フォーマットが同じなら何もせず終了
if *inputExt == *outputExt {
return
}

convExts := image.NewConvExts(*inputExt, *outputExt)
if !convExts.SupportedFormats() {
fmt.Fprintf(os.Stderr, "unsupported format! please specify these format [png jpg jpeg gif]\n")
return
}

var targetPaths []string
err := filepath.Walk(targetDir, func(path string, info os.FileInfo, err error) error {
if filepath.Ext(path) == ("." + *inputExt) {
targetPaths = append(targetPaths, path)
}
return nil
})

if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

for _, p := range targetPaths {

//var p Path = Path(p)
// 対象の拡張子出ない場合はスルーする
path := Path(p)
if !(path.IsConvesionTargetExt(convExts)) {
continue
}

// 対象の拡張子でない場合はスルーする
if image.SupportedFormat(filepath.Ext(p)) == false {
fmt.Fprintln(os.Stderr, err)
continue
}

f, err := os.Open(p)
// 読み込みエラーの場合はエラーを出してスルーする
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}

err = image.FmtConv(f, convExts)
if err != nil {
fmt.Fprintln(os.Stderr, err, " error filepath:", p)
os.Exit(1)
}

// 成功ならば変換元を削除する
if err := os.Remove(p); err != nil {
fmt.Fprintln(os.Stderr, err, " error filepath:", p)
os.Exit(1)
}

err = f.Close()
if err != nil {
fmt.Fprintln(os.Stderr, err, " error filepath:", p)
os.Exit(1)
}
}
}

type Path string

func (p *Path) IsConvesionTargetExt(c image.ConvExts) bool {
ext := filepath.Ext(string(*p))
if len(ext) == 0 {
return false
}

if ext[0] == '.' {
ext = ext[1:]
}

return ext == c.InExt
}
190 changes: 190 additions & 0 deletions kadai2/su-san/cover.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
body {
background: black;
color: rgb(80, 80, 80);
}
body, pre, #legend span {
font-family: Menlo, monospace;
font-weight: bold;
}
#topbar {
background: black;
position: fixed;
top: 0; left: 0; right: 0;
height: 42px;
border-bottom: 1px solid rgb(80, 80, 80);
}
#content {
margin-top: 50px;
}
#nav, #legend {
float: left;
margin-left: 10px;
}
#legend {
margin-top: 12px;
}
#nav {
margin-top: 10px;
}
#legend span {
margin: 0 5px;
}
.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }

</style>
</head>
<body>
<div id="topbar">
<div id="nav">
<select id="files">

<option value="file0">github.com/gopherdojo/dojo7/kadai2/su-san/image.go (93.8%)</option>

</select>
</div>
<div id="legend">
<span>not tracked</span>

<span class="cov0">not covered</span>
<span class="cov8">covered</span>

</div>
</div>
<div id="content">

<pre class="file" id="file0" style="display: none">// Package image は画像のフォーマットを変換するためのパッケージです。
package image

import (
"image"
"image/gif"
"image/jpeg"
"image/png"
"os"
"path/filepath"
)

var supportedFormats = map[string]bool{"jpg": true, "jpeg": true, "png": true, "gif": true}

func SupportedFormat(ext string) bool <span class="cov8" title="1">{
// ドット始まりの拡張子ならドットを削除する
if len(ext) &gt; 0 &amp;&amp; ext[0] == '.' </span><span class="cov8" title="1">{
ext = ext[1:]
}</span>
<span class="cov8" title="1">_, ok := supportedFormats[ext]
return ok</span>
}

// ConvExts は変換対象のフォーマットと変換先のフォーマットを表す構造体です
type ConvExts struct {
InExt, OutExt string
}

// SupportedFormats は指定フォーマットが対応しているか確認するメソッドです
func (c *ConvExts) SupportedFormats() bool <span class="cov8" title="1">{
return SupportedFormat(c.InExt) &amp;&amp; SupportedFormat(c.OutExt)
}</span>

// NewConvExts は変換対象のフォーマットと変換先のフォーマットを表す構造体です
func NewConvExts(in, out string) ConvExts <span class="cov8" title="1">{
if in == "" </span><span class="cov8" title="1">{
in = "jpg"
}</span>

<span class="cov8" title="1">if out == "" </span><span class="cov8" title="1">{
out = "png"
}</span>
<span class="cov8" title="1">return ConvExts{InExt: in, OutExt: out}</span>
}

// FmtConv は指定されたフォーマットからフォーマットへ変換する関数です
func FmtConv(f *os.File, exts ConvExts) (err error) <span class="cov8" title="1">{

var img image.Image
var decodeErr error

switch exts.InExt </span>{
case "jpeg", "jpg":<span class="cov8" title="1">
img, decodeErr = jpeg.Decode(f)</span>
case "png":<span class="cov8" title="1">
img, decodeErr = png.Decode(f)</span>
case "gif":<span class="cov8" title="1">
img, decodeErr = gif.Decode(f)</span>
}

<span class="cov8" title="1">if decodeErr != nil </span><span class="cov0" title="0">{
return decodeErr
}</span>

<span class="cov8" title="1">path := f.Name()
pathWithoutExt := path[:len(path)-len(filepath.Ext(path))+1]
outputFile, err := os.Create(pathWithoutExt + exts.OutExt)
if err != nil </span><span class="cov0" title="0">{
return err
}</span>
<span class="cov8" title="1">defer func() </span><span class="cov8" title="1">{
cerr := outputFile.Close()
if err == nil </span><span class="cov8" title="1">{
err = cerr
}</span>
}()

<span class="cov8" title="1">switch exts.OutExt </span>{
case "jpeg", "jpg":<span class="cov8" title="1">
err = jpeg.Encode(outputFile, img, nil)</span>
case "png":<span class="cov8" title="1">
err = png.Encode(outputFile, img)</span>
case "gif":<span class="cov8" title="1">
err = gif.Encode(outputFile, img, nil)</span>
}

<span class="cov8" title="1">return</span>
}
</pre>

</div>
</body>
<script>
(function() {
var files = document.getElementById('files');
var visible;
files.addEventListener('change', onChange, false);
function select(part) {
if (visible)
visible.style.display = 'none';
visible = document.getElementById(part);
if (!visible)
return;
files.value = part;
visible.style.display = 'block';
location.hash = part;
}
function onChange() {
select(files.value);
window.scrollTo(0, 0);
}
if (location.hash != "") {
select(location.hash.substr(1));
}
if (!visible) {
select("file0");
}
})();
</script>
</html>
Loading