Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions data/config/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,19 @@ filegroup(
name = "config",
srcs = glob(["*.json"]),
)

cc_test(
name = "config_dict_validation_test",
srcs = ["ConfigDictValidationTest.cpp"],
data = [
":config",
"//data/dictionary:binary_dictionaries",
"//test/testcases",
],
deps = [
"//src:simple_converter",
"@bazel_tools//tools/cpp/runfiles",
"@googletest//:gtest_main",
"@rapidjson",
],
)
109 changes: 109 additions & 0 deletions data/config/ConfigDictValidationTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Open Chinese Convert
*
* End-to-end validation of all configs against consolidated testcases.json.
*/

#ifndef BAZEL
// This test is Bazel-only; CMake builds should skip compiling it.
static_assert(false, "ConfigDictValidationTest is only supported under Bazel");
#else

#include <fstream>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>

#include "gtest/gtest.h"
#include "rapidjson/document.h"
#include "src/SimpleConverter.hpp"

#include "tools/cpp/runfiles/runfiles.h"
using bazel::tools::cpp::runfiles::Runfiles;

namespace opencc {
namespace {

class ConfigDictValidationTest : public ::testing::Test {
protected:
void SetUp() override {
#ifdef BAZEL
runfiles_.reset(Runfiles::CreateForTest());
ASSERT_NE(nullptr, runfiles_);
testcasesPath_ = runfiles_->Rlocation("_main/test/testcases/testcases.json");
configDir_ = runfiles_->Rlocation("_main/data/config");
dictDir_ = runfiles_->Rlocation("_main/data/dictionary");
#else
FAIL() << "This test expects Bazel runfiles.";
#endif
Comment thread
frankslin marked this conversation as resolved.
}

std::string ReadFile(const std::string& path) {
std::ifstream ifs(path);
EXPECT_TRUE(ifs.is_open()) << path;
std::stringstream buffer;
buffer << ifs.rdbuf();
return buffer.str();
}

SimpleConverter& GetConverter(const std::string& config) {
auto it = converters_.find(config);
if (it != converters_.end()) {
return *it->second;
}
const std::string configPath = configDir_ + "/" + config + ".json";
auto inserted = converters_.emplace(
config,
std::make_unique<SimpleConverter>(configPath,
std::vector<std::string>{
configDir_, dictDir_}));
return *inserted.first->second;
}

std::unique_ptr<Runfiles> runfiles_;
std::string testcasesPath_;
std::string configDir_;
std::string dictDir_;
std::unordered_map<std::string, std::unique_ptr<SimpleConverter>>
converters_;
};

TEST_F(ConfigDictValidationTest, ConvertExpectedOutputs) {
const std::string json = ReadFile(testcasesPath_);
rapidjson::Document doc;
doc.Parse(json.c_str());
ASSERT_FALSE(doc.HasParseError());
ASSERT_TRUE(doc.IsObject());
ASSERT_TRUE(doc.HasMember("cases"));
const auto& cases = doc["cases"];
ASSERT_TRUE(cases.IsArray());

for (auto& testcase : cases.GetArray()) {
ASSERT_TRUE(testcase.IsObject());
ASSERT_TRUE(testcase.HasMember("input"));
ASSERT_TRUE(testcase["input"].IsString());
const std::string input = testcase["input"].GetString();
const std::string id =
testcase.HasMember("id") && testcase["id"].IsString()
? testcase["id"].GetString()
: "(unknown id)";
ASSERT_TRUE(testcase.HasMember("expected"));
const auto& expectedObj = testcase["expected"];
ASSERT_TRUE(expectedObj.IsObject());
for (auto itr = expectedObj.MemberBegin(); itr != expectedObj.MemberEnd();
++itr) {
const std::string config = itr->name.GetString();
ASSERT_TRUE(itr->value.IsString());
const std::string expected = itr->value.GetString();
SimpleConverter& converter = GetConverter(config);
EXPECT_EQ(expected, converter.Convert(input))
<< "config=" << config << " case=" << id;
}
}
}

} // namespace
} // namespace opencc

#endif // BAZEL
10 changes: 9 additions & 1 deletion data/dictionary/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ genrule(
tools = ["//data/scripts:merge"],
)

# These component files are only used for merge_TWPhrases and should not
# produce standalone .ocd2 outputs.
PHRASE_PARTS = [
"TWPhrasesIT.txt",
"TWPhrasesName.txt",
"TWPhrasesOther.txt",
]

[
genrule(
name = "reverse_" + txt,
Expand All @@ -30,7 +38,7 @@ genrule(
]
]

TEXT_DICTS = glob(["*.txt"]) + [
TEXT_DICTS = glob(["*.txt"], exclude = PHRASE_PARTS) + [
"TWPhrases.txt",
"TWVariantsRev.txt",
"TWPhrasesRev.txt",
Expand Down
13 changes: 6 additions & 7 deletions data/dictionary/DictionaryTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,12 @@ std::string DictionaryTest::runfile_dir_;

INSTANTIATE_TEST_SUITE_P(
, DictionaryTest,
::testing::Values("HKVariants", "HKVariantsRevPhrases",
"JPShinjitaiCharacters", "JPShinjitaiPhrases",
"JPVariants", "STCharacters", "STPhrases", "TSCharacters",
"TSPhrases", "TWPhrasesIT", "TWPhrasesName",
"TWPhrasesOther", "TWVariants", "TWVariantsRevPhrases",
"TWPhrases", "TWVariantsRev", "TWPhrasesRev",
"HKVariantsRev", "JPVariantsRev"),
::testing::Values(
"HKVariants", "HKVariantsRev", "HKVariantsRevPhrases",
"JPShinjitaiCharacters", "JPShinjitaiPhrases", "JPVariants",
"JPVariantsRev", "STCharacters", "STPhrases", "TSCharacters",
"TSPhrases", "TWPhrases", "TWPhrasesRev", "TWVariants",
"TWVariantsRev", "TWVariantsRevPhrases"),
[](const testing::TestParamInfo<DictionaryTest::ParamType>& info) {
return info.param;
});
Expand Down
89 changes: 30 additions & 59 deletions node/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,85 +4,56 @@ const util = require('util');

const OpenCC = require('./opencc');

const configs = [
'hk2s',
'hk2t',
'jp2t',
's2hk',
's2t',
's2tw',
's2twp',
't2hk',
't2jp',
't2s',
'tw2s',
'tw2sp',
'tw2t',
];
const cases = JSON.parse(fs.readFileSync('test/testcases/testcases.json', 'utf-8')).cases || [];

const testSync = function (config, done) {
const inputName = 'test/testcases/' + config + '.in';
const outputName = 'test/testcases/' + config + '.ans';
const configName = config + '.json';
const opencc = new OpenCC(configName);
const text = fs.readFileSync(inputName, 'utf-8');
const converted = opencc.convertSync(text);
const answer = fs.readFileSync(outputName, 'utf-8');
assert.equal(converted, answer);
const testSync = function (tc, cfg, expected, done) {
const opencc = new OpenCC(cfg + '.json');
const converted = opencc.convertSync(tc.input);
assert.equal(converted, expected);
done();
};

const testAsync = function (config, done) {
const inputName = 'test/testcases/' + config + '.in';
const outputName = 'test/testcases/' + config + '.ans';
const configName = config + '.json';
const opencc = new OpenCC(configName);
fs.readFile(inputName, 'utf-8', function (err, text) {
const testAsync = function (tc, cfg, expected, done) {
const opencc = new OpenCC(cfg + '.json');
opencc.convert(tc.input, function (err, converted) {
if (err) return done(err);
opencc.convert(text, function (err, converted) {
if (err) return done(err);
fs.readFile(outputName, 'utf-8', function (err, answer) {
if (err) return done(err);
assert.equal(converted, answer);
done();
});
});
assert.equal(converted, expected);
done();
});
};

async function testAsyncPromise(config) {
const inputName = 'test/testcases/' + config + '.in';
const outputName = 'test/testcases/' + config + '.ans';
const configName = config + '.json';
const opencc = new OpenCC(configName);

const text = await util.promisify(fs.readFile)(inputName, 'utf-8');
const converted = await opencc.convertPromise(text);
const answer = await util.promisify(fs.readFile)(outputName, 'utf-8');

assert.equal(converted, answer);
};
async function testAsyncPromise(tc, cfg, expected) {
const opencc = new OpenCC(cfg + '.json');
const converted = await opencc.convertPromise(tc.input);
assert.equal(converted, expected);
}

describe('Sync API', function () {
configs.forEach(function (config) {
it(config, function (done) {
testSync(config, done);
cases.forEach(function (tc, idx) {
Object.entries(tc.expected || {}).forEach(function ([cfg, expected]) {
it('[' + cfg + '] case #' + (idx + 1), function (done) {
testSync(tc, cfg, expected, done);
});
});
});
});

describe('Async API', function () {
configs.forEach(function (config) {
it(config, function (done) {
testAsync(config, done);
cases.forEach(function (tc, idx) {
Object.entries(tc.expected || {}).forEach(function ([cfg, expected]) {
it('[' + cfg + '] case #' + (idx + 1), function (done) {
testAsync(tc, cfg, expected, done);
});
});
});
});

describe('Async Promise API', function () {
configs.forEach(function (config) {
it(config, function (done) {
testAsyncPromise(config).then(done);
cases.forEach(function (tc, idx) {
Object.entries(tc.expected || {}).forEach(function ([cfg, expected]) {
it('[' + cfg + '] case #' + (idx + 1), function (done) {
testAsyncPromise(tc, cfg, expected).then(() => done(), done);
});
});
});
});
33 changes: 14 additions & 19 deletions python/tests/test_opencc.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
from __future__ import unicode_literals

import json
import os
import pytest
import sys

from glob import glob

_this_dir = os.path.dirname(os.path.abspath(__file__))
_opencc_rootdir = os.path.abspath(os.path.join(_this_dir, '..', '..'))
_test_assets_dir = os.path.join(_opencc_rootdir, 'test', 'testcases')
_testcases_path = os.path.join(_opencc_rootdir, 'test', 'testcases', 'testcases.json')


def test_import():
Expand All @@ -26,22 +25,18 @@ def test_init_delete_converter():
def test_conversion():
import opencc

for inpath in glob(os.path.join(_test_assets_dir, '*.in')):
pref = os.path.splitext(inpath)[0]
config = os.path.basename(pref)
converter = opencc.OpenCC(config)
anspath = '{}.{}'.format(pref, 'ans')
assert os.path.isfile(anspath)

with open(inpath, 'rb') as f:
intexts = [l.strip().decode('utf-8') for l in f]
with open(anspath, 'rb') as f:
anstexts = [l.strip().decode('utf-8') for l in f]
assert len(intexts) == len(anstexts)

for text, ans in zip(intexts, anstexts):
assert converter.convert(text) == ans, \
'Failed to convert {} for {} -> {}'.format(pref, text, ans)
with open(_testcases_path, 'r', encoding='utf-8') as f:
parsed = json.load(f)

for case in parsed.get('cases', []):
input_text = case.get('input')
expected = case.get('expected', {})
if not input_text or not isinstance(expected, dict):
continue
for cfg, ans in expected.items():
converter = opencc.OpenCC(f'{cfg}.json')
assert converter.convert(input_text) == ans, \
'Failed to convert {} for {} -> {}'.format(cfg, input_text, ans)


if __name__ == "__main__":
Expand Down
1 change: 1 addition & 0 deletions test/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ cc_test(
"//src:common",
"@bazel_tools//tools/cpp/runfiles",
"@googletest//:gtest_main",
"@rapidjson",
],
)
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
include_directories("${PROJECT_SOURCE_DIR}")
include_directories("${PROJECT_BINARY_DIR}/src")
include_directories("${PROJECT_SOURCE_DIR}/src")
include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson-1.1.0")

set(CONFIG_TEST
config_test/config_test.json
Expand Down
Loading
Loading