1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include <cmath>
31 #include <cstdlib>
32 #include <fstream>
33 #include <string>
34 
35 #include "base/compiler_specific.h"
36 #include "base/file_util.h"
37 #include "base/logging.h"
38 #include "rewriter/calculator/calculator_interface.h"
39 #include "testing/base/public/gunit.h"
40 #include "testing/base/public/mozctest.h"
41 
42 namespace mozc {
43 namespace {
44 
45 // Runs calculation with |expression| and compares the result and |expect|.
VerifyCalculation(const CalculatorInterface * calculator,const string & expression,const string & expected)46 void VerifyCalculation(const CalculatorInterface *calculator,
47                        const string &expression,
48                        const string &expected) {
49   string result;
50   EXPECT_TRUE(calculator->CalculateString(expression, &result))
51       << expression << "  expected = " << expected;
52   const double result_val = atof(result.c_str());
53   const double expected_val = atof(expected.c_str());
54   const double err = fabs(result_val - expected_val);
55 
56   EXPECT_DOUBLE_EQ(expected_val, result_val)
57       << "comparison: " << result_val << " vs " << expected_val << std::endl
58       << "error: " << err << std::endl
59       << "expr = " << expression << std::endl
60       << "result = " << result;
61 }
62 
63 // Runs calculation and compare results in PRINTED string.
VerifyCalculationInString(const CalculatorInterface * calculator,const string & expression,const string & expected)64 void VerifyCalculationInString(const CalculatorInterface *calculator,
65                                const string &expression,
66                                const string &expected) {
67   string result;
68   EXPECT_TRUE(calculator->CalculateString(expression, &result))
69       << expression << "  expected = " << expected;
70   EXPECT_EQ(expected, result) << "expr = " << expression << std::endl;
71 }
72 
73 // Tries to calculate |wrong_key| and returns true if it fails.
VerifyRejection(const CalculatorInterface * calculator,const string & wrong_key)74 void VerifyRejection(const CalculatorInterface *calculator,
75                      const string &wrong_key) {
76   string result;
77   EXPECT_FALSE(calculator->CalculateString(wrong_key, &result))
78       << "expression: " << wrong_key << std::endl;
79 }
80 
81 }  // namespace
82 
TEST(CalculatorTest,BasicTest)83 TEST(CalculatorTest, BasicTest) {
84   CalculatorInterface *calculator = CalculatorFactory::GetCalculator();
85 
86   // These are not expressions
87   // apparently
88   VerifyRejection(calculator, "test");
89   // Expressoin must be ended with equal '='.
90   VerifyRejection(calculator, "5+4");
91   // Expression must include at least one operator other than parentheses.
92   VerifyRejection(calculator, "111=");
93   VerifyRejection(calculator, "(5)=");
94   // Expression must include at least one number.
95   VerifyRejection(calculator, "()=");
96   // Expression with both heading and tailing '='s should be rejected.
97   VerifyRejection(calculator, "=(0-0)=");
98 
99   // Test for each operators
100   VerifyCalculation(calculator, "38+2.5=", "40.5");
101   VerifyCalculation(calculator, "5.5-21=", "-15.5");
102   VerifyCalculation(calculator, "4*2.1=", "8.4");
103   VerifyCalculation(calculator, "8/2=", "4");
104   VerifyCalculation(calculator, "15・3=", "5");
105   VerifyCalculation(calculator, "100%6=", "4");
106   VerifyCalculation(calculator, "2^10=", "1024");
107   VerifyCalculation(calculator, "4*-2=", "-8");
108   VerifyCalculation(calculator, "-10.3+3.5=", "-6.8");
109   // Expression can starts with '=' instead of ending with '='.
110   VerifyCalculation(calculator, "=-10.3+3.5", "-6.8");
111 
112   // Full width cases (some operators may appear as full width character).
113   VerifyCalculation(calculator, "12345+67890=", "80235");
114   VerifyCalculation(calculator, "5−1=", "4");     // − is U+2212
115   VerifyCalculation(calculator, "-ー3+5=", "8");  // ー is U+30FC
116   VerifyCalculation(calculator, "1.5*2=", "3");
117   VerifyCalculation(calculator, "10/2=", "5");
118   VerifyCalculation(calculator, "2^ー2=", "0.25");
119   VerifyCalculation(calculator, "13%3=", "1");
120   VerifyCalculation(calculator, "(1+1)*2=", "4");
121 
122   // Expressions with more than one operator.
123   VerifyCalculation(calculator, "(1+2)-4=", "-1");
124   VerifyCalculation(calculator, "5*(2+3)=", "25");
125   VerifyCalculation(calculator, "(70-((3+2)*4))%8=", "2");
126 
127   // Issue 3082576: 7472.4 - 7465.6 = 6.7999999999993 is not expected.
128   VerifyCalculationInString(calculator, "7472.4-7465.6=", "6.8");
129 }
130 
131 // Test large number of queries.  Test data is located at
132 // data/test/calculator/testset.txt.
133 // In this file, each test case is written in one line in the format
134 // "expression=answer".  Answer is suppressed if the expression is invalid,
135 // i.e. it is a false test.
TEST(CalculatorTest,StressTest)136 TEST(CalculatorTest, StressTest) {
137   const string filename = testing::GetSourceFileOrDie({
138       "data", "test", "calculator", "testset.txt"});
139   CalculatorInterface *calculator = CalculatorFactory::GetCalculator();
140 
141   std::ifstream finput(filename.c_str());
142   string line;
143   int lineno = 0;
144   while (getline(finput, line)) {
145     ++lineno;
146 
147     // |line| is of format "expression=answer".
148     const size_t index_of_equal = line.find('=');
149     DCHECK(index_of_equal != string::npos);
150     const size_t query_length = index_of_equal + 1;
151     const string query(line, 0, query_length);
152 
153     // Smoke test.
154     // If (OS_ANDROID && x86) the result differs from expectation
155     // because of floating point specification so on such environment
156     // Following verification is skipped.
157     string unused_result;
158     calculator->CalculateString(query, &unused_result);
159 #if !defined(OS_ANDROID) || !defined(__i386__)
160     if (line.size() == query_length) {
161       // False test
162       VerifyRejection(calculator, line);
163       continue;
164     }
165     const string answer(line, query_length);
166     VerifyCalculation(calculator, query, answer);
167 #endif  // !defined(OS_ANDROID) || !defined(__i386__)
168   }
169   LOG(INFO) << "done " << lineno << " tests from " << filename << std::endl;
170 }
171 
172 }  // namespace mozc
173