1<?php
2
3/**
4 * Hoa
5 *
6 *
7 * @license
8 *
9 * New BSD License
10 *
11 * Copyright © 2007-2017, Hoa community. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 *     * Redistributions of source code must retain the above copyright
16 *       notice, this list of conditions and the following disclaimer.
17 *     * Redistributions in binary form must reproduce the above copyright
18 *       notice, this list of conditions and the following disclaimer in the
19 *       documentation and/or other materials provided with the distribution.
20 *     * Neither the name of the Hoa nor the names of its contributors may be
21 *       used to endorse or promote products derived from this software without
22 *       specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37namespace Hoa\Math\Test\Unit\Visitor;
38
39use Hoa\Compiler;
40use Hoa\File;
41use Hoa\Math as LUT;
42use Hoa\Math\Visitor\Arithmetic as CUT;
43use Hoa\Regex;
44use Hoa\Test;
45
46/**
47 * Class \Hoa\Math\Test\Unit\Visitor\Arithmetic.
48 *
49 * Test suite of the hoa://Library/Math/Arithmetic.pp grammar and the
50 * Hoa\Math\Visitor\Arithmetic class.
51 *
52 * @copyright  Copyright © 2007-2017 Hoa community
53 * @license    New BSD License
54 */
55class Arithmetic extends Test\Unit\Suite
56{
57    public function case_visitor_exhaustively()
58    {
59        $this
60            ->given(
61                $sampler = new Compiler\Llk\Sampler\BoundedExhaustive(
62                    Compiler\Llk\Llk::load(
63                        new File\Read('hoa://Library/Math/Test/Unit/Arithmetic.pp')
64                    ),
65                    new Regex\Visitor\Isotropic(
66                        new LUT\Sampler\Random()
67                    ),
68                    9
69                ),
70                $compiler = Compiler\Llk\Llk::load(
71                    new File\Read('hoa://Library/Math/Arithmetic.pp')
72                ),
73                $visitor  = new CUT()
74            )
75            ->executeOnFailure(function () use (&$expression) {
76                echo 'Failed expression: ', $expression, '.', "\n";
77            })
78            ->when(function () use (&$sampler, &$compiler, &$visitor) {
79                foreach ($sampler as $i=> $expression) {
80                    try {
81                        $x = (float) $visitor->visit(
82                            $compiler->parse($expression)
83                        );
84                    } catch (\Exception $e) {
85                        continue;
86                    }
87
88                    eval('$y = (float) ' . $expression . ';');
89
90                    if (is_nan($x) || is_nan($y)) {
91                        $this->boolean(true);
92
93                        continue;
94                    }
95
96                    $this
97                        ->float($x)
98                            ->isNearlyEqualTo($y);
99                }
100            });
101    }
102
103    public function case_visitor_unknown_variable()
104    {
105        $this
106            ->given(
107                $compiler     = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')),
108                $visitor      = new CUT(),
109                $variableName = 'unknown_variable'
110            )
111            ->then
112                ->object($compiler->parse($variableName . ' * 2'))
113                    ->isInstanceOf('Hoa\Compiler\Llk\TreeNode')
114                ->exception(function () use ($variableName, $compiler, $visitor) {
115                    $visitor->visit($compiler->parse($variableName . ' * 2'));
116                })
117                    ->isInstanceOf('Hoa\Math\Exception\UnknownVariable');
118    }
119
120    public function case_visitor_variable()
121    {
122        $this
123            ->given(
124                $compiler      = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')),
125                $visitor       = new CUT(),
126                $variableName  = 'a_variable',
127                $variableValue = 42
128            )
129            ->when($visitor->addVariable($variableName, function () use ($variableValue) {
130                return $variableValue;
131            }))
132            ->then
133                ->float($visitor->visit($compiler->parse($variableName . ' * 2')))
134                    ->isEqualTo($variableValue * 2);
135    }
136
137    public function case_visitor_unknown_constant()
138    {
139        $this
140            ->given(
141                $compiler      = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')),
142                $visitor       = new CUT(),
143                $constantName  = 'UNKNOWN_CONSTANT'
144            )
145            ->then
146                ->object($compiler->parse($constantName . ' * 2'))
147                    ->isInstanceOf('Hoa\Compiler\Llk\TreeNode')
148                ->exception(function () use ($constantName, $compiler, $visitor) {
149                    $visitor->visit($compiler->parse($constantName . ' * 2'));
150                })
151                    ->isInstanceOf('Hoa\Math\Exception\UnknownConstant');
152    }
153
154    public function case_visitor_constant()
155    {
156        $this
157            ->given(
158                $compiler      = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')),
159                $visitor       = new CUT(),
160                $constantName  = 'A_CONSTANT',
161                $constantValue = 42
162            )
163            ->when($visitor->addConstant($constantName, $constantValue))
164            ->then
165                ->float($visitor->visit($compiler->parse($constantName . ' * 2')))
166                    ->isEqualTo($constantValue * 2);
167    }
168
169    public function case_visitor_unknown_function()
170    {
171        $this
172            ->given(
173                $compiler       = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')),
174                $visitor        = new CUT(),
175                $functionName   = 'unknown_function'
176            )
177            ->then
178                ->object($compiler->parse($functionName . '() * 2'))
179                    ->isInstanceOf('Hoa\Compiler\Llk\TreeNode')
180                ->exception(function () use ($functionName, $compiler, $visitor) {
181                    $visitor->visit($compiler->parse($functionName . '() * 2'));
182                })
183                    ->isInstanceOf('Hoa\Math\Exception\UnknownFunction');
184    }
185
186    public function case_visitor_function()
187    {
188        $this
189            ->given(
190                $compiler       = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')),
191                $visitor        = new CUT(),
192                $functionName   = 'a_function',
193                $functionResult = 42
194            )
195            ->when($visitor->addFunction($functionName, function () use ($functionResult) {
196                return $functionResult;
197            }))
198            ->then
199                ->float($visitor->visit($compiler->parse($functionName . '() * 2')))
200                    ->isEqualTo($functionResult * 2);
201    }
202
203    public function case_change_default_context()
204    {
205        $this
206            ->given(
207                $compiler       = Compiler\Llk\Llk::load(new File\Read('hoa://Library/Math/Arithmetic.pp')),
208                $visitor        = new CUT(),
209                $context        = new \Mock\Hoa\Math\Context(),
210                $variableName  = 'a_variable',
211                $variableValue = 42
212            )
213            ->when($context->addVariable($variableName, function () use ($variableValue) { return $variableValue; }))
214            ->then
215                ->object($visitor->setContext($context))
216                    ->isNotIdenticalTo($context)
217                ->object($visitor->getContext())
218                    ->isIdenticalTo($context)
219                ->float($visitor->visit($compiler->parse('abs(' . $variableName . ' - PI)')))
220                    ->isEqualTo(abs($variableValue - M_PI))
221                ->mock($context)
222                    ->call('getFunction')->withArguments('abs')->once
223                    ->call('getVariable')->withArguments($variableName)->once
224                    ->call('getConstant')->withArguments('PI')->once;
225    }
226}
227