2008년 9월 25일 목요일

Windows 에서 git 사용

Cygwin? msysGit?

Cygwin 을 쓰던 사람이라면 Cygwin 으로 배포되는 git 을, git 이외에 Cygwin 류의 것을 쓸 일이 없다는 사람은 msysGit 을 추천한다.

(Cygwin 홈 페이지 자신을 "a Linux-like environment for Windows" 라고 소개하고 있다.)

Cygwin 팁

Windows 폴더 팝업 메뉴에서 Cygwin shell 을 바로 띄우는 방법이다. fvue.nl/wiki/Bash_and_Windows 에 자세한 내용이 있다. (msysGit 은 설치하면 "Git Gui Here"와 "Git Bash Here" 메뉴 아이템을 추가한다.)

C:\cygwin\cygwin_here.bat (Window2000 이상)

 @echo off
 REM --- cygwin_here.bat ------------------------------------------------------
 REM function: Start Cygwin in current directory
 REM args:     - 1..9

 REM Setting `CHERE_INVOKING' prevents /etc/profile from issuing `cd $HOME'
 set CHERE_INVOKING=1
 C:\cygwin\bin\bash --login -i %1 %2 %3 %4 %5 %6 %7 %8 %9

registry 수정

 HKEY_CLASSES_ROOT
 |-Folder
   |-shell
     |-Shell to Cygwin   "Shell to Cyg&win"
       |-command         "C:\cygwin\cygwin_here.bat"

혹시 권한을 탓하며 실행을 거부하는 경우에는 Cygwin 을 실행하고 권한을 주면 된다.

 cd /
 chmod +x cygwin_here.bat

(Cygwin 에서 cat 으로 cygwin_here.bat 을 만들었는데 실행권한이 없었다. 그냥 다른 Windows 에디터를 사용했었다면 이런 일이 안 생겼을지도 모르겠다.)

원활한 원격 작업을 위한 설정

원격 저장소와 통신에 사용하는 디폴트 프로토콜은 ssh 이다. 보통 아무 작업도 해주지 않으면 push 나 pull 할 때 암호를 물어오게 되어 있다. 암호를 입력해 주면 되는데, 문제는 그렇게 두면 git gui 에서는 원격 작업을 할 수 없다는 것이다.

작업 머신에서 서버에 암호 입력 없이 접근할 수 있게 해주는 방법이 있다.

 ssh-keygen.exe -C "you@your.email.com" -t dsa

 scp ~/.ssh/id_dsa.pub  you@yourgitserver:~/.ssh/authorized_keys

(이 때 한 번 로그인 암호를 입력하게 된다.)

ssh-keygen 시에 passphrase 를 물어 오는데 입력을 하지 않는 것이 편하다. 입력을 하고 나중에 ssh-agent, ssh-add 를 이용하면 많이 불편하지는 않지만, 입력을 하지 않는 것 보다는 불편하다.

ssh-agent, ssh-add 사용하기

만일 주인이 없는 사이에 누군가 개발 머신을 켜고 원격 저장소를 엑세스하는 것을 막고자 한다면, 아니면 누군가 id_dsa 를 복사해 가서 원격 저장소를 엑세스 할 가능성을 없애려면, 매번 암호 입력을 하거나 passphrase 를 사용해서 ssh-keygen 을 해야만 한다.

아래는 ssh-agent 와 ssh-add 를 사용하는 방법이다. 먼저 Cygwin 이나 Git-Bash 를 실행하고

 eval `ssh-agent -s`
 ssh-add

passphrase 를 입력해준다.

이제 마음껏 작업한다.

 git pull
 git push
 git gui
 ...

다른 사람이 자리에 앉아서 서버에 접속할 가능성을 차단하기 위해 ssh-agent 를 죽인다.

 ssh-agent -k

물론 컴퓨터를 끄고 집에 가려 한다면 마지막 명령은 필요없다.

2008년 9월 24일 수요일

Git - CVS 에서 전환

첫번째 문서 중 중요 내용 요약, 약간의 내용

단어들

저장소
repository
중앙 저장소
central repository
개별 저장소, 작업본 저장소
local repository
작업본
working copy
bare 저장소
작업본이 없는 저장소
SHA1-number
git 은 모든 object 에 SHA1 hash number 를 생성한다. 이것은 어떤 파일이나 특정 commit 등을 지정하는데 사용할 수 있다.

처음

이름과 이메일 설정

 git config --global user.name "Your Name"
 git config --global user.email "my@email.com"

원할 경우, conflicts 해결을 위해 머지(merge) 툴을 설정할 수 있다.

 git config --global merge.tool meld

CVS 에서 하던 것처럼

괜히 더 복잡하다고 생각할 수도 있다.

중앙 저장소를 두는 셋팅의 경우.

가져오기
 cvs checkout

 git clone <server>:/path/to/repos/project.git
중앙 저장소에 바뀐 부분 가져오기
 cvs update

 git pull origin master
수정한 것을 중앙 저장소로
 cvs update
 cvs checkin file1 file2 ...

 git pull origin
 git commit file1 file2 ...
 git push origin master

여기에서 git 과 CVS 의 차이가 확연히 나타난다. git 의 commit 은 개별 저장소에만 commit 을 하는 것이다. (git 의 경우 작업본마다 개별 저장소를 가지고 있다.) 중앙 저장소에 push 하는 것은 별개의 작업이다.

좀 더 유용한 작업 방식은 아래 처럼.

 ... 작업
 git commit file1 file2 ...
 ... 작업
 git commit fileN dir2 ...
 ... 작업
 git commit -a  # (수정 사항 모두 commit)
 ... 새로운 기능 완성!
 git pull origin master
 git push origin master
기타 작업
 cvs add file1 ...
 git add file1 ...

 cvs remove file1
 git rm file1

파일 이동이나 이름 바꾸기

 [cvs remove, add 각각]
 git mv file1 file

지운 파일 살리기

 cvs update
 git checkout file1

commit 을 자주

git 과 CVS 의 가장 큰 차이 두가지:

  • 작업본이 완전한 저장소 - commit 과 commit 들을 보내는 차이가 존재함
  • 각각의 파일 상태를 추적하는 것이 아니라 전체 상태를 추적함

git 의 commit 은 CVS 의 그것과 같다. 다만 push 하기 전까지 그것은 개발자의 작업본 저장소에만 적용된다. 중앙 저장소로 push 하면 개발자의 저장소와 중앙 저장소는 같은 상태가 된다. 마지막 push 이후 12번의 commit 을 했다면 12번의 commit 이 중앙 저장소로 push 된다. CVS 에서라면 서버에 직접 12번 commit 을 했어야 한다. git 에서는 원하는 만큼 commit 하고 됐다고 생각될 때 서버로 작업한 것을 올릴 수 있다.

결론 : git 은 자주 commit 하는데 부담이 없다.

Branch

git 에서 branch 와 merge 는 (CVS에 비해서) 아주 간단하다. 사실 각각의 개별 저장소는 각각의 branch 라고 할 수 있다.

branch 만들기

 git branch <branch>

특정 지점 으로 branch 만들기

 git branch <branch> <지점>

<지점> - 다른 branch, tag, SHA1-number 등이 올 수 있다.

branch 목록 보기

 git branch

작업 branch 바꾸기

 git checkout <branch>

git checkout 은 branch, tag 등 지점을 가져오는데도 사용되고 파일이나 디렉토리를 가져오는데도 사용된다.

branch 지우기 - commit 들이 현재 branch 에서 추적 가능한 경우에만

 git branch -d <branch>

branch 지우기 - commit 들이 추적 불가능한 경우에도 지워버림

 git branch -D <branch>
작은 오류 수정 push

작은 오류 수정을 해야할 필요가 있는데, 많은 commit 을 했고 push 할만한 상태가 아닐 경우, branch 가 유용할 수 있다.

  • 아직 수정하지 않은 경우 - 임시 branch (fix 라고 이름 지었다고 가정)를 만들고 checkout 한다. 문제를 해결하고 commit, 서버에 push 한다. 작업 branch 를 chekout 하고 수정사항을 서버에서 pull 한다. fix branch 를 지운다.
  • 문제를 이미 해결하고 commit 해둔 경우 - 임시 branch (fix)를 만든다. 오류 수정한 commit 의 SHA1-number 를 찾는다. (gitk 나 git log 사용) git cherry-pick SHA1-number 로 해당 commit 만을 fix branch 에 반영한다. push 하고 작업 branch 를 checkout 한다. fix branch 를 삭제한다.
기타 유용한 경우
  • 실험 branch
  • 둘 이상의 release 관리
  • 특화 기능 추가 요청
  • 등등 …

submodule

git 은 파일 하나 하나의 변화를 추적하는 대신 전체의 변화를 한 commit 으로 관리한다. 부분별 관리가 따로 필요할 경우를 위해서 만들어진 기능이 submodule 기능이다.

Manual 참조.

기타

  • push 는 bare 저장소에 쓰기 위한 것이다. 작업본이 있는 저장소에도 push 를 할 수 있기는 하지만 작업본까지 바꾸지는 않기 때문에 개발자를 혼란에 빠뜨릴 것이다.
  • GUI 가 있다. "git gui". pull은 없고 fetch 와 merge 를 해야한다.
  • git bisect - 버그가 들어온 경우, 어떤 commit 때문인지를 2진 검색(binary search) 으로 찾을 때 사용.

2008년 9월 21일 일요일

Git #0 - git?

  • git - Fast Version Control System

최근 많은 오픈소스 프로젝트들(특히 RoR(Ruby on Rails) 관련 프로젝트들)이 버젼 관리 툴을 git 으로 옮겨갔다는 것을 알게 되었다. RoR 개발 자체가 git 으로 옮겨간 것이 관련 프로젝트들의 이동에 영향을 미쳤던 것 같다.

근래에 새로운 프로젝트를 시작할 계획을 하면서 어떤 버젼 관리 툴을 쓸 것인가 고민 중이었는데, git 이 어떤지 한 번 둘러보기로 했다.

처음 git 이 만들어지고 사용되기 시작했을 때, 별 관심이 가지 않았던 것은 Linux 개발을 염두에 두고 만들어졌을 것이라는 생각이 들었기 때문일 것이다. 아마도 Linux 처럼 큰 규모이고 개발자들이 흩어져 있는 프로젝트를 염두에 두고 작성된 것을 간단한 프로젝트나 문서 관리에 사용하기에는 불필요하게 복잡하지 않을까 했던 것 같다.

Git 은 분산형 버젼 관리 툴이고, 성능에 중점을 두고 개발하였다고 한다. 분산형 버젼 관리에 대해서 한글 블로그 글이 있다.

whiteship.me/1826 와 포함된 링크들이 도움이 될 것이다.

Linus 가 2007년에 Google Talk 에서 한 강연도 git 을 이해하는데 도움이 된다. 다만, CVS(SVN 포함)와 그 사용자들을 무지하게 비난하기 때문에 CVS 나 SVN 사용자들은 기분이 나쁠 수도 있다.

분산형 버젼 관리 툴이라하면 개인 프로젝트에 사용하기에는 다른 툴에 비해 쓰기가 더 불편할 것 같지만 개인 프로젝트의 경우에도 git 이 CVS 나 Subversion 보다 사용하기에 더 간편하다는 느낌이 들었다. CVS 나 Subversion 을 쓸 때는 중앙 저장소를 어떻게 구성해야 할 것인지 먼저 고민을 해야한다. 게다가 프로젝트 디렉토리 이름이라도 바꾸려면 (개인 프로젝트 단계라 할지라도) 성가신 작업을 거치거나 중앙 저장소의 명칭과 불일치한 상태로 작업을 해야한다. 이에 반해 git은 working copy 와 저장소가 함께 있기 때문에 작명 작업을 뒤로 미루거나 후에 이름을 바꾸는 일에 대한 부담이 적다. (물론 개인 프로젝트 단계에 있거나 작은 팀의 경우이다. 공개 되었거나 큰 팀이 작업한다면 중앙 집중형이건 분산형이건 이름을 바꾸지 않는 것이 상책일 것이다.)

팀에서 CVS 대신 git 을 사용하는 것을 검토해 보기로 하였다. 말을 꺼낸 내가 그 일을 맡게 되었다. 아마도 몇 개의 git 사용에 관한 글을 더 쓰게 될 것 같다.

2008년 9월 8일 월요일

Java.next

JtestR 에 관한 글을 쓴 이후에 아래 질문에 대해서 좀 써 봐야겠다고 생각했었다.

  • 왜 Java 프로젝트에서 Ruby 를?

충분하지 않은 간단한 답은 간결함 이랄까? 어쨌든 설명이 수월치 않았다.

그러던 와중에 웹 서핑을 하다가 실마리가 될만한 시리즈를 발견했다. Stuart Halloway 가 쓴 것으로 JVM 을 기반으로 차세대 언어에 관한 글이다. Clojure, Groovy, JRuby, Scala, 이 네개의 언어가 소개된다.

첫번째 글은 차세대 언어들의 Java 와 대비되는 공통점들에 대해서 이야기하고 있다. 이글이 위 질문에 대한 답을 어느정도는 해줄 수 있다고 생각한다.

같은 것을 표현 하는 코드는 (readablity 를 해치지 않는 범위 안에서) 짧을 수록 좋다. Java 는 같은 일을 하는데 군더더기 코드가 많이 필요하다.

또 한가지, 첫번째 글의 결론에 나오는 데로 Java.next 언어들은 Java 에 비해서 좀 더 Domain 에 잘 맞는 DSL(Domain-Specific Languages) 을 개발하기가 용이하다. JtestR 에 포함된 RSpec 이 한가지 예로 BDD(Behavior Driven Development) DSL 이라고 말할 수 있다. 물론, Java 에도 BDD 를 지원하는 페키지들이 개발되어 있기는 하다. 그렇지만, 각각의 예제를 RSpec 의 예제와 비교해 본다면 어떤 것이 읽기 좋은지 금방 결정할 수 있을 것이다.

질문의 범위를 좁혀서 ‘왜 JtestR 을 쓰려고 했나?’ 라는 질문을 해보자. 그러면 ‘테스트 코드만이라도 간결하게 짜고 싶어서’, 그리고 ‘RSpec 을 쓰고 싶어서’ 라는 말이 대부분의 이유를 설명해 주는 대답이 될 것이다.

2008년 8월 31일 일요일

RDoc 을 blog 작성 도구로

JtestR 관련 두번째 글을 작성하고 blogger 에 올리려고 했을 때 잠시 좌절했었다. 소스 코드와 함께 예쁘게 포맷되어서 올려진 다른 글들을 보고는 기본 에디터에서 소스라고 지정해줄 수 있을 것이라고 생각했던 것이다. 소제목들과 소스만 영역 설정해서 표시해주면 될 것이라고 생각했었는데 너무 많은 것을 기대했었다는 것을 올리려는 순간에야 알았다.

참고로한 소스 코드와 함께 올려졌으면서도 예쁘게 포맷된 블로그 포스트의 경우 이미 HTML 출력을 지원하는 에디터의 손을 거친 것이었다. 어떻게 해야하나? 우선은 별 생각 없이 HTML 이나 Blog 저작 툴이라고 되어 있는 것들을 깔아보고 하나를 고를 계획이었다.

그렇지만, HTML 편집을 직접하는 것은 원하지 않았다. 포맷팅에 신경을 쓰지 않고 글을 쓰고 싶었다. Docbook 이나 LaTeX 같은 것을 쓸까? 한 페이지 블로그 엔트리 작성에 그것들을 사용하는 것은 아무래도 오버라는 생각이 들었다.

그렇게 HTML 저작 툴들이 깔려가고 있을 때 떠오른 것이 RDoc 이다.

RDoc ?

RDoc 은 Javadoc 이나 Doxygen 과 비슷한 부류의 프로그램이다. Ruby 소스의 주석으로부터 문서를 만들어내는 도구이지만 소스 파일이 아닌 경우도 포맷팅을 해준다.

RDoc 에서 지원해주는 Markup 들이 딱 내가 필요로하는 정도였다.

  • 빈 줄로 단락 구분
  • 들여쓰기로 verbatim 처리 (소스 표시에 좋다.)
  • 다양한 목록 처리 (일반, numbered, labeled)
  • http: ftp: 등의 링크 표시의 간편함
  • =로 단락 제목 (Heading)

예를 들어서 위 내용의 변환 전 소스는 아래와 같다.

 == RDoc ?

 RDoc[http://rdoc.rubyforge.org/] 은 Javadoc 이나 Doxygen 과 비슷한 부류의 프로그램이다.
 Ruby 소스의 주석으로부터 문서를 만들어내는 도구이지만 소스 파일이 아닌 경우도 포맷팅을 해준다.

 RDoc 에서 지원해주는 Markup[http://rdoc.rubyforge.org/classes/RDoc/Markup.html] 들이 딱 내가 필요로하는 정도였다.
 * 빈 줄로 단락 구분
 * 들여쓰기로 verbatim 처리 (소스 표시에 좋다.)

원래의 JtestR 글을 조금 만져주었더니 RDoc 의 소스로 바로 사용이 가능했다. (링크, 헤딩(Heading), 소스 들여쓰기) 문제는 RDoc 이 Ruby 문서화를 위한 도구이기 때문에 기본 출력이 blog 포스팅에 사용하기에는 불편하다는 점이다. 우선은 인코딩과 출력 양식을 조절해 줄 필요가 있다.

  rdoc -1 -c utf-8 rdoc_as_html_writing.rdoc -n without_template.html
-1
한개의 html 파일로 출력
-c utf-8
인코딩은 utf-8
-n without_template.html
without_template.html 파일로 출력

원하는 단계에 거의 다 왔다. 하지만 여전히 RDoc 의 기본 출력은 군더더기 들이 많아서 불편하다.

예를들면 without_template.html 의 내용은 아래와 같다.

  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  <html>
  <head>
    <title>RDoc Documentation</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  </head>
  <body>
  <h2>File: rdoc_as_html_writing.rdoc</h2>
  <table>
    <tr><td>Path:</td><td>rdoc_as_html_writing.rdoc</td></tr>
    <tr><td>Modified:</td><td>Mon Sep 01 11:11:45 +0900 2008</td></tr>
  </table>

  <h1>RDoc 을 blog 작성 도구로</h1>
  <p>
  ...
  <h2>Classes</h2>
  </body>
  </html>

여기서 파일 이름이나 날짜가 나올 필요도 없고, 관련 Classes 가 나올 필요도 없고 오로지 <body> 테그 안의 </table> 이후의 내용만 있으면 된다. 어떻게? 템플릿을 조금 손 보면 된다.

RDoc 의 기본 팀플릿 중 one_page_html.rb 를 가져다가 손을 보았다.

  cp /usr/lib/ruby/1.8/rdoc/generators/template/html/one_page_html.rb blog_template.rb

수정 후의 blog_template.rb

   module RDoc
   module Page
   ######################################################################
   #
   # The following is used for the -1 option
   #

   CONTENTS_XML = %{
   IF:description
   %description%
   ENDIF:description
   }

   ########################################################################

   ONE_PAGE = %{
   START:files
   } + CONTENTS_XML + %{
   END:files
   }

   end
   end

많은 내용이 있었지만 우리의 목적에는 이것으로 충분하다. 소스 코드가 아닌 파일의 경우에는 그냥 description 으로 처리가 된다.

템플릿을 사용하는 것은 —template 옵션을 사용한다.

최종에 가까워 지는 Rakefile 내용이다. (최종 룰은 아래에서 완성된다.)

  rule '.html' => '.rdoc' do |t|
    sh "rdoc -1 -c utf-8 --template ./blog_template.rb #{t.source} -n #{t.name}"
  end

이제 해당 html 생성은 rake some_file.html 하면 된다.

포스팅

이제 첫째줄을 제목으로 해서 나머지 내용을 올리면 포스팅이 완료된다.

(생성된 html을 blogger.com 에 올릴 때 주의할 점은 "설정"=>"서식" 에서 "줄바꿈 전환" 을 "아니오"로 해 두어야 한다는 것이다. 그렇지 않으면 원하는 결과가 아닌 수시로 줄바꿈이 있는 결과를 보게 된다. 미리보기에는 반영이 안되기 때문에 편집기에 복사해 붙이고 미리보기를 할 경우에는 당황스러울 수 있다.)

(소스 코드 부분의 배경색을 바꿔주기 위해서 "레이아웃"=>"HTML 편집"을 하고 CSS 부분에 <pre> 태그의 스타일을 추가했다.

  pre {
    background: #dddddd;
  }

)

헤딩 조절

기본적으로 blogger 는 포스트 제목에 <h3> 헤딩을 사용한다.(blogger 템플릿에 따라서 다른 지는 모르겠다.) 하지만 RDoc 은 <h1> 부터 출력을 만들어낸다. 그대로 사용하면 전체 적인 조화가 흐트러지는 문제가 생긴다.

해결방법은

  1. 그냥 조화가 흐트러진 채로 쓴다. - (지금까지는 이렇게 했다.)
  2. 작성시부터 <h3> 이후의 헤딩을 사용한다. (=== 부터 사용)
  3. 전처리를 통해서 헤딩을 바꿔주고 그 파일로부터 html을 만들어 준다.
  4. RDoc 처리를 바꾼다. (단순히 RDoc 템플릿을 수정하는 것으로는 해결이 안 되는 것 같다.)

정도를 생각해 볼 수 있었다. 첫번째는 해결방법이 아니고, 두번째는 적절한 방법이 아니다. 세번째 방법은 간단히 할 수 있다.

Rakefile .html 룰 처리에 변환 코드를 넣었다.

   rule '.html' => '.rdoc' do |t|
     h3_heading = IO.read("#{t.source}").map do |l|
       l[/^=/] = '===' if l[/^=/]
       l
     end

     tmp_file = "__#{t.source}"
     #
     tmp_file = tmp_file.gsub!(/[\.\/\\]/,'_')

     File.open(tmp_file, "w") { |f| f.write(h3_heading) }

     sh "rdoc -1 -c utf-8 --template ./blog_template.rb #{tmp_file} -n #{t.name}"

     rm tmp_file
   end

이제 되었다. 이 글을 변환하고 올려 보아야겠다. ^^

2008년 8월 27일 수요일

JtestR 써보기 - 실행

(이 글의 많은 내용은 JtestR 홈페이지의 Getting StartedConfiguration 에 기초하고 있다.)

디렉토리, 기본 설정

JtestR 은 기본적으로 test 디렉토리에 jtestr_config.rb 와 Ruby 테스트들이 있을 것을 기대한다. 물론 이 디렉토리가 test 가 아닐 경우 실행시 지정해 줄 수 있다. 지정해 주는 방식은 JtestR을 실행하는 방식에 따라 조금씩 다르다. (Getting Started 를 참조) JtestR의 기본 classpath 구성은 아래와 같이 이루어 진다.

   def find_existing_common_paths
     Dir["{build,target}/{classes,test_classes}"] + Dir['{lib,build_lib}/**/*.jar']
   end

이것과 현재의 구성이 다를 경우에는 jtestr_config.rb 에서 classpath 를 설정해 주어야 한다.

classpath 설정 예

   classpath ['classes','test_classes']
   classpath Dir['lib/*.jar']
   classpath Dir['test_lib/*.jar']

JUnit 테스트들의 경우는 jtestr_config.rb 에서 지정을 해 주어야 실행이 된다. TestNG 의 경우도 거의 같은 방식으로 지정해줄 수 있다고 한다.(TestNG를 써보지는 않았다.)

jtestr_config.rb 예

   junit 'unit' => ['com.some.company.any.prj.util.SomeAllTest',
                    'com.some.company.any.prj.app.SomeAllTest']

실행

background 서버

JtestR은 시작하는데 시간이 많이 걸린다. 이것은 JRuby 가 시작하는데 시간이 많이 걸리기 때문이다. 이 문제를 해결하기 위해 JtestR은 background 서버를 실행시키는 방법을 제공한다. background 서버가 실행 중일 경우 JtestR은 자동적으로 이것을 사용하도록 되어있다.

Ant, Maven 과 연동

Ant 예

   <target name="test" description="Runs all tests">
     <taskdef name="jtestr" classname="org.jtestr.ant.JtestRAntRunner" classpath="test_lib/jtestr-0.3.1.jar"/>
     <jtestr tests="test"/>
   </target>

Background 서버

   <target name="test-server" description="Starts test server">
     <taskdef name="jtestr-server" classname="org.jtestr.ant.JtestRAntServer" classpath="test_lib/jtestr-0.3.1.jar"/>

     <jtestr-server/>
   </target>
JUnit

JUnit의 TestCase 로 실행시키는 방법을 제시한 것은 다른 IDE 와 결합해서 사용하기 쉽게 해주려는 의도인 것 같다. org.jtestr.ant.JtestRSuite 을 JUnit 테스트로 사용하면 된다.

원문의 예

   <junit haltonfailure="false" fork="yes">
     <jvmarg value="-Djtestr.junit.tests=test_tests/one_of_each"/>

     <formatter type="xml" usefile="false"/>

     <test name="org.jtestr.ant.JtestRSuite"/>
   </junit>

(www.evalcode.com/2008/08/jruby-jtestr-in-eclipse 에 Eclipse 에서 이 방법을 사용해서 JtestR 을 실행시키는 방법이 소개되어 있다.)

command line

classpath 가 바르게 설정되어 있다고 가정한다면 그냥

   java org.jtestr.JtestRRunner

하면 된다.

테스트 디렉토리가 test 가 아닐 경우는

   java org.jtestr.JtestRRunner 22332 my_prj/my_tests

와 같이 하면 된다. (그 외 기타 옵션들에 대한 자세한 안내는 Getting Started 참조)

   java org.jtestr.JtestRRunner [port] [tests] [logging] [configFile] [outputLevel] [output] [groups]

(실수 - 처음 JtestR을 실행시킬 때 엉뚱한 문제로 한참 고생을 했었다. 기존의 JUnit 테스트들을 실행할 때 MethodNotFound 였던가? exception 을 내고는 중단 되는 것이다. 한참을 헤멘 뒤에야 CLASSPATH 에 junit.jar 가 있기 때문이라는 것을 알았다. 컴파일 시의 JUnit 과 실행시의 JUnit 버젼이 달랐다는 …)

(Getting Started 에는 없지만 background 서버를 command line 에서 실행시키는 방법이 있다. (JtestR 0.3.1 기준)

   java org.jtestr.BackgroundServer

)

Eclipse 에서 JtestR

필자의 Eclipse 환경은 대략 이렇다.

  • OS - ubuntu8.04 x86_64
  • JDK - sun-jdk-1.5.0
  • Eclipse - 3.4

(java 6 가 깔려 있기는 하지만 Eclipse 실행이나 개발시에는 쓰지 않는다.)

myeclipse.sh

   LANG=ko_KR \
     JAVA_HOME=/usr/lib/jvm/java-1.5.0-sun \
     PATH=/usr/lib/jvm/java-1.5.0-sun/bin:$PATH \
     ~/usr/eclipse3.4/eclipse

필자 외에는 전부 MS 윈도우가 주 개발 환경이고 기존 코드의 한글 주석이나 스트링이 완성형이었기 때문에 LANG=ko_KR 로 설정하고 실행한다. (eclipse 안에서 설정할 수도 있지만, 처음 eclipse 를 쓰기 시작할 당시에는 이 방법이 가장 간단했다.)

Eclipse 에서 JtestR 을 실행시키는 방법은 위의 방법들이 거의 다 적용 가능하다. ant 도 지원하고 JUnit 테스트도 지원하고, JtestRRunner 를 Run 으로 실행시킬 수도 있다.

처음 시도한 방법은 JUnit 테스트로 실행시키는 방법이다. 그러나, 이 방법은 생각만큼 유용하지 않았다. Fail 이나 Error 시에 해당 파일로 이동이 잘 되지 않았다. JUnit 테스트들은 Failure Trace 가 제대로 나오지도 않았다.

ant 실행이나, 일반 Java Run configuration 을 이용하는 방법은 출력이 거의 유사하다. 이 경우 console 윈도우에서 ruby 파일로의 이동은 여전히 안 되지만, JUnit 테스트 Failure 의 경우는 이동이 가능하다. 현재 상태로는 일반 Java Run configuration 을 이용하는 것이 최선인 것 같다.

(Eclipse 플러그인 중에 coverage 분석 툴인 Emma 를 기반으로한 eclEmma 가 있다. JtestR을 실행하면서 coverage 분석을 하려면 background 서버를 사용하지 않도록 해야 한다.)

Emacs 에서 JtestR

Java 개발에 있어서는 팀의 준 표준 툴이기도 하고, 꽤 괜찮은 툴인 eclipse 를 주로 사용하기는 하지만, 원래 필자는 emacs 신도이다. ruby 파일 수정은 여전히 emacs 에서 주로 이루어지기도 하고, Eclipse 에서 ruby 파일로 바로 링크도 되지 않기 때문에 emacs 에서 JtestR 을 실행시켜 보기로 하였다.

처음 떠오른 것이 C 개발을 emacs 에서 해 본 사람이라면 누구나 기억할 emacs 의 compile 명령이다. M-x compile. (필자의 경우 예전에 너무도 자주 사용하던 명령이라 키 바인딩이 되어 있었다.) Compile command: 하고 물어온다. 관련 설정을 바꾸지 않았다면 처음 실행시에는 "make -k" 가 디폴트로 나올 것이다. "java org.jtestr.JtestRRunner" 라고 입력해도 되지만, 이건 좀 아니다. ant 를 쓴다면 "ant test" 해도 되겠지만, ant 실행시의 출력은 compile 이 적절한 파일을 찾는데 방해가 된다. (물론 여전히 그냥 실행시킨다고 적절한 파일을 바로 찾는 것은 아니긴 하지만 말이다.)

Rake 를 이용하자.

Makefile 을 만들고 make -k 를 그대로 사용하는 방법도 있겠지만, 루비스트 들에게는 Rake 가 있다.

필자가 만든 Rakefile 의 일부이다.

   task :test do |t|
     sh "java -cp test_lib/jtestr-0.3.1.jar org.jtestr.JtestRRunner" rescue nil
   end

(Java 빌드를 위한 ruby 툴로 rake 기반의 툴들이 있다. raven 과 buildr 이 그것들인데 불행하게도 이들을 사용하려할 당시 raven 은 잘 동작하지 않았고 buildr 홈페이지의 문서들은 온전한 상태가 아니었다. 지금은 buildr 의 문서들이 온전히 돌아와 있다.)

이제 Compile command: 에 rake test 를 입력하면 테스트들을 실행한다.

rspec Failure 로 바로 이동

실험을 위해 test/unit/fail_spec.rb 을 만들어 보았다.

   describe "fail test" do
     it "should fail" do
       'a'.should be == 'b'
     end
   end

그러나 compile 에서 fail_spec.rb 로 바로 이동하는 것은 여전히 잘 되지 않았다. 엄밀히 말해서 이동 시켜줄 생각이 전혀 없어 보였다. 이것은 JtestR 이 테스트 결과를 통합해 보여주면서 indent 를 하기 때문인 것 같다.

간단한 해결 방법은 jtestr_config.rb 에 rspec_formatter "p" 라는 내용을 추가 하는 것이다. (Configuration 참조) 이것은 rspec 자체의 출력기를 사용하는 것으로 indent 되지 아니한 결과를 출력하게 되고 이것은 compile 에서 잘 잡아 준다. 대략 다음과 같은 출력이 나온다.

   ...
   'fail test should fail' FAILED
   expected == "b", got "a"
   /home/hjlee/workspace/a_project/test/unit/fail_spec.rb:5:
   ...
그러나 아직은

그러나 compile 과 JtestR을 현재 상태 그대로 사용하는 것은 문제가 있다. (compile 이라는 명령 이름에서 보이듯이 테스트 를 위한 것이 아니라, compile 을 위한 도구이니 딱 입맛에 맞기를 바라는 것이 사실 무리이다.)

rspec 의 경우 위와 같은 꼼수를 사용해서 어찌할 수 있었지만 JUnit 의 경우나 Test::Unit 의 경우에는 여전히 해당 파일로 바로 가기가 어려웠다. 아마도 JtestR이 지원하는 다른 종류의 ruby 테스트 툴들도 비슷할까?

이것이 마음에 드는 수준으로 동작하게 하려면 JtestR과 포함된 테스트 툴들의 출력을 모두 고치거나, 새로운 emacs-lisp 페키지를 만들거나 compile 이 JtestR의 결과를 이해할 수 있도록 커스터마이징, 아니면 수정을 해야 한다.

차선책 - tags

이런 일들이 이루어지거나 누군가 해 놓은 것을 발견하거나 스스로 해결하기 전에는 차선책을 사용할 수 있다. 필자가 생각한 차선책은 TAGS 파일을 사용하는 방법이다. TAGS 파일을 만들고 visit-tags-table 로 이 파일을 읽어둔다. compile 결과 화면에서 이동하고 싶은 class 나 method 이름 위에 커서를 위치하고 C-x 4 . (‘find-tag-other-window’) 로 돌아다니는 것이다.

tags:emacs task

(blog.lathi.net/articles/2007/11/07/navigating-your-projects-in-emacs 여기서 본 것 같은데 열리지가 않는다.)

필자가 어떤 글에서 긁어서 조금 수정한 rake 소스 이다. (원문과의 차이 - *.java 는 없었다. TAGS 과 SRC_FILES 간의 의존 설정이 안 되어 있었다. xctags 가 ctags 로 바뀌었다.) 시스템에 설치되어 있는 ctags 명령은 오리지널 ctags 가 아닌 exuberant-ctags 이어야 한다. (ruby 를 이해하고 emacs 용의 TAGS 도 만들 수 있다.)

   module Tags
     SRC_FILES = FileList['**/*.rb'].exclude("pkg") + FileList['**/*.java']
   end

   namespace "tags" do
     task :emacs => "TAGS"
     file "TAGS" => Tags::SRC_FILES do
       puts "Making Emacs TAGS file"
       sh "ctags -e #{Tags::SRC_FILES}", :verbose => false
     end
   end

   task :tags => ["tags:emacs"]

소스 변경이 있으면 rake tags 로 TAGS 파일을 함께 수정해 준다. 뭐 test 테스크가 tags 테스크에 의존하도록 해 놓아도 될 것 같긴 하지만, 그렇게 해보지는 않았다. (기본 설정 상태에서 TAGS 파일의 변경이 있으면 emacs 가 find-tag 수행 전에 다시 읽을 것인지 물어본다. tags-revert-without-query - 이 customizing variable 로 물어 볼지 그냥 항상 다시 읽을지 설정할 수 있다.)

마치며

간단한 글들을 자주 올리려고 했는데, 세번째 글이 꽤 길어져 버렸다. JtestR은 시작한지 얼마 되지 않아 다른 툴들(시도해 본 것은 Eclipse 와 emacs 뿐이지만)과 함께 사용하기에 아쉬운 부분들이 있기는 하지만, 쓸만한 상태에 도달한 것 같다. 아무튼 누군가 JtestR을 써보려 하는 사람이 있다면 이 글이 도움이 되었으면 좋겠다.

2008년 8월 19일 화요일

JtestR - Ruby 도구들을 이용해서 Java 코드 테스트를 쉽게 해주는 도구.

http://jtestr.codehaus.org/
JtestR is a tool that will make it easier to test Java code with state of the art Ruby tools.
(원문의 state of the art 는 번역하지 않았다. 번역한다면 "첨단의 Ruby 도구들을 ..." 정도가 될 것인데, "첨단"이라는 단어는 왠지 어색한 느낌이고, 그렇다고 자연스러운 적절한 단어가 떠오르지도 않았다. 또, 괜한 Language war의 불씨가 될지도 모른다는 생각도 들었고...)
소개 그대로 JtestR은 주 개발 언어가 Java인 프로젝트에서 Ruby 언어로 된 테스트 도구들을 사용할 수 있도록 해주겠다는 것이다. JUnit 등의 Java 자체 테스트 도구들 보다 Test::Unit 이나 RSpec 같은 Ruby 테스트 도구들이 편하다고 생각하는 나 같은 사람에게 딱 좋은 도구다.