Page List

Search on the blog

ラベル 開発環境 の投稿を表示しています。 すべての投稿を表示
ラベル 開発環境 の投稿を表示しています。 すべての投稿を表示

2019年6月29日土曜日

プロジェクトごとの環境変数の管理

まえがき

 複数のプロジェクト開発を担当していると、プロジェクトごとにさまざまな設定を管理しないといけない。

 たとえばプロジェクトごとに aws アカウントが異なる場合は、 profile を作っておいて aws コマンドを実行するときに profile オプションを付ける必要がある。しかし、プロジェクト数が多くなってくると、profile 名何だっけ?となることがある。

 他にもdbの接続情報などをプロジェクトごとに使い分けたかったりする。

direnv

 direnv というツールを使うとディレクトリごとに環境変数を管理できるらしい。.envrc というファイルを作って環境変数を定義すると、そのディレクトリの中でのみ有効になる。

 例として、projectX と projectY というディレクリを作って設定をしてみる。

$ mkdir projectX
$ cd projectX
$ echo "export MY_PWD=hogehoge" >.envrc
$ direnv allow

$ mkdir projectY
$ cd projectY
$ echo "export MY_PWD=fugafuga" >.envrc
$ direnv allow

 ディレクトリごとに環境変数が切り替わっていることを確認してみる。

$ cd projectX
$ echo $MY_PWD
hogehoge

$ cd projectY
$ echo $MY_PWD
fugafuga

これは便利!

2016年4月29日金曜日

デフォルトシェルをzshにしてみた

会社の人がzsh使っているのを見て、bashからzshに変えてみた。
pecoというツールを入れてみたところ、コマンド履歴検索、ディレクトリ検索が圧倒的に楽になった。


やったことをメモしておく。

最新版zshのインストール
デフォルトでzsh入っていたが、新しい方がいいので最新版をインストール。
brew install zsh
echo "/usr/local/bin/zsh" | sudo tee -a /etc/shells 
chsh -s /usr/local/bin/zsh

~/.zshrcの設定
プロンプト、コマンド履歴、lsの色などを設定。
$ cat ~/.zshrc
PROMPT="%F{green}%n$%f "
export LSCOLORS=gxfxcxdxbxegedabagacad
alias ls='ls -G'

export HISTFILE=${HOME}/.zsh_history
export HISTSIZE=1000
export SAVEHIST=100000
setopt hist_ignore_dups
setopt EXTENDED_HISTORY

コマンド履歴検索の強化
pecoを利用した履歴検索の強化。
c-rで検索画面が出てくるようにした。

http://qiita.com/shepabashi/items/f2bc2be37a31df49bca5

ディレクトリ検索の強化
pecoを利用したディレクトリ検索の強化。
c-uで検索画面が出てくるようにした。


gitの補完強化
gitのコマンドやブランチ名の補完ができるようにした。

2016年2月13日土曜日

pyenv-virtualenvでPythonの環境構築

pyenv-virtualenvとは?
virtualenv環境を管理するためのpyenvプラグイン。

pyenvを使うと異なったバージョンをPythonを使い分けることができるが、virtualenvを使うと同じバージョンのPythonで異なる環境(依存するライブラリが異なる環境など)を使い分けることができる。
pyenv-virtualenvはvirtualenvの機能をpyenvから使うことができる便利なプラグイン。

インストール
pyenvはインストール済みであるとする。

Mac OSの場合はhomebrewでインストール。
$ brew install pyenv-virtualenv

インストール後、以下を.bash_profileに追記。
if which pyenv-virtualenv-init > /dev/null; then eval "$(pyenv virtualenv-init -)"; fi

サンプル
シチュエーション
projectXというプロジェクトをPython 3.5.1で開発したい。
システムにインストールされているPythonのバージョンは変えたくない。
pyenvで3.5.1の環境は作れるが別のプロジェクトでも3.5.1を使う可能性があり、依存するライブラリを共有したくない。

解決策
pyenv-virtualenvの出番だ!
$ mkdir projectX
$ cd projectX/
$ pyenv local 3.5.1
$ pyenv version
3.5.1 (set by /Users/kenjih/work/projectX/.python-version)
$ pyenv virtualenv projectX
$ pyenv local projectX 
$ pyenv version
projectX (set by /Users/kenjih/work/projectX/.python-version)

上記のようにpyenv virtualenv projectXでprojectXという名前の環境を作成できる。
仮想環境のPythonのバージョンは環境作成時のpyenv versionのものが使われる。(上の例では3.5.1)
pyenv local projectXとすることで、カレントディレクトリ以下がprojectX環境となり、独立したPythonの環境を構築できる。

作成した環境一覧の表示、作成した環境の削除は以下のコマンドで行う。
$ pyenv virtualenvs
  3.5.1/envs/projectX (created from /Users/kenjih/.pyenv/versions/3.5.1)
  projectX (created from /Users/kenjih/.pyenv/versions/3.5.1)
$ pyenv virtualenv-delete projectX

pyenvでPythonの環境構築

やりたいこと
  • いろいろなバージョンのPythonを使いたい
  • あるアプリはPython2系で、別のアプリはPython3系で開発したい
pyenvを使うと上記のやりたいことを実現できる。

インストール
Macの場合はhomebrewでインストールできる。
brew install pyenv

インストール後、~/.bash_profileに以下を追加する。
if which pyenv > /dev/null; then eval "$(pyenv init -)"; fi

使ってみる
まずはシステムにインストールされているPythonの情報を確認。
$ python -V
Python 2.7.10
$ pip -V
pip 7.1.2 from /usr/local/lib/python2.7/site-packages (python 2.7)

pyenvを使って別のバージョンのPythonを入れてみる。
pyenv installで指定したバージョンのPythonをインストールできる。
新しいバージョンのPythonを入れた時やバイナリを提供するパッケージをインストールしたときはpyenv rehashを実行。
$ pyenv version
system (set by /Users/kenjih/.pyenv/version)
$ pyenv install 3.5.1
$ pyenv rehash
$ pyenv shell 3.5.1
$ pyenv version
3.5.1 (set by PYENV_VERSION environment variable)

Python 3.5.1が入ったっぽい。
現在のPythonのバージョンを確認してみる。
$ python -V
Python 3.5.1
$ pip -V
pip 7.1.2 from /Users/kenjih/.pyenv/versions/3.5.1/lib/python3.5/site-packages (python 3.5)

Pythonのバージョンの設定方法
pyenvは、以下の3つの単位でバージョンを設定できる。

設定単位 設定コマンド 設定が記述される場所
シェルセッション pyenv shell x.x.x 環境変数 PYENV_VERSION
アプリケーション pyenv local x.x.x カレントディレクトリの.python-version
グローバル pyenv global x.x.x ~/.pyenv/version

pyenvは上の設定内容に従って以下のようにPythonのバージョンを決める。
  1. 環境変数PYENV_VERSIONが定義されていればそれを使う
  2. カレントディレクトリに.python-versionファイルがあればそれを使う
  3. カレントディレクトリに.python-versionファイルがあればそれを使う
  4. rootディレクトリまで親を辿っていって.python-versionファイルがあればそれを使う
  5. ~/.pyenv/versionファイルを使う
シンプルで分かりやすい。

アプリケーションごとの設定を試してみる
おそらく最もよく使うのはこの設定かなと思うので試してみた。

$ mkdir py2 && cd py2 && pyenv local 2.7.11 && cd ..
$ mkdir py3 && cd py3 && pyenv local 3.5.1 && cd ..
$ cd py2 && python -V && cd ..
Python 2.7.11
$ cd py3 && python -V && cd ..
Python 3.5.1

これは便利だ。

2015年9月14日月曜日

Gitのdiffのタブ幅設定

Haneda Airport (Midnight)

Gitでdiffしたときにデフォルトのタブ幅が8になっていて見にくかったので、タブ幅を設定した。

以下のコマンドを実行すると、デフォルトのページャがタブ幅4のlessに設定される。
その結果、diffしたときに見やすくなる。

git config --global core.pager 'less -x4'

という、まあそれだけ。

2015年9月1日火曜日

MacBook買った!

 最近MacBookを買った。設定したことを概要レベルでメモしておく。

キーボードまわりの設定
キーのリピート→最速に設定
リピート入力認識までの時間→最短に設定
修飾キーの設定→controlをcommand、optionをcontrol、commandをoptionに設定
メタキーとしてOptionキーを使用→On
Spotlight検索のショートカットキーをalt + spaceに変更(ctrl + spaceはemacsで使いたい)
ファンクションキー→標準のファンクションキーとして使用

パッケージマネージャ
Homebrewをインストール
Homebrew Caskをインストール後、caskroom/cask、caskroom/versionsをtap

プログラミング環境の構築
gcc/g++のインストール→ターミナルにg++と入力するとdeveloper toolsのインストールを促されるのでそこから
java/javac→brew cask install java7
eclipse 公式ページからダウンロードしてapplicationフォルダに格納

前のPCからのデータ移行
Dropboxで同期

Bashの設定
~/.bash_profileを編集

export PS1="\u$ "
export CLICOLOR=1

export LSCOLORS=DxGxcxdxCxegedabagacad

その他
OpenOfficeインストール

2014年3月27日木曜日

EclipseでJavaを書くときに設定しておくべきもの

 Eclipseでワークスペース/プロジェクトを作成したときに、設定すべきものをメモしておく。主にフォントの設定、FindBugsの設定、Checkstyleの設定など。

1. 行数を表示
Window> Preferences> General> Editors> Text Editors
Show line numbersにチェック

2. 空白文字を表示
Window> Preferences> General> Editors> Text Editors
Show whitespace charactersにチェック

3. 等幅フォントにする(windowsなら不要)
Window> Preferences> General> Appearance> Colors and Fonts >Java >Java Editor Text Font
DejaVu Sans Monoを選択

4. FindBugsの設定
Window> Preferences> Java> FindBugs
Minimum rank to report: を20に設定

5. FindBugsの自動実行
プロジェクト右クリック> Properties> FindBugs
FindBugsを自動的に実行をチェック

6. Checkstyleのアクティベート
プロジェクト右クリック> Checkstyle
Activate Checkstyle

7. Checkstyleの設定
Window> Preferences> Checkstyle
Sun Checksをコピーしてチェック設定ファイルを作成する。作成したものをデフォルトに設定。必要に応じて、不要な(too muchな)チェックを外す。

・Class Design - Design For Extension (これを外さないと、拡張されていないメソッドには全部finalかabstractをつけろと言われる)
・Miscellaneous - Final Parameters (これを外さないと、変更されないパラメータには全部finalつけろと言われる)
・Javadoc Comments - Package Javadoc (これを外さないと、package-info.javaを書けと言われる)

2014年2月15日土曜日

スタブとモックの違い

 スタブとモックの違いがようやく分かりました。一言で言うと、
スタブとはテスト対象の間接入力をテストシナリオに合わせて都合よく書き換えるためのもの。モックとはテスト対象の間接出力が正しいことを確認するためのもの。
です[1]。

間接入力、間接出力とはその名のとおりテスト対象の間接的な入力、出力のことです。例えば、サービスクラスをテストする場合を考えてみます。

boolean createService(Entity entity)

というメソッドをテストする場合、メソッドの入力はentity、出力はtrue/falseです。
直接的な入出力はこれだけですが、サービスクラスからDaoの処理を呼び出す場合、その処理結果が間接的にサービスクラスに入力されます。同様にサービスクラスはDaoクラスを呼び出すという間接的な出力処理を行います。


実際のテストコードを使ってスタブ、モックそれぞれの使い方を考えてみます。
以下のサービスクラスをテストする場合を考えます。
package com.kenjih.sample.service;

import java.sql.SQLException;
import java.util.Date;

import com.kenjih.sample.Dao.OrderDao;
import com.kenjih.sample.component.UserContext;
import com.kenjih.sample.entity.OrderEntity;
import com.kenjih.sample.exception.BussinessException;

public class OrderServiceImpl implements OrderService {

    OrderDao orderDao;

    UserContext userContext;
    
    public boolean addOrder(OrderEntity entity) {
        if (entity == null)
            throw new BussinessException("entity is null.");

        String userId = userContext.getLoginUser();
        if (userId == null)
            throw new BussinessException("user is not logged in.");
        
        entity.setOrderedBy(userId);
        entity.setOrderedAt(new Date());        

        boolean result = false;
        try {
            result = orderDao.create(entity);
        } catch (SQLException ex) {
            throw new BussinessException("database error occurred.", ex);
        } catch (Exception ex) {
            throw new BussinessException("unknown error occurred.", ex);
        }

        return result;
    }
}
上記のテスト対象をテストする場合どのようなコードを書けばいいか考えてみます。
まず、テスト対象のすべての文が網羅されるように条件を考えます。分岐条件が引数entityに加えて
  • UserContext#getLoginUser()
  • OrderDao#create()
からの間接入力に依存しているため、間接入力を都合のいいように書き換える必要がでてきます。このときに利用するのがスタブです。

次に、テスト対象の出力が正しいことを検証します。テスト対象の戻り値(またはスローされる例外)が正しいことを確認する他に、処理の内部で行われる間接的な出力が正しいことを確認する必要があります。例えば上の処理の場合、
  • ログインユーザーが取得できなかった場合は、OrderDao#create()を呼び出さない。
  • ログインユーザーが取得できた場合は、引数Entityと同じインスタンスを使ってOrderDao#create()を一度だけ呼び出す。
などを確認する必要があります。このように間接出力が正しいことを検証するために使用するのがモックです。

以下にMockitoを利用した場合のテストコードを載せておきます。
文法上@Mockを使っていますが、実際は、orderDao、userContextともにスタブかつモックの役割を果たしています。
when(oo).thenReturn(xx)、when(oo).thenThrow(xx)の部分ではスタブとして使い、verify(oo).xxx()の部分ではモックとして使っています。

package com.kenjih.sample.service;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import java.sql.SQLException;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import com.kenjih.sample.component.UserContext;
import com.kenjih.sample.entity.OrderEntity;
import com.kenjih.sample.exception.BussinessException;
import com.kenjih.sample.Dao.OrderDao;

@RunWith(MockitoJUnitRunner.class)
public class OrderServiceTest {

    @Mock
    private OrderDao orderDao;

    @Mock
    private UserContext userContext;

    @InjectMocks
    private OrderServiceImpl orderService;

    /*
     * 入力entityがnullの場合
     */
    @Test
    public void testUpdate_1() throws SQLException {
        try {
            orderService.addOrder(null);
            fail();
        } catch (BussinessException ex) {
            assertEquals("entity is null.", ex.getMessage());
            verify(userContext, never()).getLoginUser();
            verify(orderDao, never()).create((OrderEntity)anyObject());
        }
    }

    @Test
    /*
     * userContext.getLoginUser()からの間接入力がnullの場合
     */
    public void testUpdate_2() throws SQLException {
        when(userContext.getLoginUser()).thenReturn(null);

        try {
            orderService.addOrder(new OrderEntity());
            fail();
        } catch (BussinessException ex) {
            assertEquals("user is not logged in.", ex.getMessage());
            verify(userContext).getLoginUser();            
            verify(orderDao, never()).create((OrderEntity)anyObject());
        }
    }

    @Test
    /*
     * orderDao.create()からの間接入力がSQLExceptionの場合
     */
    public void testUpdate_3() throws SQLException {
        when(userContext.getLoginUser()).thenReturn("user001");
        when(orderDao.create((OrderEntity) anyObject())).thenThrow(
                new SQLException());

        try {
            orderService.addOrder(new OrderEntity());
            fail();
        } catch (BussinessException ex) {
            assertEquals("database error occurred.", ex.getMessage());
            verify(userContext).getLoginUser();            
            verify(orderDao).create((OrderEntity)anyObject());
        }
    }

    @Test
    /*
     * orderDao.create()からの間接入力がSQLException意外の例外の場合
     */
    public void testUpdate_4() throws SQLException {
        when(userContext.getLoginUser()).thenReturn("user001");
        when(orderDao.create((OrderEntity) anyObject())).thenThrow(
                new RuntimeException());

        try {
            orderService.addOrder(new OrderEntity());
            fail();
        } catch (BussinessException ex) {
            assertEquals("unknown error occurred.", ex.getMessage());
            verify(userContext).getLoginUser();            
            verify(orderDao).create((OrderEntity)anyObject());
        }
    }

    @Test
    /*
     * orderDao.create()からの間接入力がfalseの場合
     */
    public void testUpdate_5() throws SQLException {
        when(userContext.getLoginUser()).thenReturn("user001");
        when(orderDao.create((OrderEntity) anyObject())).thenReturn(false);

        OrderEntity entity = new OrderEntity();
        assertFalse(orderService.addOrder(entity));
        verify(userContext).getLoginUser();            
        verify(orderDao).create(entity);
    }

    @Test
    /*
     * orderDao.create()からの間接入力がtrueの場合
     */
    public void testUpdate_6() throws SQLException {
        when(userContext.getLoginUser()).thenReturn("user001");
        when(orderDao.create((OrderEntity) anyObject())).thenReturn(true);

        OrderEntity entity = new OrderEntity();
        assertTrue(orderService.addOrder(entity));
        verify(userContext).getLoginUser();            
        verify(orderDao).create(entity);
    }

}

References
[1] Test Double at XUnitPatterns.com

2014年2月13日木曜日

MockitoでUnitテスト効率化



 Mockitoというテストフレームワークがとても便利です。いわゆるモッキングフレームワークなのですが、SpringなどのDIコンテナを使って依存性を注入しているようなケースでもスタブ/モックを簡単に作ってくれて使いやすいです。

準備
Mavenを使って必要なライブラリをインストールします。pom.xmlに以下を記述してください。 直接関係はありませんが、サンプルでCommons Langも使っているためそれもdependenciesに含めています。
<dependencies>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>1.9.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.0</version>
    </dependency>
</dependencies>
簡単な機能の使用例
Mockitoの機能を使ってみます。djUnitのVirtual Mock Objectsのようなことが出来ます。
package com.kenjih.mockito;

import java.util.LinkedList;
import java.util.List;

import org.junit.Test;

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;


public class Sample {
    @Test
    public void runTest1() {
        List mockedList = mock(List.class);

        mockedList.add("hoge");
        mockedList.clear();
        mockedList.add("fuga");
        mockedList.add("fuga");
        mockedList.add("fuga");

        // "hoge"を引数にしてaddメソッドは一度だけ呼ばれたか?
        verify(mockedList).add("hoge");
        
        // clearメソッドは一度だけ呼ばれたか?
        verify(mockedList).clear();
        
        // "fuga"を引数にしてaddメソッドはちょうど3回呼ばれたか?
        verify(mockedList, times(3)).add("fuga");
        
        // isEmptyメソッドは呼ばれていないか?
        verify(mockedList, never()).isEmpty();

    }
    
    @Test
    public void runTest2() {
        LinkedList mockedList = mock(LinkedList.class);

        // mockedList.get(0)が呼ばれたら必ず"first"を返すように設定。
        when(mockedList.get(0)).thenReturn("first");
        
        // mockedList.get(1)が呼ばれたらRuntimeExceptionをスローするように設定。
        when(mockedList.get(1)).thenThrow(new RuntimeException());

        assertEquals("first", mockedList.get(0));
        assertEquals("first", mockedList.get(0));
        
        try {
            mockedList.get(1);
            fail();
        } catch(RuntimeException e) {
            
        }
        
        // 値が設定されていない場合はnullを返す。
        assertNull(mockedList.get(2));
        
        // 任意の引数に対して、mockedList.getは"hoge"を返すように設定。
        when(mockedList.get(anyInt())).thenReturn("hoge");
        for (int i = 0; i < 10; i++)
            assertEquals("hoge", mockedList.get(i));
    }
}
Mockitoを用いたUnit Testの例
簡単な使い方を紹介したところで、もう少し実用的な例を考えます。 例としてサービスクラスの単体テストを考えます。サービスクラスはDAOを持っていて、DAOの部分をモック化してテストを行います。以下の例では、DAOはコンストラクタ/セッターでセットされる想定になっていますが、SpringなどのDIコンテナからDAOをインジェクションしている場合にも同様の方法でモックを使うことが出来ます。

まず、テスト対象のサービスクラスです。updateメソッドをテストすることを考えてみてください。(サンプルなので処理はかなり適当に書いてます)
package com.kenjih.mockito;

import java.sql.SQLException;

public class Service {

    private Dao dao;   
    
    public boolean update(Entity entity) {
        if (entity == null) {
            return false;
        }
        
        boolean ret = false;
        try {
            ret = dao.update(entity);
        } catch (SQLException e) {
            throw new BussinessException("Database access error occurred.", e);            
        } catch (RuntimeException e) {
            throw new BussinessException("Unknown error occurred.");
        }
        
        if (!ret) {
            throw new BussinessException("data was not updated.");
        }
        
        return true;
    }
}

サービスクラスから参照されているクラスの定義は以下のとおりです。
package com.kenjih.mockito;

import java.sql.SQLException;

public interface Dao {
    boolean create(Entity entity) throws SQLException;
    Entity read(int id) throws SQLException;
    boolean update(Entity entity) throws SQLException;
    boolean delete(Entity entity) throws SQLException;
}
package com.kenjih.mockito;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

public class Entity {
    private String name;

    public Entity() {}

    public Entity(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(name).toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Entity)) {
            return false;
        }
        if (this == obj) {
            return true;
        }
        Entity rhs = (Entity) obj;
        return new EqualsBuilder().append(name, rhs.name).isEquals();
    }
}
package com.kenjih.mockito;

public class BussinessException extends RuntimeException {

    private static final long serialVersionUID = 2986073923681825950L;

    public BussinessException() {  
        super();  
    }  
  
    public BussinessException(String message) {  
        super(message);  
    }  
  
    public BussinessException(String message, Throwable throwable) {  
        super(message, throwable);  
    }  
  
    public BussinessException(Throwable throwable) {  
        super(throwable);  
    }  
}

そして以下がテスト実行クラスです。@MockでmockedDaoがモックになっているところと、@InjectMocksでserviceにモック化されたオブジェクトが注入されているところがポイントです。
また、@Beforeが付与されたメソッドでモックの挙動を定義しています。mockedDao#update()メソッドを呼んだ場合の戻り値を、引数ごとに指定しています(どの引数パターンにマッチするかの判定には引数Entityのequalsメソッドが使われます)。
package com.kenjih.mockito;

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

import java.sql.SQLException;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class TestService {
    
    @Mock
    private Dao mockedDao;
    
    @InjectMocks
    private Service service;   // Mockオブジェクト注入
    
    @Rule
    public ExpectedException exception = ExpectedException.none();

    @Before
    public void init() throws SQLException {
        // mockedDaoの振る舞いを設定する。
        when(mockedDao.update(new Entity("DB_ERR"))).thenThrow(new SQLException());
        when(mockedDao.update(new Entity("UNKNOWN_ERR"))).thenThrow(new RuntimeException());
        when(mockedDao.update(new Entity("FALSE"))).thenReturn(false);
        when(mockedDao.update(new Entity("SUCCESS"))).thenReturn(true);    
    }
    
    @Test 
    public void testUpdate_1() {
        assertEquals(false, service.update(null));
    }
    
    @Test
    public void testUpdate_2() {
        exception.expect(BussinessException.class);
        exception.expectMessage("Database access error occurred.");
        service.update(new Entity("DB_ERR"));
    }
    
    @Test
    public void testUpdate_3() {
        exception.expect(BussinessException.class);
        exception.expectMessage("Unknown error occurred.");
        service.update(new Entity("UNKNOWN_ERR"));        
    }
    
    @Test
    public void testUpdate_4() {
        assertTrue(service.update(new Entity("SUCCESS")));    
    }
    
}

2013年12月20日金曜日

Eclipse CDT環境設定

Eclipse CDTの環境設定メモ。
既存のソースをEclipseのプロジェクトに入れてコンパイル/実行が出来るようにするまでの作業メモ。

Unresolved inclusion: を解決する
プロジェクト右クリック - Properties - C/C++ General - Path and Symbols - Includesタブ - LanguesでGNU Cを選択 - AddでIncludeしたいファイルのパスを追加。

 標準関数のインクルードパスがどこにあるかを知りたいときは、適当なtest.cを作って
gcc -v -E test.c
とすれば、
(省略)
#include <...> の探索はここから始まります:
 /usr/lib/gcc/i686-linux-gnu/4.6/include
 /usr/local/include
 /usr/lib/gcc/i686-linux-gnu/4.6/include-fixed
 /usr/include/i386-linux-gnu
 /usr/include
(省略)
のように表示されるので、これらをAddで追加すればよい。

math libraryをリンクする
プロジェクト右クリック - Properties - C/C++ Build - Settings - Tool Settingsタブ - GCC C Linker - Libraries - +ボタン - "m"と入力(ダブルクオーテーションの中身のみ入力) - OK

なんかいろいろ警告が出るのを消す
警告なのに赤線で出てきてやな感じだったので、-wオプションでコンパイルすることにした。

プロジェクト右クリック - Properties - C/C++ Build - Settings - Tool Settingsタブ - GCC C Compiler - Warnings - inhibit all warningsのみチェック - OK

2013年11月11日月曜日

GDBでC++デバッグするときはpretty-printersが便利

pretty-printersというツールを使うと、GDBデバッグ時にSTLコンテナの中身を”人間にも読める形”で表示させることが出来ます。

Before --pretty-printersを使わない場合
以下のソースをサンプルとして使います。
int main(int argc, char **argv) {
    int buf[] = {1,2,3,4,5};
    vector<int> vec(buf, buf+5);

    cout << accumulate(buf, buf+5, 0) << endl;
    cout << accumulate(vec.begin(), vec.end(), 0) << endl;

    return 0;
}
デバッグ中に、bufおよびvecの中身を表示すると以下のようになります。

(gdb) p buf
$1 = {1, 2, 3, 4, 5}
(gdb) p vec
$2 = {<std::_Vector_base<int, std::allocator<int> >> = {_M_impl = {<std::allocator<int>> = {<__gnu_cxx::new_allocator<int>> = {<No data fields>}, <No data fields>}, _M_start = 0x804c008, _M_finish = 0x804c01c, _M_end_of_storage = 0x804c01c}}, <No data fields>}

配列は中身が表示されますが、vectorはコンテナに格納した値が表示されません。
p *(vec._M_impl._M_start)@5とすれば中身が見れますが、毎回タイプするのは面倒です。

After --pretty-printersを使った場合
pretty-printersを使うと、先ほどの例がこうなります。

(gdb) p buf
$1 = {1, 2, 3, 4, 5}
(gdb) p vec
$2 = std::vector of length 5, capacity 5 = {1, 2, 3, 4, 5}

きちんと欲しい情報が表示されました。
他にもいろいろ試してみましたが、vector<vector<int> >やset<pair<int, int> >などの複雑な型にも対応しているようです。

インストール方法
1. svnからチェックアウト
svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python

2. ~/.gdbinitに以下を追記
python
import sys
sys.path.insert(0, '/home/maude/gdb_printers/python')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end

※赤字のところは、チェックアウトしたディレクトリに置き換えてください。

参考URL
[1] debugging - How do I print the elements of a C++ vector in GDB? - Stack Overflow
[2] STLSupport - GDB Wiki

2012年12月1日土曜日

Dropboxを入れてみた

最近、競技プログラミングの問題をカテゴライズして、各問題ごとに直近で解いた日時、解いた回数を管理するような簡易アプリを作りました。
特にWEB上に公開するようなつもりは無くて、ローカルPCにApacheを入れてLANからのみ見れるようにしました。

1ヶ月間くらい使ってみて、問題がたまってきたので、バックアップを取りたいなと思いました。で、手軽にできるものが無いかなと考えていたところ、Dropboxを使うと簡単にできそうなのでやってみました。

やりたいこと
  • ローカルPCが壊れても、復旧できるようにしたい。
  • バックアップ対象は、DBのデータとPHPのソース。
  • バックアップの頻度は、3日に1回程度。
やったこと
まず、Dropboxをインストール。
すると、HOMEディレクトリにDropboxというディレクトリができる。
ここにファイルを入れるとDropboxのサーバーに自動で保存されるので、バックアップ完了という流れ。

あとは、自動でDBのdumpと、ソースコードをまとめてtar化するようなスクリプトを書けばいいだけ。
#!/bin/sh

data_backup_file="/home/kenjih/Dropbox/backup/procom/data/dump.sql"
src_backup_file="/home/kenjih/Dropbox/backup/procom/src/app.tar"
src_dir="/home/kenjih/dev/procom/src/app"
log="/home/kenjih/Dropbox/backup/procom/backup.log"

today=`date '+%s'`
updated_date=`stat -c '%y' ${data_backup_file}`
expire_date=`date -d "${updated_date} 3 days" '+%s'`

if [ $today -gt $expire_date ]; then
    mysqldump -u usr -ppasswd procom >${data_backup_file}
    tar -cf ${src_backup_file} ${src_dir} 2>/dev/null
    echo "backup files saved at `date '+%Y/%m/%d %T'`." >>${log}
fi

で、cronを設定。
# m h  dom mon dow   command
0 23 * * * /home/kenjih/dev/procom/src/app/Console/backup.sh