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