| // Copyright 2010-2014, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <cmath> |
| #include <cstdlib> |
| #include <fstream> |
| #include <string> |
| |
| #include "base/compiler_specific.h" |
| #include "base/file_util.h" |
| #include "base/logging.h" |
| #include "rewriter/calculator/calculator_interface.h" |
| #include "testing/base/public/gunit.h" |
| |
| DECLARE_string(test_srcdir); |
| |
| namespace mozc { |
| namespace { |
| |
| const char kTestDir[] = "data/test/calculator/"; |
| |
| // Runs calculation with |expression| and compares the result and |expect|. |
| void VerifyCalculation(const CalculatorInterface *calculator, |
| const string &expression, |
| const string &expected) { |
| string result; |
| EXPECT_TRUE(calculator->CalculateString(expression, &result)) |
| << expression << " expected = " << expected; |
| const double result_val = atof(result.c_str()); |
| const double expected_val = atof(expected.c_str()); |
| const double err = fabs(result_val - expected_val); |
| |
| EXPECT_DOUBLE_EQ(expected_val, result_val) |
| << "comparison: " << result_val << " vs " << expected_val << endl |
| << "error: " << err << endl |
| << "expr = " << expression << endl |
| << "result = " << result; |
| } |
| |
| // Runs calculation and compare results in PRINTED string. |
| void VerifyCalculationInString(const CalculatorInterface *calculator, |
| const string &expression, |
| const string &expected) { |
| string result; |
| EXPECT_TRUE(calculator->CalculateString(expression, &result)) |
| << expression << " expected = " << expected; |
| EXPECT_EQ(expected, result) |
| << "expr = " << expression << endl; |
| } |
| |
| // Tries to calculate |wrong_key| and returns true if it fails. |
| void VerifyRejection(const CalculatorInterface *calculator, |
| const string &wrong_key) { |
| string result; |
| EXPECT_FALSE(calculator->CalculateString(wrong_key, &result)) |
| << "expression: " << wrong_key << endl; |
| } |
| |
| } // anonymous namespace |
| |
| TEST(CalculatorTest, BasicTest) { |
| CalculatorInterface *calculator = CalculatorFactory::GetCalculator(); |
| |
| // These are not expressions |
| // apparently |
| VerifyRejection(calculator, "test"); |
| // Expressoin must be ended with equal '='. |
| VerifyRejection(calculator, "5+4"); |
| // Expression must include at least one operator other than parentheses. |
| VerifyRejection(calculator, "111="); |
| VerifyRejection(calculator, "(5)="); |
| // Expression must include at least one number. |
| VerifyRejection(calculator, "()="); |
| |
| // Test for each operators |
| VerifyCalculation(calculator, "38+2.5=", "40.5"); |
| VerifyCalculation(calculator, "5.5-21=", "-15.5"); |
| VerifyCalculation(calculator, "4*2.1=", "8.4"); |
| VerifyCalculation(calculator, "8/2=", "4"); |
| // "15・3=" |
| VerifyCalculation(calculator, "15\xE3\x83\xBB""3=", "5"); |
| VerifyCalculation(calculator, "100%6=", "4"); |
| VerifyCalculation(calculator, "2^10=", "1024"); |
| VerifyCalculation(calculator, "4*-2=", "-8"); |
| VerifyCalculation(calculator, "-10.3+3.5=", "-6.8"); |
| |
| // Full width cases (some operators may appear as full width character). |
| // "12345+67890=" |
| VerifyCalculation(calculator, |
| "\xEF\xBC\x91\xEF\xBC\x92\xEF\xBC\x93\xEF\xBC\x94\xEF\xBC" |
| "\x95\xEF\xBC\x8B\xEF\xBC\x96\xEF\xBC\x97\xEF\xBC\x98\xEF" |
| "\xBC\x99\xEF\xBC\x90\xEF\xBC\x9D", |
| "80235"); |
| // "5−1=" |
| VerifyCalculation(calculator, "5\xE2\x88\x92""1=", "4"); |
| // "-ー3+5=" |
| VerifyCalculation(calculator, "-\xE3\x83\xBC""3+5=", "8"); |
| // "1.5*2=" |
| VerifyCalculation(calculator, "1\xEF\xBC\x8E""5\xEF\xBC\x8A""2=", "3"); |
| // "10/2=" |
| VerifyCalculation(calculator, "10\xEF\xBC\x8F""2=", "5"); |
| // "2^ー2=" |
| VerifyCalculation(calculator, "2\xEF\xBC\xBE\xE3\x83\xBC""2=", "0.25"); |
| // "13%3=" |
| VerifyCalculation(calculator, "13\xEF\xBC\x85""3=", "1"); |
| // "(1+1)*2=" |
| VerifyCalculation(calculator, |
| "\xEF\xBC\x88""1+1\xEF\xBC\x89*2=", |
| "4"); |
| |
| // Expressions with more than one operator. |
| VerifyCalculation(calculator, "(1+2)-4=", "-1"); |
| VerifyCalculation(calculator, "5*(2+3)=", "25"); |
| VerifyCalculation(calculator, "(70-((3+2)*4))%8=", "2"); |
| |
| // Issue 3082576: 7472.4 - 7465.6 = 6.7999999999993 is not expected. |
| VerifyCalculationInString(calculator, "7472.4-7465.6=", "6.8"); |
| } |
| |
| // Test large number of queries. Test data is located at |
| // data/test/calculator/testset.txt. |
| // In this file, each test case is written in one line in the format |
| // "expression=answer". Answer is suppressed if the expression is invalid, |
| // i.e. it is a false test. |
| TEST(CalculatorTest, StressTest) { |
| const string filename = FileUtil::JoinPath(FLAGS_test_srcdir, |
| string(kTestDir) + "testset.txt"); |
| EXPECT_TRUE(FileUtil::FileExists(filename)) << "Could not read: " << filename; |
| |
| CalculatorInterface *calculator = CalculatorFactory::GetCalculator(); |
| |
| ifstream finput(filename.c_str()); |
| string line; |
| int lineno = 0; |
| while (getline(finput, line)) { |
| ++lineno; |
| |
| // |line| is of format "expression=answer". |
| const size_t index_of_equal = line.find('='); |
| DCHECK(index_of_equal != string::npos); |
| const size_t query_length = index_of_equal + 1; |
| const string query(line, 0, query_length); |
| |
| // Smoke test. |
| // If (OS_ANDROID && x86) the result differs from expectation |
| // because of floating point specification so on such environment |
| // Following verification is skipped. |
| string unused_result; |
| calculator->CalculateString(query, &unused_result); |
| #if !defined(OS_ANDROID) || !defined(__i386__) |
| if (line.size() == query_length) { |
| // False test |
| VerifyRejection(calculator, line); |
| continue; |
| } |
| const string answer(line, query_length); |
| VerifyCalculation(calculator, query, answer); |
| #endif // !defined(OS_ANDROID) || !defined(__i386__) |
| } |
| LOG(INFO) << "done " << lineno << " tests from " << filename << endl; |
| } |
| |
| } // namespace mozc |