2020年3月29日

[golang]testとerror

golangの埋め込みを使うことで継承っぽいことができるので、それを使ってテストしようと思っていた。
ハードウェアが絡む部分は、mockを作ってやってしまいたいのでね。

が、先にロジックの部分をテストしておいて、その次にmockを作った方が良いことに気付いた。
まあ、いつもそうやって作るので、golangだろうと何だろうと、そうなるだろう。

 

正常系よりも、準正常系や異常系のチェックが大半だろう。
特にハードウェアが絡むと、実機では通常発生しないようなエラーもテストしたいし。

golangでエラーといえば、errors。
errors.New("文字列")でエラーを返していたけど、文字列だけで判定するのは嫌だ。
エラーコードのような一意の値でうまいことやれないだろうか?


何かを参考にしようと検索していたら、こういうのがあった。

https://github.com/ethereum/go-ethereum/blob/ec2131c8d3382db3762909a8e7d225be61bae230/core/error.go

なるほどね、各packageごとにerror.goを用意しておくのね。
Newだからアドレスの比較になるので、時間もかかるまい。

ちょっと嫌なのは、Newするということは動的に作るというところだ。
動的に作るということはRAMに作るということで、RAMを消費してしまうだろう。
まあ、今回は組み込み向けじゃないから、気にしないが。

 

あとは、せっかくなのでGitHub Actionsとかいうものを使ってみたいのだが、まだまだ私には敷居が高いのう。

[golang][windows]vscodeが起動しているとgo getに失敗する

深く調べていないのだが、記録として残しておこう。

 

環境

  • Windows10 + WSL(Ubuntu 18.04)
  • Visual Studio Code(1.43.2) + Remote-WSL(0.42.4) + Go(0.13.1)

 

Windowsのvscodeを起動した状態でgo getすると失敗した。
既にgo getしてあれば大丈夫だったが、新規で取得する場合に失敗するようだ。

$ go get github.com/google/gousb
go get: rename /home/hirokuma/go/pkg/mod/github.com/google/gousb@v0.0.0-20190812193832-18f4c1d8a750.tmp-933615597 /home/hirokuma/go/pkg/mod/github.com/google/gousb@v0.0.0-20190812193832-18f4c1d8a750: permission denied

 

vscodeを終了しておいてgo getすると大丈夫みたい。
問題がvscodeなのかextensionsなのかは知らないが、取りあえず動けばいいのだ。

2020年3月28日

[golang]go getの"no Go files"はエラーじゃない

「わかる」「わからない」「わかる」「わからない」・・・と花占いのように悩んでしまうのが、golangのmodule関係。

今までVirtualBox上で動かしていたのだけど、今回はテストを試したいだけなのでWindowsでも問題ない。Windowsというか、WSLですな。

git cloneしようと思ったが、そういえばgo getってのもあったのを思い出した。

 

src$ go get -d github.com/hirokuma/go_pasori370
package github.com/hirokuma/go_pasori370: no Go files in /home/hirokuma/go/src/github.com/hirokuma/go_pasori370

エラーだと思ってあれこれパラメータを変えたけど、メッセージは変わらない。
あきらめげgit cloneしようと思ってディレクトリを見てみると、なんとダウンロードできているじゃないか。。。

 

そう、メッセージは単に「goファイルがないよ」といっているだけなのだ。
そして、確かに私はgo_pasori370の直下にはgoファイルを置いていない。
そういえば、go getはpackageを使おうと思って取ってくるようなものだろうから、goファイルがないというのはおかしいと思っているのかもしれない。

 

みんな、というか、ブログなどに書いている人はあまりその辺で悩んでいるような様子が見られない。
まあ、解決する方法を検索しているせいかもしれないが、何か、何か私が理解できていないものがあるのだろう。
その何かを見つける日まで、私の戦いは続く・・・。

VirtualBoxのGuest OS(Linux)を変更して起きたこと2件

タイトルが長い・・・。

 

私の作業環境はWindows10。
Linuxを使いたいことも多いので、そちらはVirtualBoxで動かしている。

余っていたHDDにVMのデータを置いていたのだが、どうにも起動などが遅いので、ほとんど使っていなかった128GBのSSDにOSを入れて、そっちとmountして使うように変更した。

今まではXubuntuかLubuntuだったのだが、今回はMint Linuxにした。
特に何かあったわけではないのだが、たまには違うのもよかろう。

 

Mint Linuxに問題があったわけでもないのだが、ちょうどそのタイミングでいくつか現象が出てしまったのでメモを残しておこう。


Host OSとGuest OSの間でクリップボードの共有ができなくなった

Host側で調べ物をして、その結果をコピーしてGuest側に持っていく、という使い方をしているので、クリップボード共有ができないのは非常に痛手だ。

これの原因は、VirtualBoxのバージョンだった。
アップデートして6.1.4にしていたところが問題だった。

virtualbox.org • View topic - VirtualBox 6.1.4 Clipboard sharing doesn't work
https://forums.virtualbox.org/viewtopic.php?f=6&t=97156

どうしようもなさそうなので、6.1.2にしたら共有できた。

 

Visual Studio Codeを使っているとVMごと落ちる

左側のEXPLORERでファイルを右クリックしてもメニュー枠しか表示されていないので、もう一度右クリックするとVMごと落ちる、という現象に出会った。
これは必ず発生して、一度右クリックした後にvscodeを終了させたとしても落ちてしまった。

Linux Mint Guest OS crashes while using VSCode · Issue #84745 · microsoft/vscode · GitHub
https://github.com/microsoft/vscode/issues/84745

$HOME/.vscode/argv.jsonに"disable-hardware-acceleration": true、などと書けば良い。
私の場合は既にそのファイルがあって、コメントアウトまでされている状態だったので、コメントを外せば済んだ。

スクロールすると描画が遅いのが気になってしまうのだが、VMが落ちるよりはましだ。
そのうち解決するかもしれんしね。


Mint Linux自体は、設定がしやすくて良いですな。

2020年3月26日

[golang]本体無しでテストをしたい (3)

前回、golangで埋め込みというものを使うと継承っぽいことができることが分かった。

では、それをどうテストやmockに応用すると良いのだろうか?

【Go言語】埋め込みでinterfaceを簡単に満たす - Eureka Engineering - Medium
https://medium.com/eureka-engineering/golang-embedded-ac43201cf772

 

テストしたいのは、UserInterfaceを引数に持つIsTestTarget()関数。
何を困っているかというと、この関数内でさらに関数を呼び出しているところだ。
u.Age()の戻り値を26とか25にして、trueだったりfalseだったりになることを確認したいのだが、u.Age()の戻り値をどうやって変更させるか。。。

 

というわけで、ここではUserInterfaceを埋め込んだtestUserという構造体を作り、testUserをreceiverとするAge()関数を作ってしまう。
そうすると、testUserは本体の実装からは切り離されるし、u.Age()は好きな値を返せるようになるし、めでたしめでたし。

「これって、UserInterfaceのテストじゃなくて、testUserのテストになるんじゃないの?」と気がしたが、そもそもIsTestTarget()が実行できれば良いだけなので、その引数にそこまでこだわる必要はあるまい。


では、話の始まりに戻ろう。

testingだけで完結するmockを作る #Golang - Qiita
https://qiita.com/maitaketurn/items/2618e9ecd68852a0a83b

 

テスト対象の構造体TaskControllerはinterfaceのTaskInterfaceを埋め込んでいて、テスト側のmockTaskControllerもTaskInterfaceを埋め込んで、それをreceiverに持つcreateTaskとupdateTaskと同じsignatureの関数がある。

それとは別に、mockCreateTaskとmockUpdateTaskというメンバがあるが、これは関数ポインタみたいなものだろう。
継承したような形で実装したcreateTask()とupdateTask()は、このmockCreateTaskとmockUpdateTaskを呼び出すだけだ。
だから、実際に呼ぶ前にmockCreateTaskとmockUpdateTaskには本体のアドレスを教えてやらねばならぬ。

 

あれに近いか。
fffでfakeを設定するやつだ。例えが分かりづらくて済まんね。
ともかく、テストごとに返したい値が変わるから可変値というやり方もあるけど、ここでは関数ごと差し替えられるようにした、と。


こういうのは、実装し続けていたらわかるようになるだろう。
少なくとも、go.modよりも理解はしやすいだろう。

次回は、自分のテストに埋め込みを使ってやってみる。

2020年3月25日

[golang]本体無しでテストをしたい (2)

テストでmockを使う方法を調べているつもりだったが、いつの間にか埋め込みというものを調べていた。
まあよくあることだ。

 

そもそも、埋め込みのパターンの記事から読み始めてはいかんな。
埋め込みとは何か、から見なくては。

【Go言語】埋め込みでinterfaceを簡単に満たす - Eureka Engineering - Medium
https://medium.com/eureka-engineering/golang-embedded-ac43201cf772

大ざっぱな理解だろうが、継承っぽいことができる、ということでいいのかな。
vtblをコピーしてアドレスが同じところを指しているだけなので、定義すればオーバーライドしてそのメソッドだけは別のものを指定できる、と。
interfaceを埋め込んだ場合はどこも指していないので、定義すれば呼び出せるし、定義しなければnilか何かになっていて呼べない。

 

body.go

01: package body
02: 
03: type BodyInterface interface {
04:     Go() string
05:     GoGo() string
06: }
07: 
08: type BodyStruct struct {
09:     BodyInterface
10: }
11: 
12: func (body *BodyStruct) Go() string {
13:     return "Go"
14: }
15: 
16: func (body *BodyStruct) GoGo() string {
17:     return "GoGo"
18: }

 

mummy.go

01: package body
02: 
03: type MummyStruct struct {
04:     BodyStruct
05: }
06: 
07: func (body *MummyStruct) GoGo() string {
08:     return "MuMu"
09: }

 

main.go

01: package main
02: 
03: import (
04:     "ex6/body"
05:     "fmt"
06: )
07: 
08: func main() {
09:     var body0 body.BodyStruct
10:     fmt.Printf("%s\n", body0.Go())
11:     fmt.Printf("%s\n", body0.GoGo())
12: 
13:     var body body.MummyStruct
14:     fmt.Printf("%s\n", body.Go())
15:     fmt.Printf("%s\n", body.GoGo())
16: }

$ go run ex6/main
Go
GoGo
Go
MuMu

 

MummyStructをreceiverに持つGo()は実装していないのだが、BodyStructのGo()が呼び出せている。
予想通りだ。

 

では、こうしたらどうなる?

 

mummy.go

01: package body
02: 
03: type MummyStruct struct {
04:     BodyInterface
05: }
06: 
07: func (body *MummyStruct) GoGo() string {
08:     return "MuMu"
09: }

 

main.go

01: package main
02: 
03: import (
04:     "ex6/body"
05:     "fmt"
06: )
07: 
08: func main() {
09:     var body0 body.BodyStruct
10:     fmt.Printf("%s\n", body0.Go())
11:     fmt.Printf("%s\n", body0.GoGo())
12: 
13:     var body body.MummyStruct
14:     fmt.Printf("%s\n", body.GoGo())
15:     fmt.Printf("%s\n", body.Go())
16: }

継承っぽいことをしている箇所を、BodyStructではなくBodyInterfaceにしたのだ。
まあ、結果の予想が付くので、Printfするところもbody.GoGo()を先に呼び出すようにした。

$ go run ex6/main
Go
GoGo
MuMu
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x48ea85]

goroutine 1 [running]:
main.main()
        /home/xxx/go/src/ex6/main/main.go:15 +0x1e5
exit status 2

 

定義したMummyStructのGoGo()は呼び出しできているが、定義していないGo()を呼び出すとpanicが発生している。
予想通りですな。

 

では、次回はこれをどうテストに応用できるのか、だ。

2020年3月24日

[golang]本体無しでテストをしたい (1)

golangでは、それなりにテストを行う環境が用意されている。
vscodeなんか使うと、テスト関数のファイル先頭や関数先頭にテスト実行のリンクが出てくるくらいには環境が用意されている。

 

しかし、ハードウェアが絡む場合、実機を使わないようにテストしたい場合がほとんどだろう。
今回のようにPCでそのまま実行できるならよいのかもしれないが、クロスコンパイルで環境がそもそも無かったり、ハードウェアの挙動を指定できなかったりすると、その部分だけ差し替えて実行したいものだ。

スタブとかモックとか言うやつですな。
今まではずっと「スタブ」と呼んでいたのだけど、どうやらモック(mock)という方が正しいらしい。

 

どれが筋のいいやり方なのか分からんので、最初に出てきたものを見てみる。

testingだけで完結するmockを作る #Golang - Qiita
https://qiita.com/maitaketurn/items/2618e9ecd68852a0a83b#%E3%81%BE%E3%81%A8%E3%82%81

んんー、よくわからんぞ?

 

まず、テスト対象の実装。
これはわかる。わかるというか、テスト対象というだけだ。
あるとすれば、interfaceを持っていることと、宣言されたTaskInterfaceをメンバに持つ構造体TaskControllerがあるということか。
TaskInterfaceをメンバに持って、どういう効果があるのだろうか。

わからんのは、test側。
構造体mockTaskControllerがあるのはいいとして、その先頭がTaskInterfaceだけ書いてある。
これが「ダックタイピング」というやつなのか・・・?

 

そうではないらしい。
ダックタイピングというのは、たぶんC言語でC++のclassっぽい、あるいはclassの継承っぽいことをさせたいときに、とあるアドレスからの相対アドレスにあるデータが期待したデータ配列にさえなっていれば実行できるよ、というようなやつと同じようなやつだろう。
言語としてサポートしているなら、メンバの順番が変わっていてもうまいことやってくれるのかもしれん。

 

どうも、埋め込み(embedding)とかいうらしい。

Go言語の埋め込みについて4つのポイントでまとめた - Qiita
https://qiita.com/momotaro98/items/4f6e2facc40a3f37c3c3

似ているのはここか。
HighJumpRunnerインターフェースがあって、それと関係なくgrasshopper構造体があって、でも(*grasshopper)をreceiverに持つHighJump()関数がある。
そして、*grasshopperを埋め込んだShinJinrui2構造体がある。
ShinJinrui2のインスタンスaShinJinrui2の埋め込みメンバ?にgrasshopperのポインタを設定すると、aShinJinrui2.HighJump()が実行できるようになっている。

 

なんか利点があるのだろうが、今日は頭が回らんのでここまで。

2020年3月22日

受信とデコード

前回の続きではあるが、golangと関係ない話だ。

 

RC-S956は、PasoriのRC-S370に搭載されているチップで、RC-S620/Sも同じチップが載っている。
RC-S370とRC-S620/Sの違いは、インターフェースがUSBなのかUARTなのかだ(他にもあるが、使うだけならそれでいいだろう)。

 

RC-S956はNFC R/Wとして動作するチップだ。
詳しいことは知らんのだが、

  1. インターフェースを通してコマンドをユーザが送信
  2. コマンドのフォーマットが正しければチップがACKを送信
  3. レスポンスがあればチップがレスポンスデータを送信

ということになっている。

ただ、ACKやレスポンスを送信するといっても、受信してほしいデータがありますよフラグがあるわけでもなく、そういう取り決めになっているだけだ。
ACKを返すのも設定次第だし(今回は必ずある)、レスポンスデータも固定長では無いので、なんか嫌な感じだ。


では、どう実装するのがよいだろうか。

まず、受信サイズはInEndpointのパケットサイズの逓倍にするのがよかろう。
gousbのサンプルも10パケット分ということで10倍した領域を用意していたし。

幸いなことに、RC-S956からの1回分のレスポンスはサイズの上限が決まっている。
そして、コマンドに対してレスポンスを返すだけで、勝手にレスポンスだけを返すことはなかったはず。
Target系のメッセージがちょっと自信ないのだが、今回は忘れよう。
1コマンド送信に対して、ACK受信(6byte)とレスポンス受信(最大256byte=4パケット)。

ACKは、いい。
受信して最初のデータなので、受信データの先頭6byteをチェックすれば良い。

問題はレスポンスだ。
ACKと別USBパケットになると思うのだけど、100%の確信があるわけではない。
設定でACKを返さないようにできた気はするのだが、その設定をするまではACKが返ってくるので、あまりそこでがんばりたくない。


となると・・・USBの1パケット中にACKとレスポンスが混ざっていたとしてもデコードできるようにするし、ACKとレスポンスが別パケットになったとしても受信をそれぞれ行うようにする、というやり方しかないか。

文字にすると「当たり前やん」と言われそうなのだけど、そういうのが苦手なのだ。

 

コマンド送信後、まずUSB5パケット以上の受信を行う。
先頭の6byteをACKと比較する。
それ以上のデータがまだあればレスポンスとして比較し、残っていなければUSB4パケット以上を受信する。

おそらく、レスポンスが複数パケットになったとしても、1回の受信で全部取得できるだろう。
ACKと連続したパケットであろうとなかろうと、最初のパケットを読めばレスポンス長はわかるので、足りているかどうかはチェックできるのだけど・・・大丈夫じゃないの、と思ってしまうのよね・・・それがダメなのか・・・。

2020年3月21日

[golang][gousb]Write()やRead()から戻らない (2)

golangなのだが、どちらかといえばlibusbの方かもしれない。いや、USBか。

 

Pasoriからちまちまと受信とデコードを繰り返そうと考えていたので、まず3バイトreadして、次は2バイト受信して・・・というコードを書いていた。

が、USBデバイスはパケット単位でのアクセスだったと思う。
Endpointの値を見ても、

2020/03/21 22:04:51 InEndpoint= ep #4 IN (address 0x84) bulk [64 bytes]
2020/03/21 22:04:51 OutEndpoint= ep #4 OUT (address 0x04) bulk [64 bytes]

今回は64バイトなのだろう。

 

送信する方は一度に送っているのでよいのだが、受信の方は考えないといけない。
ACKとレスポンスを返してくるので、おそらくはACKで1パケット、あとはレスポンスで必要な分だけのパケット数というところか。

ACKの返信は脊髄反射的なものだったはずだ。レスポンスを返すよりもACKの方を先に返すはずだ。
だから、パケットとしてもきっとレスポンスと混ざることはない・・・と信じたい。
いや、そんなことを考えるよりは、ACKとレスポンスをデコードできるまで受信し続ける、の方が正しいのか。

 

ともかく、数バイトではなく、EndpointDescのMaxPacketSize単位にすると受信できそうだった。
そうなると、デコードの仕方も考え直さないとなぁ。。。

[golang][gousb]Write()やRead()から戻らない (1)

golangのgousbを使おうとしている。

EndpointはINもOUTも取れたので、実際にデバイスと送受信してみる。
それよりも前にinterfaceの使い方がよくわからずに悩んだのだが、別の機会に。

 

https://github.com/hirokuma/go_pasori370/blob/754bb09df7e35e67a678979ac8240bf10d08eb46/dev/pasori370.go#L97

01: length, err := dev.outEndpoint.Write(data)
  

image

wiresharkで期待したデータをUSBで送信しているのは分かった。

分かったのだが、このWrite()から制御が戻ってこないのだ。
レスポンスが受信できないのであれば送信データが間違っているのかもしれないし、Write()がエラーでも返してくれればヒントになるかもしれない。
だが、制御自体戻ってこないのだ。
なぜだ・・・。

 

と思ったら、open時にgousb.Contextをdeferでcloseするようにしていたためだった。
失敗失敗。
gousb.Deviceのcloseと同じタイミングでcloseすればよいだろう。


Write()の結果は戻るようになったが、次のRead()が戻らない。
wiresharkを見ても受信データが流れているようには見えない。ただ、URB_BULK inにはなっているので受信しようとはしているのだろう。

念のため送信しているデータを以前作ったパケットデータと比較したが、間違えてはいない。
うまくいっていればACKが受信できると思うのだが、そうなっていない。

 

wiresharkで受信したままRC-S370を外して付けなおすと、たぶんkernelがアクセスしているログが取れた。
Resetコマンドもやりとりしているようで、こういう感じになっていた。

image

待ち時間が足りないのか、などとも思っていたのだが、これを見るとそうでもないようだ。
うーむ。

うちのやつと比較してみよう。
こちらがうちのログだ。

image

kernelの方は、out(CMD), in, in(ACK, in, out, in(RES)。
うちの方は、out(CMD), out, in, in。

関係あるかどうか分からないが、outが2回続いているな。
OutEndpoint.Write()を1回だけ呼んでみても、それだけでoutが2回発生してしまう。

 

・・・いや、これは関係なかった。
過去の自作ライブラリでもこうなった。

image

out(CMD), out, in, in(ACK), in, in(RESP)。
readをリトライしていけばなんとかなるかも、と思ったが、ダメだね。

 

わからぬ・・・。