記録

記録

Android Studio + Robolectricで自動テスト

Androidでの自動テストはRobolectricというフレームワークを使うのが良いと聞いたので導入してみました。

導入方法と僕が詰まった箇所の解決法を書いておきます。
導入方法は 【Android】Android Studio + Gradle + Robolectric!でテストをしよう | Yohei Blog こちらを参考にさせて頂きました。

公式サイトは Robolectricです。

導入方法

build.gralde

project直下のbuild.gradleにclasspath 'org.robolectric~を追記します。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.12.+'
        classpath 'org.robolectric:robolectric-gradle-plugin:0.12.+'
    }
}

app/build.gradle

apply plugin: 'android'
//プラグイン追加
apply plugin: 'robolectric'
android{
//〜中略〜//
}

dependencies {

    compile fileTree(dir: 'libs', include: ['*.jar'])

    //ライブラリを追加
    androidTestCompile('junit:junit:4.+')
    androidTestCompile('org.robolectric:robolectric:2.3+')
    androidTestCompile('com.squareup:fest-android:1.0+')

}
//robolectricの設定を追記
robolectric {
    include '**/*Test.class'
    exclude '**/espresso/**/*.class'

    maxHeapSize = "2048m" // エラーの原因かもしれない

}

テスト用クラスを作成

クラスを作成するディレクトリはapp/src/test/javaになります。
packageはメインのプロジェクトと同じにしてください。

@RunWith(RobolectricTestRunner.class)
@Config(emulateSdk = 18) //後述
public class SampleTest {
    @Before
    public void setup() {
        // setup
    }

    @After
    public void teardown() {
        // teardown
    }

    @Test
    public void sampleTest() {
        Assert.assertTrue("assert",true);
    }
}

テスト実行

~/.gradlew clean check
BUILD SUCCESSFULとなれば成功です。

1 test completed, 1 failed
:app:testDebug FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:testDebug'.
> There were failing tests. See the report at: file://~/app/build/test-report/debug/index.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 16.035 secs

上記のように出力された場合はテストが失敗しています。
テストレポートは{project}/app/build/test-report/debug/index.htmlで確認できます。

上手くいかない場合

パッケージorg.junitは存在しません

まず最初に、ビルド時にorg.junitは存在しませんというエラーが出ました。
app/src/test/javaディレクトリは、android studio上でNew→Folder→Java Folderという手順で作成しましたが、その際に app/build.gradleにこのように追記されていました。

    SourceSets {
        androidTest.setRoot('src/test')
        main { java.srcDirs = ['src/main/java', 'src/test/java'] }
    }

フォルダ作成時にどのような手順をとれば良いかはわかりませんが、上記をこのように変更することでビルドが通ります。

    SourceSets {
        androidTest.setRoot('src/test')
    }

too small initial heap

次に、too small initial heapというエラーが出ました。
app/build.gradleプラグイン設定でmaxHeapSizeという項目を削除したら解決しましたが、この記事を書くために再び追記してみたらエラーは出ませんでした...。謎です。

java.lang.UnsupportedOperationException

java.lang.UnsupportedOperationExceptionというエラーが出た場合はsdkがサポートされていないということなので、アノテーション@Config(emulatedSdk = 18)と記述してやれば解決します。

長くなってしまいましたが導入にかなり苦戦してしまったので、同じようなエラーで悩んでいる方の一助となれば幸いです。

シェルスクリプト for文で引数全てに対し繰り返し処理を実行する

シェルスクリプトでfor文は基本的に

for x in $var1 $var2 $var3
do
 echo $x
done

みたいな感じに変数や値を複数指定して使用しますが、

#for_echo.sh
for x in "$@"
do
 echo $x
done

上記のように記述すると指定した引数全てに対し処理が実行されます。

in 以降は省略可能で

#for_echo.sh
for x
do
 echo $x
done

この様に記述しても良いです。

$./for_echo.sh foo bar baz
foo 
bar
baz

基本的にin以降は省略しないほうが良いようですが、省略できること自体知らなかったのでメモ。

Android SurfaceView でカウントダウンタイマー実装

最近Androidでゲームアプリを作成してて、カウントダウンタイマーを実装する時にちょっと詰まったのでメモ。

/**
 * 制限時間を計算
 */
public class CalcTime {
    private final int GAME_TIME = 30;
    private long mNowTime;
    private long mStartTime;

    public CalcTime() {
        this.mNowTime = GAME_TIME;
    }

    // 現在の時間を返す
    public long getNowTime(){
        return mNowTime;
    }
    // カウントダウン開始
    public void startCountDown(){
        mStartTime = System.currentTimeMillis();
    }

    // 現在の時間を計算
    // カウント終了でtrueを返す
    public boolean calc(){
        long current = System.currentTimeMillis();
        long time_gone = (current - mStartTime) / 1000;
        if(time_gone >= 30){
            mNowTime = 0;
        }else {
            mNowTime = GAME_TIME - time_gone;
        }
        if(mNowTime == 0) {
            return true;
        }
        return false;
    }
}

startCountDown()で開始時間をセットして毎フレームcalc()で計算、getNowTime()で値を取得して表示すればOK。
↓表示例(SurfaceViewを継承したクラスで実装)

private CalcTime mCalcTime = new CalcTime();
private Boolean mClicked = false;
 @Override
    public void run() {
        while (thread != null){
           if(mClicked){
             mCalcTime.calc();
           }
           this.onDraw(getHolder());
        }
    }
    private void onDraw(SurfaceHolder holder){
        Canvas c = holder.lockCanvas();
        if(c == null){
            return;
        }
         // 1の位
        int ones_time = (int)mCalcTime.getNowTime() % 10;
        // 10の位
        int tens_time = (int)mCalcTime.getNowTime() / 10;
        c.drawText(tens_time,0,0,paint);
        c.drawText(ones_time,0,FONT_SIZE,paint);

        holder.unlockCanvasAndPost(c);
    }
 @Override
   public boolean onTouchEvent(MotionEvent event) {
       if(event.getAction() == ACTION_DOWN){
          mClicked = true;
          mCalcTime.startCountDown();
       }
       return super.onTouchEvent(event);
   }

pt3で録画失敗

pt3を使って録画サーバーを立てて稼働している。

割と問題なく動いていたのだが気づいたら一週間ぐらい前から録画に失敗していた。
予約ソフトはepgrecを使用しているのだが、番組表の取得に失敗しまくっていた。

でなんか色々調べてみたらpt3のドライバが全く入っていなかった。
なんでかはわからないが、色々更新だのなんだのすると消えちゃうことがあるらしい。
ドライバ入れなおして解決。

解決まではそうむずかしいことではないが、気づくまで録画は失敗し続ける。
録画出来なかった番組は二度と帰ってこないのである。 

「rails server」実行時に「NoMethodError」がでた場合の対処法

Ruby on Rails Tutorial をやっていると結構環境によって躓くことがあるのでメモっておく。

 第1章で
$ rails server 
=> Booting WEBrick
=> Rails application starting on http://0.0.0.0:3000
=> Call with -d to detach 
=> Ctrl-C to shutdown server 
自分の環境で上記を実行すると下記のようなエラーが発生した。
/home/***/.rbenv/versions/2.0.0-p451/lib/ruby/gems/2.0.0/gems/railties-4.0.5/lib/rails/railtie/configurable.rb:30:in `method_missing': undefined method `application' for #<FirstApp::Application:0x0000060132cd30> (NoMethodError) 
対処法としては
$ vim ~/rails_project/firlst_app/config/environments/development.rb
# ~/rails_project/firlst_app/config/environments/development.rb
# src
1 Rails.application.configure do
# dest
1 FirstApp::Application.configure do 
上記のような変更を加えればOK。

次にhttp://localhost:3000に接続すると
DEPRECATION WARNING: You didn't set config.secret_key_base. Read the upgrade documentation to learn more about this new config option. (called from service at /home/YAMAMOTO_HI/.rbenv/versions/2.0.0-p451/lib/ruby/2.0.0/webrick/httpserver.rb:138)
[2014-07-02 15:33:05] ERROR RuntimeError: You must set config.secret_key_base in your app's config.
こんな感じのエラーがでるので
$ rake secret 
して、出てきた文字列をコピー 
$ vim ~/rails_projects/first_app/config/initializers/secret_token.rb
#  ~/rails_projects/first_app/config/initializers/secret_token.rb
FirstApp::Application.config.secret_key_base = '***' ←コピーした文字列に置き換える
 以上

LinuxでICカードリーダが認識されないときの対処法

CentOSサーバーでEpgrecを使って録画をしていたのだが、何故か一週間くらい録画されていなかった。
Epgrecの録画自体は成功しているのだが画面に何も映らない。
特に設定など弄っていないので原因がわからなかったがふとカードリーダを見たらランプが点滅していない。(点灯状態だと認識されていない)

カードもカードリーダも他のPCやTVに繋いだところ問題ないようだが、録画サーバに繋いだ時は

SCardEstablishContext: Service not available
だったので色々調べてみると、まずHALデーモンが起動しておらず、起動させようとしたら起動しなかった。
何故かmessagebusが起動していなかった模様。

[root@****]# service messagebus start
システムロガーを起動中:                                    [  OK  ]
[root@****]# service haldaemon start
HAL デーモンを起動中:                                      [  OK  ]
[root@****]# service pcscd restart
PC/SC スマートカードデーモン (pcscd) を停止中:             [失敗]
PC/SC スマートカードデーモン (pcscd) を起動中:             [  OK  ]

これで直った。 

ポートフォワーディングについて

サーバーマシン側でWEBサーバーやSSHサーバーを起動して外部から接続をするためにポートフォワーディングを試みた。

ポートフォワーディングってのはつまりポート開放のこと。ルーターは基本的に全てのポートを閉めて外部からのアクセスを止めてるので、一部を開けて(webサーバーなら80) 外部からアクセス出来るようにするということだ。

今回は最初にwebサーバーようにTCP80を開けてapacheのデフォルトページが表示されればOK。
手順としては (バッファロールーターの場合)
以上。
ポート開放なんてそんなに難しいものではないのでさくっと終わるかとおもいきや…80に接続出来ない!
色々やってみたけどやっぱり出来ない。ふと気づいてtracertでグーグルまでの経路を調べてみると…。
192.168...
192.168...
に、二重ルーター
今までずっと二重ルーターになってたんですねー。これはいかん。
バッファロー製の無線付きルーターを使ってたのだが、NTTのひかり電話用のルーターも有効化されていた。
これではいくらバッファローのほうのポートを開けてもNTTがブロックしてしまう。
ということでNTTのほうをブリッジモードにして解決。