emulator: ERROR: This AVD's configuration is missing a kernel file!
エミュレータネタ連発。上記エラーが発生した時に確認することを順にメモ。
エラーメッセージ全体
Cannot launch AVD in emulator. Output: emulator: ERROR: This AVD's configuration is missing a kernel file! Please ensure the file "kernal-qemu" is in the same location as your system image. emulator: ERROR: ANDROID_SDK_ROOT is defined (/Users/woshidan/Library/Android/sdk) but cannot find kernel file in (/Users/woshidan/Library/Android/sdk/system-images/) sub directories
発生したエミュレータのAPIバージョンとABIとどこ製か
一部のエミュ(主にARM系)のエミュにはGoogle社製のものとAndroid Open Source Projectによるものがあって、自分の手元で発生したのは
で発生。
kernal-qemuとは
QEMU(仮想マシンエミュレータ)のハード的なところとソフト的なところをつないでくれるソフト。カーネル。 どうも、Androidのエミュレータは、QEMUを利用してARM系のエミュをx86などのCPUのPC上で動かしているそうです。
今回のエラーはエミュレータ作成の時に指定したターゲットに対して、このファイルがないと言われています。
実はでもなく、システムイメージが何かよくわかってないので用語や文末がふわふわですが、エミュレータに指定できるターゲットに相当するディレクトリがありまして、そのディレクトリの中に kernal-qemu
がないと言われていることになります。
発生した場合に確認すること
- SDK Manager上で該当システムイメージがインストールされているか
- 一度エミュレータを作成した後にシステムイメージが壊れていたり、Android StudioのAndroid SDKのパスの設定がおかしくて認識されてなかったりするかも?
- システムイメージが入っているディレクトリを開いてみて
kernal-qemu
があるか
実際に当該ディレクトリを覗いてみた例
// エラーが出ない $ ls /Users/woshidan/Library/Android/sdk/system-images/android-15/google_apis/x86 NOTICE.txt kernel-ranchu source.properties build.prop package.xml system.img kernel-qemu ramdisk.img userdata.img // エラーが出る $ ls /Users/woshidan/Library/Android/sdk/add-ons/addon-google_apis-google-16-1/images/armeabi-v7a NOTICE.txt build.prop ramdisk.img system.img userdata.img
どうするか
本来はないと言われているkernel-qemu
を調達できればいいのですが、いまのところ、Linuxよくわかってないし、方法不明*1。
もしかすると、SDK Managerをアップデートした後、システムイメージをアンインストールして再インストールすると直るかもしれないですが、最新バージョンのシステムイメージが欲しい場合などを除き、Android Open Source Projectの方のディレクトリには kernel-qemu
が含まれていたため、未検証です。
また、自分がこの不具合に遭遇したARM系というのはたいていの実機が該当するので、実機で検証するか実機が欲しいと交渉した方が早いかもしれません。
現場からは以上です。
*1:誰かわかったら教えてください
ERROR: resizing partition e2fsck failed with exit code 8
Androidのエミュレータの設定をいじっている最中、上図のエラーダイアローグを稀によく見かけていたような気がするのですがいままで原因がわからないうちになんか回避してしまっていたのでした。その原因と対応がなんとなくわかったのでメモ。
Androidのエミュレータの設定やデータは /Users/${username}/.android/avd
ディレクトリの中にそれぞれのエミュレータ専用の設定ファイル(.ini
)と設定ファイルやデータをしまうためのディレクトリ(.avd
)が用意されています。
avd$ pwd /Users/woshidan/.android/avd avd$ ls 5.1_WVGA_API_21.avd 5.1_WVGA_API_21.ini API15.avd API15.ini ...
個々のエミュレータ用の設定やデータが入っている.avd
の中身は以下となっています。
API15.avd$ ls cache.img hardware-qemu.ini userdata.img config.ini sdcard.img emulator-user.ini userdata-qemu.img
件のエラーはこの中のデータなどが入っているファイルシステムである .img
ファイルの形式がおかしいため起こるようです。これは、エミュレータの設定をいじってから再起動などをしていると起こることがあります。
エラーメッセージ中にあるe2fsck
コマンドについてはこちらによると、
ext2/ext3/ext4ファイルシステムの整合性をチェックし、修復する。マウント中のファイルシステムに対してはこのコマンドを実行しないようにする(修復をかけるとファイルシステムが壊れる恐れがある)。
なので、このコマンドがうまく走ってくれたらファイル形式の問題は解決しそうです。
また、一部の .img
ファイルは起動時に存在しなければ作り直されるので、中に入っているデータに未練がなければ消して作り直してみてもよさそうです。エミュだし。ということで、
- 手動で
e2fsck
を各ファイルにかけてみる cache.img
,userdata-qemu.img
を削除する- エミュレータを作り直す
などで解決しそうですというか、私は2つ目と3つ目で解決した後StackOverflowで一つ目の方法を見つけたのでした。
現場からは以上です。
AppiumでiOSのテストのサンプルを動かすところまで
iOSやAndroidのUIテストをSeleniumのように書くことができるAppiumというものがあるそうですが、 説明を見ていてもさっぱり挙動がわからなかったので、動かしてみたメモです。
各種のバージョンは
- Appium 1.5.3
- maevn 3.3.9
です。スクショにAviraが映っていますが、昨日のAvast誤検知祭りの名残です。
DLしてきたAppiumを起動
Appium: Mobile App Automation Made Awesome.からAppiumをDLしてきて起動してみます。
テストが実行される様子
今回は
のサンプルコードを実行してみます。
また、サンプルコードの実行にはmaevnが必要なので事前にDLしておきます。
iOSのシミュレータが起動。
デフォルトではテスト終了後iOSのシミュレータはシャットダウンされます。
参考
iOSのidentifierForVendorとadvertisingIdentifierについて
お仕事で調べる必要があったのでメモ。
identifierForVendor
https://developer.apple.com/reference/uikit/uidevice/1620059-identifierforvendor?language=objc
- アプリのベンダー(開発元)ごとにデバイスが一意に見えるように発行されるアルファベットと数字からなる文字列
- もう少し具体的に言うと、アプリのベンダーを識別するためにvendor IDというものを発行していて、vendor IDに対して一意
- nilになるような場合
- しばらく待ってから再び値を取得し直す
- どういう事例があるかと言うと、ユーザーがアンロックしているのに、デバイスが再起動しているとき(ユーザーがアンロックしている状態で起動したとき?)
- 同じベンダーのアプリを全部アンインストールしてから再インストールすると値が変わる
- 同じベンダーのアプリが1つでも残っていたら値が変わらない
ASIdentifierManager
https://developer.apple.com/reference/adsupport/asidentifiermanager
- 広告を提供する専用のIDへのアクセスを提供
- これはユーザーが広告トラッキングを制限しているかどうかを示すフラグ
- このクラスは広告を提供するシステムを実装する開発者によって利用されることを想定
- 一般のアプリ開発者は、彼らが開発する広告用SDKとのリンクのためであっても、このクラスに直接メッセージを送る必要はない
- 広告IDの取得方法
advertisingIdentifier
https://developer.apple.com/reference/adsupport/asidentifiermanager/1614151-advertisingidentifier
- アルファベットの文字列でデバイス毎に一意になるように発行される数字の列
- UIDeviceから取得できるidfvとは異なり、この値はすべてのベンダーのアプリ内で同一の値を返す
- このIDは変化するかもしれないので、キャッシュしてはいけない
- たとえば、ユーザーがデバイス(との連携?)を消したなどで
- 広告トラッキングが制限されている場合、iOS10.0かそれ以降ですべて0の文字列になっている
- 値がnilの場合
- この値は再取得される
- これが起きる理由としてはユーザーがアンロックしているのに、デバイスが再起動しているとき(ユーザーがアンロックしている状態で起動したとき?) (IFDVと同様)
isAdvertisingTrackingEnabled
後からコード上でBitmapを与える場合、ImageViewのadjustViewBounds=trueの指定が動かない
結論
onMeasureで一部人間にはよく分からない動きをしている気がします。
- ImageViewの
android:adjustViewBounds
属性がtrueで、ImageViewの幅などを直接指定していない場合- ImageViewを許される限り拡大した場合の高さと幅を求める(widthSize/heightSize)
- 上記の高さ or 幅を適用したとして、他辺の長さを求める(newWidth/newHeight= widthSize * 縦横比の場合 / heightSize / 縦横比)*1
- 下記の最小値 >=newWidth/newHeightの場合、ImageViewのリサイズが行われる
- DrawableのサイズとPaddingの合計値(in pixel)
- 親Viewから許容されている値(in pixel)
android:minHeight
あるいはandroid:minWidth
で指定している値(in pixel)
- 上記の最小値 < newWidth/newHeightの場合、ImageViewのリサイズが行われない
- 現状のImageViewとDrawableのアスペクト比がほぼ同じ場合もonMeasureでリサイズしない
コード読み
主に関係あるのはImageViewのonMeasureメソッドなのでこれを読んでいきます。
また、下記のケースは親Viewから具体的にImageViewの大きさが制限されていない*2場合のケースとなります。
android:adjustViewBounds="true"
の場合、まずDrawableのサイズを取得します。
w = mDrawableWidth; h = mDrawableHeight; if (w <= 0) w = 1; if (h <= 0) h = 1; // We are supposed to adjust view bounds to match the aspect // ratio of our drawable. See if that is possible. if (mAdjustViewBounds) { resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; desiredAspect = (float) w / (float) h; }
そのあとにPadding
の値を取得します。
int pleft = mPaddingLeft; int pright = mPaddingRight; int ptop = mPaddingTop; int pbottom = mPaddingBottom;
そして、現在の条件でImageViewを拡大していい最大サイズの計算をします。
if (resizeWidth || resizeHeight) { /* If we get here, it means we want to resize to match the drawables aspect ratio, and we have the freedom to change at least one dimension. */ // Get the max possible width given our constraints widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec); // Get the max possible height given our constraints heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);
次に、縦か横をこのどちらかの大きさにあわせた場合のもう一辺の長さを計算します。
// 横方向にリサイズしていい場合は、まず縦の長さを目一杯撮った場合の横幅(newWidth)の計算をします // Try adjusting width to be proportional to height if (resizeWidth) { int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; // Allow the width to outgrow its original estimate if height is fixed. if (!resizeHeight && !mAdjustViewBoundsCompat) { widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); }
新しく計算した長さが、先ほど計算したImageViewを拡大していい最大サイズ以下であればリサイズをします。
if (newWidth <= widthSize) { widthSize = newWidth; done = true; }
横方向にリサイズできなかった場合は、縦方向のリサイズを試みます。
resolveAdjustedSize
の中身ですが、MeasureSpec
の値が、MeasureSpec. AT_MOST
の場合、
- DrawableのサイズとPaddingの合計値(in pixel)
- 親Viewから許容されている値(in pixel)
android:minHeight
あるいはandroid:minWidth
で指定している値(in pixel)
の最小値となっています。
private int resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec) { int result = desiredSize; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: /* Parent says we can be as big as we want. Just don't be larger than max size imposed on ourselves. */ result = Math.min(desiredSize, maxSize); break; case MeasureSpec.AT_MOST: // Parent says we can be as big as we want, up to specSize. // Don't be larger than specSize, and don't be larger than // the max size imposed on ourselves. result = Math.min(Math.min(desiredSize, specSize), maxSize); break; case MeasureSpec.EXACTLY: // No choice. Do what we are told. result = specSize; break; } return result; }
現場からは以上です。
*1:少し大雑把なので、正確にはこの辺 https://github.com/android/platform_frameworks_base/blob/marshmallow-release/core/java/android/widget/ImageView.java#L1010
*2:親Viewのandroid:layout_widthがwrap_contentなどで、MeasureSpec.getModeでMeasureSpec.AT_MOSTが返ってくる場合
RubyとrbenvとgemとBundlerと環境ごとのgem
いまさらですが、こないだ会社でRailsに携わるもののたしなみとしてnokogiriのインストールにはまったときに先輩に教えていただいたときのメモ。
- rbenvでRubyのバージョンを管理している場合、local*1やglobal*2を管理している際、systemと表記されているのはrbenvを介さずにPCにインストールされているRubyを指す
- rbenvないし、systemと表される開発者のPCにインストールされたRubyに対する形でgemをインストールしていく
- Bundlerはカレントディレクトリに置かれた
Gemfile
の内容に基づいて自身以外のgemの依存関係を管理するgemである.
あわせてよみたい参考記事。
RailsのModelのvalidatesメソッドを見て単純なバリデーションテストを生成するgemを書きました
表題の通り、RailsのModelのvalidatesメソッドを見て単純なバリデーションテストを生成するgemを書きました。
書いた理由
RailsのModelの基本的なバリデーションのテストは時々、Rails Tutorialやドキュメントのコピペと何が違うのだろう、これはActiveRecordの方のテストになるから書いてないよという話もある一方、書かないと心配で結局手動でテストしてしまうよね...と虚しくなってしまう時があります。
結局心配で手動でテストをするならば、黙って書けばよい気がしますが、自動テスト化の流れが遅れているというモバイルであっても、こういったサービスが出てこようとする中、自分はこの手のテストに時間を使ったり満足していたりしていていいのか、少しもやもやしました。
そうして、どうせいつか誰かに自動化されるのだったら、たまにはフレームワークから離れて、コピペ元のテンプレートを用意したらあとは自動的にてきとうなテストコードを出してくれるライブラリを書いて、自分で半自動化してみようか、ということで書きました。
まぁ、実際に使うにはテンプレートをもっと精査する必要があるのですが...。
使い方
class Sample < ActiveRecord::Base validates :name, length: { maximum: 32, minimum: 6 }, presence: true validates :id, numericality: { only_integer: true, greater_than: 0 } # ... end
irb(main):001:0> Tamebou::Writer.new("../path/to/model/sample.rb").write ============================================= class TestSample < MiniTest::Unit::TestCase def setup @sample = build(:sample) end def teardown # you can write some clean up code in this method end def test_valid_name_values_in_terms_of_length expected_values = ["aaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] expected_values.each do |expected_value| @sample.name = expected_value assert @sample.valid? end end def test_invalid_name_values_in_terms_of_length invalid_values = ["aaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] invalid_values.each do |invalid_value| @sample.name = invalid_value assert_not @sample.valid? end end end ============================================= ============================================= class TestSample < MiniTest::Unit::TestCase def setup @sample = build(:sample) end def teardown # you can some clean up code in this method end def test_valid_name_values_in_terms_of_presence expected_values = ["presence"] expected_values.each do |expected_value| @sample.name = expected_value assert @sample.valid? end end def test_invalid_name_values_in_terms_of_presence invalid_values = [nil] invalid_values.each do |invalid_value| @sample.name = invalid_value assert_not @sample.valid? end end end ============================================= ============================================= class TestSample < MiniTest::Unit::TestCase def setup @sample = build(:sample) end def teardown # you can some clean up code in this method end def test_valid_id_values_in_terms_of_numericality expected_values = [2, 1] expected_values.each do |expected_value| @sample.id = expected_value assert @sample.valid? end end def test_invalid_id_values_in_terms_of_numericality invalid_values = ["1.1", 2.0, 0] invalid_values.each do |invalid_value| @sample.id = invalid_value assert_not @sample.valid? end end end ============================================= => #<File:../path/to/model/sample.rb (closed)>
デフォルトはMinitest
のコードが出てくる設定なので、RSpec
(らしい)コードを出したい場合:
Tamebou::Writer.new("../path/to/model/sample.rb", Tamebou::Writer::DefaultTemplate::RSPEC).write
setup
メソッドを書き足しておきたい場合やMinitest
だけどminitest-spec-rails
などを使っているので、自分で用意したテンプレートを追加痛い場合は、
Tamebou::Writer.new("../path/to/model/sample.rb", "/path/to/your_template").write
うまくパースされてないかもしれないからログを出したい場合:
Tamebou::Writer.new("../path/to/model/sample.rb", Tamebou::Writer::DefaultTemplate::RSPEC, true).write
ちなみに、専用のテンプレートが対応していない場合は、
# 現在対応していません validates :username, :format => /\A([^@\s]+)@((?:[a-z0-9]+\.)+[a-z]{2,})\Z/i
============================================= class TestSample < MiniTest::Unit::TestCase def setup @sample = build(:sample) end def teardown # you can some clean up code in this method end def test_valid_username_values_in_terms_of_format expected_values = ["YOU SHOULD ADD EXPECTED VALUES BY YOUR OWN"] expected_values.each do |expected_value| @sample.username = expected_value assert @sample.valid? end end def test_invalid_username_values_in_terms_of_format invalid_values = ["YOU SHOULD ADD UNEXPECTED VALUES BY YOUR OWN"] invalid_values.each do |invalid_value| @sample.username = invalid_value assert_not @sample.valid? end end end =============================================
のような出力が出ます。
中身
正規表現である程度書いて諦めたりしています。
色々プロダクションで動くコードじゃないから、とりあえず1日で作ってみようと手を抜いた箇所が多くてあれです。
参考にさせていただいた記事など
morizyun.github.io qiita.com qiita.com qiita.com qiita.com Rubyist Magazine - 標準添付ライブラリ紹介 【第 10 回】 ERB