Completed
Pull Request — develop (#720)
by Imants
03:58
created

Page_Tester::runTests()   F

Complexity

Conditions 23
Paths 1952

Size

Total Lines 122
Code Lines 76

Duplication

Lines 23
Ratio 18.85 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
cc 23
c 4
b 1
f 0
dl 23
loc 122
rs 2
eloc 76
nc 1952
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Tester page is a basic implementation of a testing environment for Agile Toolkit.
4
 * See Documentation for testing
5
 */
6
class Page_Tester extends Page
7
{
8
    public $variances = array();
9
    public $input;
10
    public $responses = array();
11
    public $auto_test = true;
12
13
    /** Redefine this to the value generated by a test */
14
    public $proper_responses = null;
15
16
    /** @var Grid */
17
    protected $grid;
18
19
    public function init()
20
    {
21
        parent::init();
22
23
        if (!$this->auto_test) {
24
            $this->setVariance(array('Test'));
25
26
            return;    // used for multi-page testing
27
        }
28
        $this->grid = $this->add('Grid');
29
        /** @type Grid $this->grid */
30
        $this->grid->addColumn('template', 'name')
0 ignored issues
show
Bug introduced by
The method setTemplate does only exist in Grid, but not in Controller_Grid_Format.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
31
            ->setTemplate('<a href="'.$this->app->url(null, array('testonly' => '')).'<?$name?>"><?$name?></a>');
32
33
        $this->setVariance(array('Test'));
34
35
        //$this->setVariance(array('GiTemplate','SMlite'));
36
37
        $this->runTests();
38
39 View Code Duplication
        if (!$_GET['testonly']) {
40
            /** @type Form $f */
41
            $f = $this->add('Form');
42
            $ff = $f->addField('Text', 'responses');
43
            $this->responses =
44
                '    public $proper_responses=array(
0 ignored issues
show
Documentation Bug introduced by
It seems like ' public $proper_resp.... ' );' of type string is incompatible with the declared type array of property $responses.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
45
                '.implode(',
46
                ', $this->responses).'
47
                );';
48
            $ff->set($this->responses);
49
            $ff->js('click')->select();
50
        }
51
    }
52 View Code Duplication
    public function setVariance($arr)
53
    {
54
        $this->variances = $arr;
55
        if (isset($this->grid)) {
56
            foreach ($arr as $key => $item) {
57
                if (is_numeric($key)) {
58
                    $key = $item;
59
                }
60
                $this->grid->addColumn('html', $key.'_inf', $key.' info');
61
                $this->grid->addColumn('html,wrap', $key.'_res', $key.' result');
62
            }
63
        }
64
    }
65
    public function skipTests($msg = null)
66
    {
67
        throw $this->exception($msg, 'SkipTests');
68
    }
69
    public $cnt;
70
    public function ticker()
71
    {
72
        ++$this->cnt;
73
    }
74 View Code Duplication
    public function executeTest($test_obj, $test_func, $input)
75
    {
76
        if ($input === null) {
77
            $input = array();
78
        }
79
80
        return call_user_func_array(array($test_obj, $test_func), $input);
81
    }
82 View Code Duplication
    public function silentTest($test_obj = null)
83
    {
84
        if (!$test_obj) {
85
            $test_obj = $this;
86
        }
87
88
        $total = $success = $fail = $exception = 0;
89
        $speed = $memory = 0;
90
91
        $tested = array();
92
        $failures = array();
93
        foreach (get_class_methods($test_obj) as $method) {
94
            if (strpos($method, 'test_') === 0) {
95
                $m = substr($method, 5);
96
            } elseif (strpos($method, 'prepare_') === 0) {
97
                $m = substr($method, 8);
98
            } else {
99
                continue;
100
            }
101
            if ($tested[$m]) {
102
                continue;
103
            }
104
            $tested[$m] = true;
105
106
            foreach ($this->variances as $key => $vari) {
107
                if (is_numeric($key)) {
108
                    $key = $vari;
109
                }
110
111
                // Input is a result of preparation function
112
                try {
113
                    if (method_exists($test_obj, 'prepare_'.$m)) {
114
                        $input = $test_obj->{'prepare_'.$m}($vari, $method);
115
                    } else {
116
                        if ($test_obj->hasMethod('prepare')) {
117
                            $input = $test_obj->prepare($vari, $method);
118
                        } else {
119
                            $input = null;
120
                        }
121
                    }
122
                } catch (Exception $e) {
123
                    if ($e instanceof Exception_SkipTests) {
124
                        return array(
125
                            'skipped' => $e->getMessage(),
126
                        );
127
                    }
128
                    throw $e;
129
                }
130
131
                $this->input = $input;
132
133
                $test_func = method_exists($test_obj, 'test_'.$m) ?
134
                    'test_'.$m : 'test';
135
136
                ++$total;
137
138
                $me = memory_get_peak_usage();
139
                $ms = microtime(true);
140
141
                $this->cnt = 0;
142
                declare (ticks = 1);
143
                register_tick_function(array($this, 'ticker'));
144
145
                try {
146
                    $result = $this->executeTest($test_obj, $test_func, $input);
147
                    $ms = microtime(true) - $ms;
148
                    $me = ($mend = memory_get_peak_usage()) - $me;
149
150
                    $result = $this->formatResult($row, $key, $result);
151
152
                    $k = $key.'_'.$m;
153
                    if ($this->proper_responses[$k] == $result && isset($this->proper_responses[$k])) {
154
                        ++$success;
155
                    } else {
156
                        $failures[] = $method;
157
                        ++$fail;
158
                    }
159
                } catch (Exception $e) {
160
                    if ($e instanceof Exception_SkipTests) {
161
                        return array(
162
                            'skipped' => $e->getMessage(),
163
                        );
164
                    }
165
166
                    ++$exception;
167
168
                    $ms = microtime(true) - $ms;
169
                    $me = ($mend = memory_get_peak_usage()) - $me;
170
                }
171
172
                unregister_tick_function(array($this, 'ticker'));
173
174
                $speed += $this->cnt * 1;
175
                $memory += $me;
176
            }
177
        }
178
179
        return array(
180
            'total' => $total,
181
            'failures' => $failures,
182
            'success' => $success,
183
            'exception' => $exception,
184
            'fail' => $fail,
185
            'speed' => $speed,
186
            'memory' => $memory,
187
            );
188
    }
189
    public function runTests($test_obj = null)
190
    {
191
        if (!$test_obj) {
192
            $test_obj = $this;
193
        }
194
195
        $tested = array();
196
        $data = array();
197
        foreach (get_class_methods($test_obj) as $method) {
198
            if (strpos($method, 'test_') === 0) {
199
                $m = substr($method, 5);
200
            } elseif (strpos($method, 'prepare_') === 0) {
201
                $m = substr($method, 8);
202
            } else {
203
                continue;
204
            }
205
206
            if (isset($_GET['testonly']) && 'test_'.$_GET['testonly'] != $method) {
207
                continue;
208
            }
209
210
            // Do not retest same function even if it has both prepare and test
211
            if ($tested[$m]) {
212
                continue;
213
            }
214
            $tested[$m] = true;
215
216
            // Row contains test result data
217
            $row = array('name' => $m, 'id' => $m);
218
219
            foreach ($this->variances as $key => $vari) {
220
                if (is_numeric($key)) {
221
                    $key = $vari;
222
                }
223
224
                try {
225
                    // Input is a result of preparation function
226
                    if (method_exists($test_obj, 'prepare_'.$m)) {
227
                        $input = $test_obj->{'prepare_'.$m}($vari, $method);
228
                    } else {
229
                        if ($test_obj->hasMethod('prepare')) {
230
                            $input = $test_obj->prepare($vari, $method);
231
                        } else {
232
                            $input = null;
233
                        }
234
                    }
235
                } catch (Exception $e) {
236 View Code Duplication
                    if ($e instanceof Exception_SkipTests) {
237
                        $this->grid->destroy();
238
                        /** @type View_Error $v_error */
239
                        $v_error = $this->add('View_Error');
240
                        $v_error->set('Skipping all tests: '.$e->getMessage());
241
242
                        return;
243
                    }
244
                }
245
246
                $this->input = $input;
0 ignored issues
show
Bug introduced by
The variable $input does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
247
248
                $test_func = method_exists($test_obj, 'test_'.$m) ?
249
                    'test_'.$m : 'test';
250
251
                // Test speed
252
                $me = memory_get_peak_usage();
253
                $ms = microtime(true);
254
                $this->cnt = 0;
255
                declare (ticks = 1);
256
                register_tick_function(array($this, 'ticker'));
257
                try {
258
                    //$result=$test_obj->$test_func($input[0],$input[1],$input[2]);
259
                    $result = $this->executeTest($test_obj, $test_func, $input);
260
                } catch (Exception $e) {
261 View Code Duplication
                    if ($e instanceof Exception_SkipTests) {
262
                        $this->grid->destroy();
263
                        /** @type View_Error $v_error */
264
                        $v_error = $this->add('View_Error');
265
                        $v_error->set('Skipping all tests: '.$e->getMessage());
266
                    }
267
268 View Code Duplication
                    if ($_GET['tester_details'] == $row['name'] && $_GET['vari'] == $vari) {
269
                        throw $e;
270
                    }
271
272
                    $result = 'Exception: '.($e instanceof BaseException ? $e->getText() : $e->getMessage());
273
274
                    /** @type P $ll */
275
                    $ll = $this->add('P', $row['name']);
276
                    /** @type View $v */
277
                    $v = $ll->add('View');
278
                    $v->setElement('a')
279
                        ->setAttr('href', '#')
280
                        ->set('More details')
281
                        ->js('click')->univ()->frameURL(
282
                            'Exception Details for test '.$row['name'],
283
                            $this->app->url(null, array('tester_details' => $row['name'], 'vari' => $vari))
284
                        );
285
286
                    $result .= $ll->getHTML();
287
                }
288
                $ms = microtime(true) - $ms;
289
                $me = ($mend = memory_get_peak_usage()) - $me;
290
                unregister_tick_function(array($this, 'ticker'));
291
292
                $row[$key.'_inf'] = 'Ticks: '.($this->cnt * 1).'<br/>Memory: '.$me;
293
294
                $result = $this->formatResult($row, $key, $result);
295
296
                $k = $key.'_'.$row['name'];
297 View Code Duplication
                if ($this->proper_responses[$k] == $result && isset($this->proper_responses[$k])) {
298
                    $row[$key.'_res'] = '<font color="green">PASS</font><br/>'.htmlspecialchars($result);
299
                } elseif ($this->proper_responses[$k]) {
300
                    $row[$key.'_res'] = '<font color="red">'.htmlspecialchars($result).'</font><br/>'.
301
                        var_export($this->proper_responses[$k], true);
302
                }
303
304
                $this->responses[] = '"'.$k.'"'.'=>'.var_export($result, true);
305
            }
306
307
            $data[] = $row;
308
        }
309
        $this->grid->setSource($data);
310
    }
311
    public function formatResult(&$row, $key, $result)
312
    {
313
        $row[$key.'_res'] = $result;
314
315
        return (string) $result;
316
    }
317
    public function expect($value, $expectation)
318
    {
319
        return $value == $expectation ? 'OK' : 'ERR';
320
    }
321
322 View Code Duplication
    public function _prepare($t, $str)
323
    {
324
        $result = '';
325
326
        for ($i = 0; $i < 100; ++$i) {
327
            $result .= $str;
328
        }
329
330
        return array($this->add($t), $result);
331
    }
332
}
333