OSDN Git Service

ca50c6d944bd1f87b42ce5ba9a67b0e4fd50271d
[ethna/ethna.git] / class / Ethna_UnitTestManager.php
1 <?php
2 /**
3  *  Ethna_UnitTestManager.php
4  *
5  *  @author     Takuya Ookubo <sfio@sakura.ai.to>
6  *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
7  *  @package    Ethna
8  *  @version    $Id$
9  */
10
11 require_once('simpletest/unit_tester.php');
12 require_once('Ethna_UnitTestCase.php');
13 require_once('Ethna_UnitTestReporter.php');
14
15 /**
16  *  Ethnaユニットテストマネージャクラス
17  *
18  *  @author     Takuya Ookubo <sfio@sakura.ai.to>
19  *  @access     public
20  *  @package    Ethna
21  */
22 class Ethna_UnitTestManager extends Ethna_AppManager
23 {
24     /** @var    object  Ethna_Controller    コントローラオブジェクト */
25     var $ctl;
26
27     /** @var    array                       一般テストケース定義 */
28     var $testcase = array();
29
30     /**
31      *  Ethna_UnitTestManagerのコンストラクタ
32      *
33      *  @access public
34      *  @param  object  Ethna_Backend   &$backend   Ethna_Backendオブジェクト
35      */
36     function Ethna_UnitTestManager(&$backend)
37     {
38         parent::Ethna_AppManager($backend);
39         $this->ctl =& Ethna_Controller::getInstance();
40         $this->class_factory =& $this->ctl->getClassFactory();
41         $this->testcase = array_merge($this->testcase, $this->_getTestCaseList()); 
42     }
43
44     /**
45      *  action, view 以外のテストケースの一覧を取得する
46      *
47      *  @access private
48      *  @param  テストケースが含まれているディレクトリ名
49      */
50     function _getTestCaseList($test_dir = NULL)
51     {
52         $r = array();
53
54         if (is_null($test_dir)) {
55             $test_dir = $this->ctl->getTestdir();
56         }
57         $base = $this->ctl->getBasedir();
58
59         $child_dir_list = array();
60
61         $dh = opendir($test_dir);
62         if ($dh == false) {
63             return;
64         }
65
66         $ext = $this->ctl->getExt('php');
67         while (($file = readdir($dh)) !== false) {
68             if ($file == "." || $file == "..") {
69                 continue;
70             }
71             $file = $test_dir . $file;
72
73             if (is_dir($file)) {
74                 $child_dir_list[] = $file;
75                 continue;
76             }
77
78             if (preg_match("/\.$ext\$/", $file) == 0) {
79                 continue;
80             }
81
82             $file = str_replace($this->ctl->getTestdir(), '', $file);
83
84             $key = ereg_replace("^(.*)Test\.$ext", '\1', $file);
85             $key = str_replace('/', '', $key);
86
87             $r[$key] = str_replace($base . '/', '', $this->ctl->getTestdir() . $file);
88         }
89
90         closedir($dh);
91
92         foreach ($child_dir_list as $child_dir) {
93             $tmp = $this->_getTestCaseList($child_dir . "/");
94             $r = array_merge($r, $tmp);
95         }
96
97         return $r;
98     }
99
100     /**
101      *  定義済みアクション一覧を取得する
102      *
103      *  @access public
104      *  @return array   アクション一覧
105      */
106     function _getActionList()
107     {
108         $im =& new Ethna_InfoManager($this->backend);
109         return $im->getActionList();
110     }
111
112     /**
113      *  クラス名からビュー名を取得する
114      *
115      *  @access public
116      *  @param  string  $class_name     ビュークラス名
117      *  @return string  アクション名
118      */
119     function viewClassToName($class_name)
120     {
121         $prefix = sprintf("%s_View_", $this->ctl->getAppId());
122         if (preg_match("/$prefix(.*)/", $class_name, $match) == 0) {
123             // 不明なクラス名
124             return null;
125         }
126         $target = $match[1];
127
128         $action_name = substr(preg_replace('/([A-Z])/e', "'_' . strtolower('\$1')", $target), 1);
129
130         return $action_name;
131     }
132
133     /**
134      *  指定されたクラス名を継承しているかどうかを返す
135      *
136      *  @access private
137      *  @param  string  $class_name     チェック対象のクラス名
138      *  @param  string  $parent_name    親クラス名
139      *  @return bool    true:継承している false:いない
140      */
141     function _isSubclassOf($class_name, $parent_name)
142     {
143         while ($tmp = get_parent_class($class_name)) {
144             if (strcasecmp($tmp, $parent_name) == 0) {
145                 return true;
146             }
147             $class_name = $tmp;
148         }
149         return false;
150     }
151
152     /**
153      *  ビュースクリプトを解析する
154      *
155      *  @access private
156      *  @param  string  $script ファイル名
157      *  @return array   ビュークラス定義一覧
158      */
159     function __analyzeViewScript($script)
160     {
161         $class_list = array();
162
163         $source = "";
164         $fp = fopen($script, 'r');
165         if ($fp == false) {
166             return null;
167         }
168         while (feof($fp) == false) {
169             $source .= fgets($fp, 8192);
170         }
171         fclose($fp);
172
173         // トークンに分割してクラス定義情報を取得
174         $token_list = token_get_all($source);
175         for ($i = 0; $i < count($token_list); $i++) {
176             $token = $token_list[$i];
177
178             if ($token[0] == T_CLASS) {
179                 // クラス定義開始
180                 $i += 2;
181                 $class_name = $token_list[$i][1];       // should be T_STRING
182                 if ($this->_isSubclassOf($class_name, 'Ethna_ViewClass')) {
183                     $view_name = $this->viewClassToName($class_name);
184                     $class_list[$view_name] = array(
185                         'template_file' => $this->ctl->_getForwardPath($view_name),
186                         'view_class' => $class_name,
187                         'view_class_file' => $this->ctl->getDefaultViewPath($view_name),
188                     );
189                 }
190             }
191         }
192
193         if (count($class_list) == 0) {
194             return null;
195         }
196         return $class_list;
197     }
198
199     /**
200      *  ディレクトリ以下のビュースクリプトを解析する
201      *
202      *  @access private
203      *  @param  string  $action_dir     解析対象のディレクトリ
204      *  @return array   ビュークラス定義一覧
205      */
206     function __analyzeViewList($view_dir = null)
207     {
208         $r = array();
209
210         if (is_null($view_dir)) {
211             $view_dir = $this->ctl->getViewdir();
212         }
213         $prefix_len = strlen($this->ctl->getViewdir());
214
215         $ext = '.' . $this->ctl->getExt('php');
216         $ext_len = strlen($ext);
217
218         $dh = opendir($view_dir);
219         if ($dh) {
220             while (($file = readdir($dh)) !== false) {
221                 $path = "$view_dir/$file";
222                 if ($file != '.' && $file != '..' && is_dir($path)) {
223                     $tmp = $this->__analyzeViewList($path);
224                     $r = array_merge($r, $tmp);
225                     continue;
226                 }
227                 if (substr($file, -$ext_len, $ext_len) != $ext) {
228                     continue;
229                 }
230
231                 include_once($path);
232                 $class_list = $this->__analyzeViewScript($path);
233                 if (is_null($class_list) == false) {
234                     $r = array_merge($r, $class_list);
235                 }
236             }
237         }
238         closedir($dh);
239
240         return $r;
241     }
242
243     /**
244      *  定義済みビュー一覧を取得する
245      *
246      *  @access public
247      *  @return array   ビュー一覧
248      */
249     function _getViewList()
250     {
251         $im =& new Ethna_InfoManager($this->backend);
252         //$view_class_list = array_keys($im->getForwardList());
253
254         $r = array();
255
256         // テンプレート/ビュースクリプトを解析する
257         $forward_list = $im->_analyzeForwardList();
258         $view_list = $this->__analyzeViewList();
259
260         // ビュー定義エントリ一覧
261         $manifest_forward_list = $im->_getForwardList_Manifest($forward_list);
262
263         // ビュー定義省略エントリ一覧
264         $implicit_forward_list = $im->_getForwardList_Implicit($forward_list, $manifest_forward_list);
265
266         $r = array_merge($view_list, $manifest_forward_list, $implicit_forward_list);
267         ksort($r);
268
269         return $r;
270     }
271
272     /**
273      *  アクションテストクラスを取得する
274      *
275      *  @access private
276      *  @return array
277      */
278     function _getTestAction()
279     {
280         $action_class_list = array_keys($this->_getActionList());
281
282         // テストの存在するアクション
283         foreach ($action_class_list as $key => $action_name) {
284             $action_class = $this->ctl->getDefaultActionClass($action_name, false).'_TestCase';
285             if (!class_exists($action_class)) {
286                 unset($action_class_list[$key]);
287             }
288         }
289
290         return $action_class_list;
291     }
292
293     /**
294      *  ビューテストクラスを取得する
295      *
296      *  @access private
297      *  @return array
298      */
299     function _getTestView()
300     {
301         $view_class_list = array_keys($this->_getViewList());
302
303         // テストの存在するビュー
304         foreach ($view_class_list as $key => $view_name) {
305             $view_class = $this->ctl->getDefaultViewClass($view_name, false).'_TestCase';
306             if (!class_exists($view_class)) {
307                 unset($view_class_list[$key]);
308             }
309         }
310
311         return $view_class_list;
312     }
313
314     /**
315      *  ユニットテストを実行する
316      *
317      *  @access private
318      *  @return mixed   0:正常終了 Ethna_Error:エラー
319      */
320     function run()
321     {
322         $action_class_list = $this->_getTestAction();
323         $view_class_list = $this->_getTestView();
324
325         $test =& new GroupTest("Ethna UnitTest");
326
327         // アクション
328         foreach ($action_class_list as $action_name) {
329             $action_class = $this->ctl->getDefaultActionClass($action_name, false).'_TestCase';
330             $action_form = $this->ctl->getDefaultFormClass($action_name, false).'_TestCase';
331
332             $test->addTestCase(new $action_class($this->ctl));
333             $test->addTestCase(new $action_form($this->ctl));
334         }
335
336         // ビュー
337         foreach ($view_class_list as $view_name) {
338             $view_class = $this->ctl->getDefaultViewClass($view_name, false).'_TestCase';
339
340             $test->addTestCase(new $view_class($this->ctl));
341         }
342
343         // 一般
344         foreach ($this->testcase as $class_name => $file_name) {
345             $dir = $this->ctl->getBasedir().'/';
346             include_once $dir . $file_name;
347             $testcase_name = $class_name.'_TestCase';
348             $test->addTestCase(new $testcase_name($this->ctl));
349         }
350
351         // ActionFormのバックアップ
352         $af =& $this->ctl->getActionForm();
353
354         //出力したい形式にあわせて切り替える
355         $reporter = new Ethna_UnitTestReporter();
356         $test->run($reporter);
357
358         // ActionFormのリストア
359         $this->ctl->action_form =& $af;
360         $this->backend->action_form =& $af;
361         $this->backend->af =& $af;
362
363         return array($reporter->report, $reporter->result);
364     }
365 }
366 ?>