Go の標準っぽいプレゼンを作る - 完全版
バックアップ開発部の y-okubo と申します。
今回は、以下の記事で書いた内容の(ほぼ)完全版を書きます。
概要
- Go 界隈の方々がよく使っているプレゼンツール(フォーマット)です
- とは言えど、このツールを使っていないプレゼンもいっぱいあります
- 当然ですが Go で書かれています
- シンタックスハイライトは美しいですが、フォーマットの指定方法が特殊です
インストール
GOPATH を設定した上でインストールします
$ go get golang.org/x/tools/cmd/present
サーバ起動
作成するスライドを格納するディレクトリに移動してから実行します
$ present
デフォルトでは http://127.0.0.1:3999 にアクセスすることでプレゼンが見られるようになります。
フォーマット
プレゼンのメタ情報
Title of document Subtitle of document 15:04 2 Jan 2016 Tags: foo, bar, baz Author Name Job title, Company joe@example.com http://url/ @twitter_name * Title of slide or section (must have asterisk)
最低限、この内容(アスタリスクの部分)までないと表示できません(以外とハマります、コレ)。
- 一行目はプレゼンのタイトルです
- 二行目はプレゼンのサブタイトルです
- 三行目はプレゼンの発表日時?
- 四行目はタグ?ですかね(どこで役に立つのかは不明)
- 空行を挟んで個人プロフィール各種(名前・役職・会社名・メールアドレス等々)
- さらに空行を挟んで次のスライドタイトルになります
箇条書きとセクション
- bullets 1 - more bullets 1 - a bullet with 1
* Slide title or section ** Sub section *** More sub section
サブセクション以降はプレゼンの下から順に表示されます。
例えば
* Title of slide or section (must have asterisk) ** Subsection1 - bullets 2 - more bullets 2 - a bullet with 2 *** Subsubsection2 - bullets 1 - more bullets 1 - a bullet with 1
これは
こんな感じで表示されます(正直ちょっと違和感があります)。
テキスト
* Text Some More text Preformatted text is indented (however you like)
書いた内容がそのまま反映されます。
インデントを行うとその部分が整形済みテキスト(pre タグ)になります。
フォント
* Font _italic_ *bold* `program` _this_is_all_italic_ _Why_use_scoped__ptr_? Use plain ***ptr* instead.
上から順番に * イタリック * ボールド * プログラム? * イタリック * ボールド(先頭のアスタリスクもボールドにしたいのでエスケープしてる?)
こんな感じで表示されます。
インラインリンク
* Inline link [[url][label]], or [[url]]
ラベルを付けるか、そのままリンクを表示するかの違いですね。
リンク
* Link .link http://golang.org golang.org
Playground で直接実行可能な状態にする
* The Go Playground .play demo.go
こんな UI が付きます。
コード
コードは別ファイルの置く必要があります。
* Code .code -numbers test.go /START OMIT/,/END OMIT/
/開始箇所/,/終了箇所/
で表示するコードを指定します。
なお、正規表現が使えます。
イメージ
* Images .image gopher.png 408 300
高さと幅を指定できます。
公開する
http://go-talks.appspot.com で 動的に表示できます。
http://go-talks.appspot.com/github.com/owner/project/file.ext
という形式で指定すれば、作成したプレゼンが表示されます。
GitHub URL 途中の blob
や master
は 取り除いてください。
こちらは表示例です。
http://go-talks.appspot.com/github.com/y-okubo/go-present-example/sample.slide
まとめ
いかがでしょうか。
かなりクセのあるフォーマットで書きやすいとは言えないのですが、お、できるな!と思わせるプレゼンが作れるので、 Go のコミュニティで発表する予定の資料は present で、それ以外では普段お使いのプレゼンソフトを使われのが良いかと思います。
さらに詳細なフォーマットについては present の GoDoc をご覧ください。
お知らせ
ねこじゃらしでは Go に限らず Ruby や JavaScript のプログラマ、UI/UX デザイナを募集しております。
ご興味をお持ちいただけましたら、以下のリンクからお問い合わせください。
GlusterFS + Go の開発環境を Docker で構築
バックアップ開発部の y-okubo と申します。
今回は「gogfapi を使った簡単な API Server」を作るにあたり、Docker を使って Go の開発環境を構築した手順を紹介させていただきます。
「gogfapi を使った〜」は弊社が毎年行っている開発合宿のお題として選んだ物で(社内的にはもうちょっとクールなお題にしています)、そちらは別の機会に紹介したいと思います。
構成
開発環境なのでデータの永続化は考慮していません。
サーバ
当初は DockerHub でイメージを探したのですが、イメージを生成すると GlusterFS のインストールで失敗する事が多いので自作することにしました。
Dockerfile はこんな感じです(途中の root:password
はとても意識が低いので適宜書き換えてご利用ください)。
FROM centos ENV container docker # Install require packages RUN yum --setopt=tsflags=nodocs -y update RUN yum --setopt=tsflags=nodocs -y install wget nfs-utils openssh-server vim RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs # Added to enable systemd. Reference: http://developerblog.redhat.com/2014/05/05/running-systemd-within-docker-container/ RUN yum -y update; yum clean all; \ (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \ rm -f /lib/systemd/system/multi-user.target.wants/*;\ rm -f /etc/systemd/system/*.wants/*;\ rm -f /lib/systemd/system/local-fs.target.wants/*; \ rm -f /lib/systemd/system/sockets.target.wants/*udev*; \ rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \ rm -f /lib/systemd/system/basic.target.wants/*;\ rm -f /lib/systemd/system/anaconda.target.wants/*; # Add epel repository RUN wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-6.noarch.rpm RUN rpm -ivh epel-release-7-6.noarch.rpm # Install GlusterFS packages RUN wget http://download.gluster.org/pub/gluster/glusterfs/3.7/LATEST/EPEL.repo/glusterfs-epel.repo -O /etc/yum.repos.d/glusterfs-epel.repo RUN yum -y update RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install glusterfs glusterfs-server glusterfs-fuse glusterfs-geo-replication glusterfs-cli glusterfs-api glusterfs-api-devel glusterfs-devel RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install attr iputils iproute RUN yum clean all RUN echo 'root:password' | chpasswd VOLUME [ “/sys/fs/cgroup” ] EXPOSE 111 245 443 24007 2049 8080 6010 6011 6012 38465 38466 38468 38469 49152 49153 49154 49156 49157 49158 49159 49160 49161 49162 RUN systemctl disable nfs-server.service RUN systemctl enable rpcbind.service RUN systemctl enable sshd.service RUN systemctl enable glusterd.service CMD ["/usr/sbin/init"] COPY ./startup.sh /var/startup.sh RUN chmod +x /var/startup.sh RUN echo /var/startup.sh >> /root/.bashrc
ポイントは
- EPEL レポジトリを追加
startup.sh
を実行
しているところでしょうか。
GlusterFS のインストールに失敗していたのが yum install
あたりだったので、自分で EPEL レポジトリを追加しています。
startup.sh
の内容についてはこの後に説明します。
その他の内容は GlusterFS official docker image を参考にしています。
startup.sh
の内容は以下になります。
#!/bin/bash # Create volume mkdir /var/gfs_srv_vol gluster peer probe $HOSTNAME gluster volume create gfs_volume $HOSTNAME:/var/gfs_srv_vol force gluster volume start gfs_volume gluster volume quota gfs_volume enable # Mount volume mkdir /var/gfs_cli_vol mount -t glusterfs -o acl $HOSTNAME:/gfs_volume /var/gfs_cli_vol
こちらでは GlusterFS のボリューム作成とローカルマウントを行っています。
Dockerfile の中の RUN
で実行したかったのですが、$HOSTNAME
環境変数を使いたかったのでシェルスクリプトで実行するようにしてあります。
クライアント側
サーバ側とほぼ一緒の Dockerfile になります(こちらも途中の root:password
はとても意識が低いので適宜書き換えてご利用ください)。
FROM centos ENV container docker # Install require packages RUN yum --setopt=tsflags=nodocs -y update RUN yum --setopt=tsflags=nodocs -y install wget nfs-utils openssh-server vim RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs # Added to enable systemd. Reference: http://developerblog.redhat.com/2014/05/05/running-systemd-within-docker-container/ RUN yum -y update; yum clean all; \ (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \ rm -f /lib/systemd/system/multi-user.target.wants/*;\ rm -f /etc/systemd/system/*.wants/*;\ rm -f /lib/systemd/system/local-fs.target.wants/*; \ rm -f /lib/systemd/system/sockets.target.wants/*udev*; \ rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \ rm -f /lib/systemd/system/basic.target.wants/*;\ rm -f /lib/systemd/system/anaconda.target.wants/*; # Add epel repository RUN wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-6.noarch.rpm RUN rpm -ivh epel-release-7-6.noarch.rpm # Install GlusterFS packages RUN wget http://download.gluster.org/pub/gluster/glusterfs/3.7/LATEST/EPEL.repo/glusterfs-epel.repo -O /etc/yum.repos.d/glusterfs-epel.repo RUN yum -y update RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install glusterfs glusterfs-fuse glusterfs-cli glusterfs-api glusterfs-api-devel glusterfs-devel RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install attr iputils iproute RUN yum clean all RUN echo 'root:password' | chpasswd VOLUME [ “/sys/fs/cgroup” ] EXPOSE 111 245 443 24007 2049 8080 6010 6011 6012 38465 38466 38468 38469 49152 49153 49154 49156 49157 49158 49159 49160 49161 49162 RUN systemctl disable nfs-server.service RUN systemctl enable rpcbind.service RUN systemctl enable sshd.service CMD ["/usr/sbin/init"] RUN yum --setopt=tsflags=nodocs -y groupinstall "Development Tools" RUN wget https://storage.googleapis.com/golang/go1.6.2.linux-amd64.tar.gz RUN tar -C /usr/local -xzf go1.6.2.linux-amd64.tar.gz COPY ./hello.c /root/hello.c RUN mkdir -p /root/go/bin \ && echo 'export GOROOT=/usr/local/go' >> /root/.bashrc \ && echo 'export GOPATH=/go/path' >> /root/.bashrc \ && echo 'export GOBIN=/go/bin' >> /root/.bashrc \ && echo 'export PATH=$PATH:$GOROOT/bin:$GOBIN' >> /root/.bashrc
こちらは最後の方で Go 開発環境のインストールと設定を行っています。
Go のソースコードはローカルマシン側に置いて、コンテナ起動時にマウントして参照できるようにしています。
イメージ生成
サーバ側
$ docker build -t glusterfs-server server
クライアント側
$ docker build -t glusterfs-client client
コンテナ起動
サーバ側
$ docker run --privileged -tid -p 20022:22 --hostname gfserver --name gfserver glusterfs-server
--privileged
で権限を与えて SELinux 等の影響を受けないようにしています。
クライアント側
$ docker run --privileged -tid -p 20023:22 -p 8080:8080 -v /Users/y-okubo:/go/path --link gfserver:server --hostname gfclient --name gfclient glusterfs-client
--link
で先ほど起動したコンテナを参照できるようにしてあります。
コンテナへ SSH ログイン
サーバ側
$ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 20022 root@localhost
クライアント側
$ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 20023 root@localhost
セキュリティ上よろしくないのですが、毎回 OpenSSH の警告が出るのを抑止しています。
クライアント→サーバの疎通確認(libgfapi 経由でのアクセス)
クライアント側に SSH ログインして、イメージ作成時に用意(Dockerfile にて指定)した hello.c
をビルド・実行します。
hello.c
のソースは以下になります。
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <glusterfs/api/glfs.h> int main (int argc, char** argv) { const char *gfserver = "server"; const char *gfvol = "gfs_volume"; int ret; glfs_t *fs; glfs_fd_t *fd; fs = glfs_new(gfvol); // Virtual filesystem type struct glfs_set_volfile_server (fs, "tcp", gfserver, 0); ret = glfs_init (fs); if (ret) { printf( "Failed to connect server/volume: %s/%s\n", gfserver, gfvol ); exit(ret); } char *greet = "Hello, Gluster!\n"; fd = glfs_creat(fs, "greeting.txt", O_RDWR, 0644); glfs_write(fd, greet, strlen(greet), 0); glfs_close(fd); return 0; }
$ gcc hello.c -lgfapi
$ ./a.out
まとめ
今回は GlusterFS の Go での開発環境を Docker で構築した際の流れを駆け足でご紹介しました。
今回はサーバ側でローカルマウントするようにしていますが、クライアント側でサーバ側のボリュームを FUSE マウントすれば別の用途でも使えるのではないかと考えています。
GlusterFS + Go という組み合わせにどれくらいの需要があるかは分かりませんが、何かのお役に立てれば幸いです。
お知らせ
ねこじゃらしでは Go に限らず Ruby や JavaScript のプログラマ、UI/UX デザイナを募集しております。
ご興味をお持ちいただけましたら、以下のリンクからお問い合わせください。
既存のiOSプロジェクトにUI自動テストを実装したの話
実装したアプリはWebAPIを使用したストレージアプリで、取得してきたデータをTableView(またはCollectionView)で表示する画面が複数あります。
開発人数は2名で別に品質管理チームがおり、品質管理チームにテストして貰う前に、UI自動テストを行うという目的です。
環境
- Xcode version 7.x
- KIF version 3.3.0
kif-framework/KIF: Keep It Functional - An iOS Functional Testing Framework
どこまでUIテストを行うか?
自動UIテストの範囲は広くどこまでやるかが明確ではないため、最終的に組み込まれたものと、そうではないものに分けて記載します。
自動テストしたこと
- シェルスクリプトを起動するだけすべてのUIテストが完了するようにする
- デバイスごと動作することを確認する(iPhoneとiPad)
- バージョンごとに動作することを確認する(iOS9とiOS8)
- ユーザー名、パスワードを入力してログインできる
- すべてのボタンが表示されていることを確認する
- すべてのボタンが動作されていることを確認する
- 検索が動作されていることを確認する
- アラートを表示は確認する
- TableViewに表示されている文字を確認する
- テスト中にスクリーンショットを保存する
複数デバイスで起動するためはxcodebuild
をシェルスクリプトで起動するようにする必要がありました。
UIテストは開発者視認できないほど早く動作し目視の確認が難しいため、テスト時にスクリーンショットを保存するのは有効です。 またスクリーンショットをフォルダ別にデバイス名、iOSバージョン、アプリバージョンを記載することで、バージョンごとの画面ログとして効果があります。
すべてのUIテストはファイルごと独立して動くようになっています、これはKIFでUIテストの順番を指定できないためです。
例えばLoginUITest.swift
とLogoutUITest.swift
があるときにLoginUITest.swift
が先にテストされるとは限りません。
そのためLoginUITest.swift
のはじめにログアウト処理、LogoutUITest.swift
がのはじめにログイン処理を入れてあります
自動テストしなかったこと
- ネットワークエラー時
- 画面数xネットワークエラー数は数が多くなりすぎる上にエラーテストとUIテストを同時にやるのは難しい
- ログイン時のユーザー名、パスワード入力のエラーケース
- 上記と同じくエラーケースが多いので、ほとんどやらなかった
- ユーザーが開発者が想定外の操作をするケース
- ボタンに対して連続タップをする、高速でスワイプをするなどのケース
- 操作後に正常であることを示すテストが難しかったため
- ボタンに対して連続タップをする、高速でスワイプをするなどのケース
例外処理時のテストを実装するにはかなり時間がかかり、ほとんど実装はしませんでした。
KIFで発生した問題
日本語のキーボードは無効にしておく
- 試験をしているとシミュレーターの入力モードが日本語になってしまうことがあったので、日本語入力にならないように設定を変更しました。
- シミュレーターのiOSのSettings > General > Keyboard > で Japanese-Kana を削除
連絡帳へのアクセスを許可しますか?
などのシステムアラートは検知できないことがあるtester.acknowledgeSystemAlert()
が動作しないことがあります- 解決法が無いですが、予めシステムの許可を設定するか、目視でタップする用にしていました
UIテストの起動に失敗する
エラーログも出ないことがあるxcodeを再起動することで、起動できたりもします
表示されているUIが検知できないでテストが失敗する
- XCTestでも発生する、現状で有効な回避方法はないと思います
実装後の感想
- 今のところスマホのUIテストにベストプラクティはないので、手探りでやるしかない
- 状態遷移が多いアプリはテスト作成に非常に時間がかかる
Unitテストと違い、UIテストを必須にすることは非常に難しい
メリット