2012年9月20日木曜日

Google Test の出力XMLに日本語を出力する

Google Test で日本語を出力したり、日本語テスト名(VC)を使うと XML に文字化けした状態で出力されてしまいます。
#ifdef _MSC_VER
TEST(Japanese, テスト)
{
    FAIL() << "テスト";
}
#endif

出力されたXML
<testsuite name="Japanese" tests="1" failures="1" disabled="0" errors="0" time="0.001">
    <testcase name="eXg" status="run" time="0" classname="Japanese">
      <failure message="Failed" type=""><![CDATA[samples.cpp:16
Failed]]></failure>
    </testcase>
  </testsuite>
テスト名「テスト」が「eXg」になってしまっています。(先行バイトが抜けてる)
FAIL() で出力した「テスト」メッセージも無き者にされてしまっています。

今回は日本語出力ができるようにソースコードを変更します。
改変

文字コード変換
まずは、MBS を UTF-8 に変換します。(XML が UTF-8 で出力しているので)
幸い、ワイド文字列から UTF-8 への変換関数はあるのでそちらを利用します。

gtest.cc
--- src.orig/gtest.cc 2011-04-15 12:49:12.000000000 +0900
+++ src/gtest.cc 2012-09-18 21:50:15.185407700 +0900
@@ -1525,6 +1525,27 @@
   return StringStreamToString(&stream);
 }
 
+String MultiByteStringToUtf8(const char* str, int num_chars) {
+  if (num_chars == -1)
+    num_chars = static_cast<int>(strlen(str));
+
+  ::std::stringstream stream;
+  for (int i = 0; i < num_chars; ++i) {
+    wchar_t wc=0;
+    int len = mbtowc(&wc, str+i, MB_CUR_MAX);
+    if( len > 1 )
+    {
+      stream << WideStringToUtf8(&wc, 1);
+      i += len-1;
+    }
+    else
+    {
+      stream << str[i];
+    }
+  }
+  return StringStreamToString(&stream);
+}
+ 
 // Converts a wide C string to a String using the UTF-8 encoding.
 // NULL will be converted to "(null)".
 String String::ShowWideCString(const wchar_t * wide_c_str) {
gtest-internal-inl.h
--- src.orig/gtest-internal-inl.h 2011-04-15 12:49:12.000000000 +0900
+++ src/gtest-internal-inl.h 2012-09-18 21:48:43.489163000 +0900
@@ -221,6 +221,7 @@
 // (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output
 // as '(Invalid Unicode 0xXXXXXXXX)'.
 GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str);
+GTEST_API_ String MultiByteStringToUtf8(const char* str, int num_chars);
 
 // Converts a wide string to a narrow string in UTF-8 encoding.
 // The wide string is assumed to have the following encoding:
ヘッダに宣言を追加しておきます。


XML の処理を修正
XML に出力するか判断する前に UTF-8 に変換しておきます。
@@ -3124,6 +3145,9 @@
 String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) {
   Message m;
 
+  String tmp = MultiByteStringToUtf8(str, -1);
+  str = tmp.c_str();
+
   if (str != NULL) {
     for (const char* src = str; *src; ++src) {
       switch (*src) {
@@ -3255,7 +3279,8 @@
               << "\" type=\"\">";
       const string location = internal::FormatCompilerIndependentFileLocation(
           part.file_name(), part.line_number());
-      const string message = location + "\n" + part.message();
+      const string message_raw = location + "\n" + part.message();
+      const string message = MultiByteStringToUtf8(message_raw.c_str(), -1);
       OutputXmlCDataSection(stream,
                             RemoveInvalidXmlCharacters(message).c_str());
       *stream << "</failure>\n";

先行バイトを許可する
最後に先行バイトを許可します。
(厳密にやるなら本当に先行バイトかどうか、後続バイトも調べるべきかも)
@@ -3022,7 +3043,7 @@
 
   // May c appear in a well-formed XML document?
   static bool IsValidXmlCharacter(char c) {
-    return IsNormalizableWhitespace(c) || c >= 0x20;
+    return IsNormalizableWhitespace(c) || c >= 0x20 || c < 0x00;
   }
 
   // Returns an XML-escaped copy of the input string str.  If

テスト側の修正
テストを実行する前に、setlocale( LC_ALL, "" ); をしてください。
Visual Studio だと、起動時に setlocale( LC_ALL, "C" ); を実行するので、
mbtowc が期待どおりに動作しません。

以上で、日本語も XML に出力できるようになるはずです。

ソース/パッチ

github で公開してます。






0 件のコメント:

コメントを投稿