From 4ace69d814e2e5400b53f6e174b9283c7bf92c15 Mon Sep 17 00:00:00 2001 From: nishi_74322014 Date: Mon, 17 Aug 2026 15:57:57 +0900 Subject: [PATCH 1/7] fixed #561 --- .../Framework/Transmission/CallController.cs | 8 +- .../Tests/TestTransmission/App.config | 19 +++ .../Tests/TestTransmission/Program.cs | 154 ++++++++++++++++++ .../TestTransmission/TMProtocolDefinition.xml | 2 + .../net48/TestTransmissionFx.csproj | 1 + .../WSClientWin_sample.vbproj | 16 +- .../WSClientWin_sample_all.sln | 20 --- 7 files changed, 189 insertions(+), 31 deletions(-) diff --git a/root/programs/CS/Frameworks/Infrastructure/Framework/Transmission/CallController.cs b/root/programs/CS/Frameworks/Infrastructure/Framework/Transmission/CallController.cs index 1f60063c6..46f599a0d 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Framework/Transmission/CallController.cs +++ b/root/programs/CS/Frameworks/Infrastructure/Framework/Transmission/CallController.cs @@ -71,6 +71,9 @@ //* ・Concurrent Collectionでプールを作成する。 //* 2026/08/13 玄人 幸道 .NET Core版で未対応のprotocolを、nullではなく //* FrameworkExceptionで返すようにした(#543)。 +//* 2021/08/14 西野 大介 デッドコードの整理、メソッド切り出しなど(#546) +//* 2026/08/17 玄人 幸道 WCF_TCPIPの呼び出しを戻した(#561)。#546で実装を +//* メソッドに切り出した際、呼び出しが欠落していた。 //********************************************************************************** using System; @@ -395,7 +398,10 @@ public object Invoke(string serviceName, object parameterValue) if (protocol == ((int)FxEnum.TmProtocol.WCF_TCPIP).ToString()) { #region WCF : netTCPBinding - + + // WCF TCP/IP (netTcpBinding) + ret = this.WCF_TCPIP(serviceName, url, timeout, props, + contextObject, parameterValueObject, out returnValueObject); #endregion } diff --git a/root/programs/CS/Frameworks/Tests/TestTransmission/App.config b/root/programs/CS/Frameworks/Tests/TestTransmission/App.config index f8824a524..3e69bebce 100644 --- a/root/programs/CS/Frameworks/Tests/TestTransmission/App.config +++ b/root/programs/CS/Frameworks/Tests/TestTransmission/App.config @@ -4,4 +4,23 @@ + + + + + + + + + + + + + + diff --git a/root/programs/CS/Frameworks/Tests/TestTransmission/Program.cs b/root/programs/CS/Frameworks/Tests/TestTransmission/Program.cs index abdf2f80d..4d0106694 100644 --- a/root/programs/CS/Frameworks/Tests/TestTransmission/Program.cs +++ b/root/programs/CS/Frameworks/Tests/TestTransmission/Program.cs @@ -24,6 +24,8 @@ //* 日時 更新者 内容 //* ---------- ---------------- ------------------------------------------------- //* 2026/08/14 玄人 幸道 新規作成(#546) +//* 2026/08/17 玄人 幸道 WCF TCP/IPのケースを追加(#561)。全ケースが +//* ASP.NET WebAPIで、WCFを一度も通っていなかった。 //********************************************************************************** using System; @@ -36,6 +38,7 @@ using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using System.ServiceModel; using System.Text; using System.Threading; @@ -111,6 +114,66 @@ public class TestReturn : BaseReturnValue public string Text = ""; } + /// + /// WCF TCP/IP のサービス スタブ(#561) + /// + /// + /// **HTTP 側の HandleOrigin / BuildResponse に相当する。** + /// 応答の作り方も揃えてある(エラー情報は空、戻り値は TestReturn)。 + /// + /// 受け取った内容を静的フィールドに記録する。 + /// **サーバが何を受け取ったかを見ないと、届いたかどうかを判定できない**ためで、 + /// これも HTTP 側(Recorded)と同じ考え方である。 + /// + public class WcfTcpStub : IWCFTCPSvcForFx + { + /// 受け取ったサービス名 + public static string ServiceName = null; + + /// 受け取った引数の本文 + public static string ParamText = null; + + /// 受け取った回数 + public static int Count = 0; + + /// 記録を消す + public static void Clear() + { + WcfTcpStub.ServiceName = null; + WcfTcpStub.ParamText = null; + WcfTcpStub.Count = 0; + } + + /// サービス インターフェイス基盤(.NETオンライン) + /// サービス名 + /// コンテキスト + /// 引数 + /// 戻り値 + /// エラー情報のバイト配列 + public byte[] DotNETOnlineTCP( + string serviceName, ref byte[] contextObject, + byte[] parameterValueObject, out byte[] returnValueObject) + { + TestParam param = (TestParam)BinarySerialize.BytesToObject(parameterValueObject); + + lock (typeof(WcfTcpStub)) + { + WcfTcpStub.ServiceName = serviceName; + WcfTcpStub.ParamText = param.Text; + WcfTcpStub.Count++; + } + + TestReturn ret = new TestReturn(); + ret.Text = "サーバからの戻り値"; + returnValueObject = BinarySerialize.ObjectToBytes(ret); + + // 受け取ったコンテキストは、そのまま返す(contextObject には触らない) + + // エラー情報:無し(空文字。Invoke 側がこれを「正常」と判定する) + return BinarySerialize.ObjectToBytes(""); + } + } + #endregion /// @@ -150,6 +213,13 @@ class Program /// オリジン(TLS)のポート private const int TlsPort = 51092; + /// WCF TCP/IP のアドレス(定義 XML の url と合わせる)(#561) + /// + /// **net.tcp の自己ホストは URL ACL を要求しない**ので、管理者権限は要らない。 + /// (HttpListener を避けた理由は上の「HttpListener を使わない理由」を参照) + /// + private const string WcfTcpUrl = "net.tcp://127.0.0.1:51093/TestTransmission/WCFTCPSvcForFx/"; + /// クライアント証明書のファイル名(定義 XML の CertFile と合わせる) private const string ClientCertFile = "TestClient.pfx"; @@ -206,6 +276,7 @@ static void Main() TcpListener origin = Program.StartOrigin(); TcpListener proxy = Program.StartProxy(); TcpListener tls = Program.StartListener(Program.TlsPort, Program.HandleOrigin, serverCert); + ServiceHost wcf = Program.StartWcfTcp(); try { @@ -221,12 +292,21 @@ static void Main() // クライアント証明書(TLS。プロキシは経由しない) Program.Case("クライアント証明書", "testCert", false, false, null, false, null, false, null, Program.ClientCertSubject); + + // WCF TCP/IP(#561) + Program.CaseWcfTcp("WCF TCP/IP", "testWcfTcp"); } finally { origin.Stop(); proxy.Stop(); tls.Stop(); + + if (wcf != null) + { + try { wcf.Close(); } + catch { wcf.Abort(); } + } } Console.WriteLine(); @@ -339,6 +419,57 @@ private static void Case( #endregion } + /// WCF TCP/IP の 1 ケースを実行して判定する(#561) + /// 表題 + /// サービス名(TMProtocolDefinition.xml の Transmission) + /// + /// **HTTP 側の Case とは見る対象が違う**ので、別の関数にしてある。 + /// 接続オプション(UserAgent・gzip・プロキシ)は HTTP のもので、WCF には無い。 + /// + /// ここで見たいのは「**呼び出しがサービスまで届き、戻り値が返るか**」である。 + /// これが通らなくなったのが #561 で、呼び出しが空だったため + /// returnValueObject が null のまま例外になっていた。 + /// + private static void CaseWcfTcp(string title, string serviceName) + { + Console.WriteLine(); + Console.WriteLine("=== {0}({1})===", title, serviceName); + + WcfTcpStub.Clear(); + + string returned = null; + + try + { + TestParam param = new TestParam(); + param.Text = "こんにちは"; + + CallController cc = new CallController(new TestContext()); + TestReturn ret = (TestReturn)cc.Invoke(serviceName, param); + + returned = (ret == null) ? null : ret.Text; + } + catch (Exception ex) + { + Exception e = ex; + while (e.InnerException != null) { e = e.InnerException; } + Console.WriteLine(" [NG] 例外 : {0} : {1}", e.GetType().Name, e.Message); + Program.NG++; + return; + } + + Program.Check("戻り値", "サーバからの戻り値", returned); + + lock (typeof(WcfTcpStub)) + { + // **サービスに届いたか。** 届かずに戻り値だけ合う、ということは起きないが、 + // 「呼び出しが空」の状態を確実に捕まえるために、受信側でも見る。 + Program.Check("サービスへ到達", 1, WcfTcpStub.Count); + Program.Check("サービス名", serviceName, WcfTcpStub.ServiceName); + Program.Check("引数", "こんにちは", WcfTcpStub.ParamText); + } + } + /// 期待値と突き合わせて出力する /// 項目名 /// 期待値 @@ -422,6 +553,29 @@ private static X509Certificate2 CreateSelfSigned(string subject) #endregion + #region WCF TCP/IP のホスト(#561) + + /// WCF TCP/IP のサービスを自己ホストで起動する + /// ServiceHost + /// + /// **セキュリティは None にする。** 既定の netTcpBinding は Transport + /// (Windows 資格情報)で、クライアント側(App.config)と揃える必要がある。 + /// ここで見たいのは通信そのものなので、両側とも None に揃えてある。 + /// + private static ServiceHost StartWcfTcp() + { + ServiceHost host = new ServiceHost(typeof(WcfTcpStub), new Uri(Program.WcfTcpUrl)); + + host.AddServiceEndpoint( + typeof(IWCFTCPSvcForFx), new NetTcpBinding(SecurityMode.None), ""); + + host.Open(); + + return host; + } + + #endregion + #region オリジン /// オリジンを起動する diff --git a/root/programs/CS/Frameworks/Tests/TestTransmission/TMProtocolDefinition.xml b/root/programs/CS/Frameworks/Tests/TestTransmission/TMProtocolDefinition.xml index e437a23c1..013924b0e 100644 --- a/root/programs/CS/Frameworks/Tests/TestTransmission/TMProtocolDefinition.xml +++ b/root/programs/CS/Frameworks/Tests/TestTransmission/TMProtocolDefinition.xml @@ -30,4 +30,6 @@ + + diff --git a/root/programs/CS/Frameworks/Tests/TestTransmission/net48/TestTransmissionFx.csproj b/root/programs/CS/Frameworks/Tests/TestTransmission/net48/TestTransmissionFx.csproj index bc07907e5..dfbe66214 100644 --- a/root/programs/CS/Frameworks/Tests/TestTransmission/net48/TestTransmissionFx.csproj +++ b/root/programs/CS/Frameworks/Tests/TestTransmission/net48/TestTransmissionFx.csproj @@ -47,6 +47,7 @@ + diff --git a/root/programs/VB/Samples/WS_sample/WSClient_sample/WSClientWin_sample/WSClientWin_sample.vbproj b/root/programs/VB/Samples/WS_sample/WSClient_sample/WSClientWin_sample/WSClientWin_sample.vbproj index 2f7e1f004..e8032f00f 100644 --- a/root/programs/VB/Samples/WS_sample/WSClient_sample/WSClientWin_sample/WSClientWin_sample.vbproj +++ b/root/programs/VB/Samples/WS_sample/WSClient_sample/WSClientWin_sample/WSClientWin_sample.vbproj @@ -81,6 +81,12 @@ + + ..\..\Build\WSIFType_sample.dll + + + ..\..\Build\WSServer_sample.dll + @@ -172,16 +178,6 @@ 13.0.3 - - - {f6e2bd99-672a-4f69-82e3-eee5d9b639df} - WSIFType_sample - - - {15f78db3-7ab0-4adf-adca-48afbd54c31e} - WSServer_sample - - >,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatch\bin\Debug\SimpleBatch.exe,-,SelectCount,SQL%individual%static%- +[2026/08/17 17:21:43,345],[INFO ],[1],,,,----->>,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatch\bin\Debug\SimpleBatch.exe,-,SelectCount,SQL%individual%static%- log4net: configuring repository [log4net-default-repository] using stream log4net: loading XML configuration log4net: Configuring Repository [log4net-default-repository] @@ -144,6 +144,6 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level log4net: Created Appender [OPERATION2] log4net: Adding appender named [OPERATION2] to logger [OPERATION]. log4net: Hierarchy Threshold [] -[2026/08/08 10:16:21,392],[INFO ],[1],87,63,[commandText]:SELECT COUNT(*) FROM Shippers [commandParameter]: -[2026/08/08 10:16:21,393],[INFO ],[1],,,,<<-----,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatch\bin\Debug\SimpleBatch.exe,-,SelectCount,SQL%individual%static%-,110,63 +[2026/08/17 17:21:43,471],[INFO ],[1],92,78,[commandText]:SELECT COUNT(*) FROM Shippers [commandParameter]: +[2026/08/17 17:21:43,472],[INFO ],[1],,,,<<-----,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatch\bin\Debug\SimpleBatch.exe,-,SelectCount,SQL%individual%static%-,114,94 3件のデータがあります diff --git a/root/programs/CS/Frameworks/Tests/TestBatch/ResultSimpleBatchCore100.txt b/root/programs/CS/Frameworks/Tests/TestBatch/ResultSimpleBatchCore100.txt index ca648cccc..046ea96cc 100644 --- a/root/programs/CS/Frameworks/Tests/TestBatch/ResultSimpleBatchCore100.txt +++ b/root/programs/CS/Frameworks/Tests/TestBatch/ResultSimpleBatchCore100.txt @@ -69,7 +69,7 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level log4net: Created Appender [OPERATION2] log4net: Adding appender named [OPERATION2] to logger [OPERATION]. log4net: Hierarchy Threshold [] -[2026/08/08 10:16:24,296],[INFO ],[2],,,,----->>,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatchCore\bin\Debug\net10.0\SimpleBatchCore.dll,-,SelectCount,SQL%individual%static%- +[2026/08/17 17:21:45,741],[INFO ],[2],,,,----->>,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatchCore\bin\Debug\net10.0\SimpleBatchCore.dll,-,SelectCount,SQL%individual%static%- log4net: configuring repository [log4net-default-repository] using stream log4net: loading XML configuration log4net: Configuring Repository [log4net-default-repository] @@ -144,6 +144,6 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level log4net: Created Appender [OPERATION2] log4net: Adding appender named [OPERATION2] to logger [OPERATION]. log4net: Hierarchy Threshold [] -[2026/08/08 10:16:24,373],[INFO ],[2],50,,[commandText]:SELECT COUNT(*) FROM Shippers [commandParameter]: -[2026/08/08 10:16:24,374],[INFO ],[2],,,,<<-----,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatchCore\bin\Debug\net10.0\SimpleBatchCore.dll,-,SelectCount,SQL%individual%static%-,67, +[2026/08/17 17:21:45,817],[INFO ],[2],50,,[commandText]:SELECT COUNT(*) FROM Shippers [commandParameter]: +[2026/08/17 17:21:45,818],[INFO ],[2],,,,<<-----,C:\OpenTouryo\root\programs\CS\Frameworks\Tests\TestBatch\SimpleBatchCore\bin\Debug\net10.0\SimpleBatchCore.dll,-,SelectCount,SQL%individual%static%-,66, 3件のデータがあります diff --git a/root/programs/CS/Frameworks/Tests/TestCode/AssemblyInfo.cs b/root/programs/CS/Frameworks/Tests/TestCode/AssemblyInfo.cs index f8b37351f..5453db50e 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/AssemblyInfo.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/AssemblyInfo.cs @@ -1,4 +1,32 @@ -using System.Reflection; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :AssemblyInfo +//* クラス日本語名 :アセンブリ情報 +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2018/11/12 西野 大介 新規作成 +//********************************************************************************** + +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/root/programs/CS/Frameworks/Tests/TestCode/Program.cs b/root/programs/CS/Frameworks/Tests/TestCode/Program.cs index 4cabfef5e..c9e9c3298 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/Program.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/Program.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :Program +//* クラス日本語名 :単体テストのエントリ ポイント +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2019/02/06 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Text; using System.Configuration; diff --git a/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt b/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt index daae30db0..544a05dea 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt +++ b/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt @@ -70,11 +70,11 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level log4net: Created Appender [OPERATION2] log4net: Adding appender named [OPERATION2] to logger [OPERATION]. log4net: Hierarchy Threshold [] -[2026/08/16 21:23:40,219],[DEBUG],[1],LogIF.DebugLog("ACCESS"); -[2026/08/16 21:23:40,233],[INFO ],[1],LogIF.InfoLog("ACCESS"); -[2026/08/16 21:23:40,234],[WARN ],[1],LogIF.WarnLog("ACCESS"); -[2026/08/16 21:23:40,234],[ERROR],[1],LogIF.ErrorLog("ACCESS"); -[2026/08/16 21:23:40,235],[FATAL],[1],LogIF.FatalLog("ACCESS"); +[2026/08/17 17:21:01,782],[DEBUG],[1],LogIF.DebugLog("ACCESS"); +[2026/08/17 17:21:01,795],[INFO ],[1],LogIF.InfoLog("ACCESS"); +[2026/08/17 17:21:01,796],[WARN ],[1],LogIF.WarnLog("ACCESS"); +[2026/08/17 17:21:01,796],[ERROR],[1],LogIF.ErrorLog("ACCESS"); +[2026/08/17 17:21:01,796],[FATAL],[1],LogIF.FatalLog("ACCESS"); ---------------------------------------------------------------------------------------------------- GetMessage: ~メッセージID:I0001に対応する記述(正常系)~ GetMessage: ~メッセージID:E0001に対応する記述(異常系)~ @@ -115,6 +115,41 @@ StringChecker.IsShift_Jis_Zenkaku - aaaaaa: False StringChecker.IsShift_Jis_Zenkaku - 亜亜亜: True StringChecker.IsShift_Jis_Hankaku - aaa: False StringChecker.IsShift_Jis_Hankaku - aaaaaa: True +-------------------------------------------------- +StringChecker.IsHankaku/IsZenkaku - [abc123]: True / False +StringChecker.IsHankaku/IsZenkaku - [アイウ]: True / False +StringChecker.IsHankaku/IsZenkaku - [あいう]: False / True +StringChecker.IsHankaku/IsZenkaku - [abc]: False / True +StringChecker.IsHankaku/IsZenkaku - [abcあ]: False / False +StringChecker.IsHankaku/IsZenkaku - [ ]: True / False +StringChecker.IsHankaku/IsZenkaku - [~]: False / True +StringChecker.IsHankaku/IsZenkaku - []: True / True +StringChecker.IsNumeric - [123]: True(IsNumbers: True) +StringChecker.IsNumeric - [-123]: True(IsNumbers: False) +StringChecker.IsNumeric - [1.5]: True(IsNumbers: False) +StringChecker.IsNumeric - [+1]: True(IsNumbers: False) +StringChecker.IsNumeric - [1e3]: True(IsNumbers: False) +StringChecker.IsNumeric - [123]: True(IsNumbers: True) +StringChecker.IsNumeric - [1.5]: True(IsNumbers: False) +StringChecker.IsNumeric - [12 3]: False(IsNumbers: False) +StringChecker.IsNumeric - [abc]: False(IsNumbers: False) +StringChecker.IsNumeric - []: False(IsNumbers: True) +StringChecker.IsNumeric - [ ]: False(IsNumbers: False) +StringChecker.IsInCodePage - [abc]: shift_jis True / us_ascii True / utf_8 True +StringChecker.IsInCodePage - [あいう]: shift_jis True / us_ascii False / utf_8 True +StringChecker.IsInCodePage - [①]: shift_jis True / us_ascii False / utf_8 True +StringChecker.IsInCodePage - [𩸽]: shift_jis False / us_ascii False / utf_8 True +StringChecker.IsInCodePage - []: shift_jis True / us_ascii True / utf_8 True +StringChecker.Match - [abc123] [0-9]+ : True +StringChecker.Match - [abc123] ^[0-9]+$ : False +StringChecker.Match - [abc] [0-9]+ : False +StringChecker.Match - [ABC] ^abc$ : False +StringChecker.Match - [ABC] ^abc$ (IgnoreCase) : True +StringChecker.Matches - [a1b22c333] [0-9]+ : 3 件 + 位置 1 : 1 + 位置 3 : 22 + 位置 6 : 333 +StringChecker.Matches - [abc] [0-9]+ : 0 件 ---------------------------------------------------------------------------------------------------- TestFormatChecker.IsJpZipCode JpZipCode_Hyphen @@ -2273,6 +2308,21 @@ StringConverter.ToHankaku - アアア: アアア StringConverter.ToZenkaku - アアア: アアア StringConverter.ToHiragana - アアア: あああ StringConverter.ToKatakana - あああ: アアア +-------------------------------------------------- +StringConverter.EditYYYYMMDDString - [20200102]: False → [20200102] +StringConverter.EditYYYYMMDDString - [2020115]: True → [20201105] +StringConverter.EditYYYYMMDDString - [2020155]: True → [20200155] +StringConverter.EditYYYYMMDDString - [202012]: True → [20200102] +StringConverter.EditYYYYMMDDString - [2020]: False → [2020] +StringConverter.EditYYYYMMDDString - [20201a02]: False → [20201a02] +StringConverter.EditYYYYMMDDString - []: False → [] +StringConverter.FormattingForOneLineLog - [SELECT * FROM Orders]: [SELECT * FROM Orders] +StringConverter.FormattingForOneLineLog - [SELECT * FROM T WHERE C = 'a b']: [SELECT * FROM T WHERE C = 'a b'] +StringConverter.FormattingForOneLineLog - [a\r\nb\rc\nd]: [a b c d] +StringConverter.FormattingForOneLineLog - [A & B]: [A & B] +StringConverter.FormattingForOneLineLog - [WHERE C = 'It''s']: [WHERE C = 'It''s'] +StringConverter.FormattingForOneLineLog - []: [] +StringConverter.FormattingForOneLineLog - [ ]: [ ] ---------------------------------------------------------------------------------------------------- FormatConverter.SeirekiToWareki 1977/4/24, ggy年M月d日(ddd): 昭和52年4月24日(日) @@ -2343,6 +2393,51 @@ FormatConverter.Suppress "123456789", 10, '@': @@@abcdefg "123456789", 11, '@': @@@@abcdefg "123456789", 20, '@': @@@@@@@@@@@@@abcdefg +-------------------------------------------------- +FormatConverter.Round_4sya5nyu / Round_Banker + 0.5 → 四捨五入 1 / 銀行家 0 + 1.5 → 四捨五入 2 / 銀行家 2 + 2.5 → 四捨五入 3 / 銀行家 2 + 3.5 → 四捨五入 4 / 銀行家 4 + -0.5 → 四捨五入 -1 / 銀行家 0 + -2.5 → 四捨五入 -3 / 銀行家 -2 + 2.4 → 四捨五入 2 / 銀行家 2 + 2.6 → 四捨五入 3 / 銀行家 3 + 1.005(2桁) → 四捨五入 1.01 / 銀行家 1.00 + "abc" → 四捨五入 0 / 銀行家 0 + "2.5"(文字列) → 四捨五入 3 +-------------------------------------------------- +FormatConverter.Floor / Ceiling + Floor 1.567(2桁) : 既定 1.56 / RZ(0方向) 1.56 / RM(負の無限大) 1.56 + Ceiling 1.567(2桁) : 既定 1.57 / RI(絶対値) 1.57 / RP(正の無限大) 1.57 + Floor -1.567(2桁) : 既定 -1.57 / RZ(0方向) -1.56 / RM(負の無限大) -1.57 + Ceiling -1.567(2桁) : 既定 -1.56 / RI(絶対値) -1.57 / RP(正の無限大) -1.56 + Floor 1.9(0桁) : 1 / Ceiling 1.1(0桁) : 2 + "abc" : Floor 0 / Ceiling 0 +-------------------------------------------------- +FormatConverter.AddFigureX + 1234567.891 を 2桁区切り : 1,23,45,67.891 + 1234567.891 を 3桁区切り : 1,234,567.891 + 1234567.891 を 4桁区切り : 123,4567.891 + -1234567.891 を 3桁区切り : -1,234,567.891 + "abc" を 3桁区切り : 0 +FormatConverter.AddZerosAfterDecimal + 123(3桁) : 123.000 + 123.4(3桁) : 123.400 + 123.456(2桁) : 123.456 + 123(0桁) : 123 + -1.2(3桁) : -1.200 +-------------------------------------------------- +FormatConverter.ToUnixTime / FromUnixTime + 1970/01/01 00:00:00 → 0 → 1970/01/01 00:00:00(Kind=Utc) + 2000/01/01 00:00:00 → 946684800 → 2000/01/01 00:00:00(Kind=Utc) + 2038/01/19 03:14:07 → 2147483647 → 2038/01/19 03:14:07(Kind=Utc) + 1960/01/01 00:00:00 → -315619200 → 1960/01/01 00:00:00(Kind=Utc) + 秒未満 : 0 +FormatConverter.ToW3cTimestamp / FromW3cTimestamp + 既定の書式 : 2020-05-06T07:08:09Z + 書式を指定 : 2020-05-06 07:08:09Z + 戻り : 2020/05/06 07:08:09(Kind=Unspecified) ---------------------------------------------------------------------------------------------------- CustomEncode.HtmlEncode: " id="txtXXXXX" /><script type="text/javascript">alert("XSS!!!")</script><input name="txtXXXXX" type="text" value=" ---------------------------------------------------------------------------------------------------- @@ -2350,6 +2445,42 @@ CustomEncode.UrlEncode: http://www.google.co.jp/search?hl=ja&q=%26 CustomEncode.UrlEncode2: http://www.google.co.jp/search?hl=ja&q=& CustomEncode.UrlEncode2: http://www.google.co.jp/search?hl=ja&q=%3C%3E ---------------------------------------------------------------------------------------------------- +CustomEncode.HtmlDecode: & +CustomEncode.HtmlDecode:

"'&  +CustomEncode.UrlEncode: %E3%81%82%26%3D%3F%2F%20%2B +CustomEncode.UrlDecode: あ&=?/ + +CustomEncode.UrlDecode: [a b c] +CustomEncode.StringToByte(65001): E3 81 82 E3 81 84 E3 81 86 41 42 31 +CustomEncode.ByteToString(65001): あいうAB1 +CustomEncode.StringToByte(932): 82 A0 82 A2 82 A4 41 42 31 +CustomEncode.ByteToString(932): あいうAB1 +CustomEncode.StringToByte(51932): A4 A2 A4 A4 A4 A6 41 42 31 +CustomEncode.ByteToString(51932): あいうAB1 +CustomEncode.StringToByte(empty): [] +CustomEncode.ByteToString(sjis as utf8): EF BF BD EF BF BD +CustomEncode.ToHexString: [00 0F 10 FF] +CustomEncode.FormHexString: [00 0F 10 FF] +CustomEncode.ToHexString(empty): [] +CustomEncode.FormHexString(lower): [0A BC] +CustomEncode.FormHexString(invalid): FormatException +CustomEncode.ToBase64String: 44GT44KT44Gr44Gh44Gv +CustomEncode.FromBase64String: こんにちは +CustomEncode.ToBase64String(empty): [] +CustomEncode.FromBase64String(invalid): FormatException +CustomEncode.ToBase64String : /++/ +CustomEncode.ToBase64UrlString: _--_ +CustomEncode.ToBase64UrlString(1byte): [_w] 長さ%4=2 +CustomEncode.FromBase64UrlString(1byte): [FF] +CustomEncode.ToBase64UrlString(2byte): [__4] 長さ%4=3 +CustomEncode.FromBase64UrlString(2byte): [FF FE] +CustomEncode.ToBase64UrlString(3byte): [__79] 長さ%4=0 +CustomEncode.FromBase64UrlString(3byte): [FF FE FD] +CustomEncode.FromBase64UrlString(invalid): Exception : Illegal base64url string! +CustomEncode.GetEncodings: 行数 141 +CustomEncode.GetEncodings: 列 key, value +CustomEncode.GetEncodings[0]: 0 = shift_jis +CustomEncode.GetEncodings[last]: 65001 = utf-8 +---------------------------------------------------------------------------------------------------- jis2k4.GetStringInfo - あああ: Char長:3; 文字列長3; @@ -2394,6 +2525,23 @@ Three > Three Four > Four ---------------------------------------------------------------------------------------------------- XmlLib > is working properly. +-------------------------------------------------- +XmlLib.GetAttributeByTagName - item/name: [一つ目] +XmlLib.GetAttributeByTagName - item/name(index=1): [一つ目] +XmlLib.GetAttributeByTagName - empty/name: [] +XmlLib.GetAttributeByTagName - none/name: [] +XmlLib.GetAttributeByTagName - item/none: [] +XmlLib.GetAttributeByXPath - //item[@id='i2']/name: [二つ目] +XmlLib.GetAttributeByXPath - //none: [] +XmlLib.GetAttributeFromXmlNode - name: [一つ目] +XmlLib.GetAttributeFromXmlNode - null: [] +XmlLib.GetXmlNodeById - item: item id=i1 +XmlLib.GetXmlNodeById - r1: root id=r1 +XmlLib.GetXmlNodeById - i1: (null) +XmlLib.GetEncodingFromXmlDeclaration - : utf-8 +XmlLib.GetEncodingFromXmlDeclaration - : shift_jis +XmlLib.GetEncodingFromXmlDeclaration - : ArgumentException +XmlLib.GetEncodingFromXmlDeclaration - : ArgumentException ---------------------------------------------------------------------------------------------------- DeflateCompression(FileStream)-1 > is working properly. DeflateCompression(FileStream)-2 > is working properly. diff --git a/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt b/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt index d61d2650d..68711fb71 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt +++ b/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt @@ -70,11 +70,11 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level log4net: Created Appender [OPERATION2] log4net: Adding appender named [OPERATION2] to logger [OPERATION]. log4net: Hierarchy Threshold [] -[2026/08/16 21:23:48,945],[DEBUG],[2],LogIF.DebugLog("ACCESS"); -[2026/08/16 21:23:48,975],[INFO ],[2],LogIF.InfoLog("ACCESS"); -[2026/08/16 21:23:48,976],[WARN ],[2],LogIF.WarnLog("ACCESS"); -[2026/08/16 21:23:48,977],[ERROR],[2],LogIF.ErrorLog("ACCESS"); -[2026/08/16 21:23:48,978],[FATAL],[2],LogIF.FatalLog("ACCESS"); +[2026/08/17 17:21:06,781],[DEBUG],[2],LogIF.DebugLog("ACCESS"); +[2026/08/17 17:21:06,789],[INFO ],[2],LogIF.InfoLog("ACCESS"); +[2026/08/17 17:21:06,789],[WARN ],[2],LogIF.WarnLog("ACCESS"); +[2026/08/17 17:21:06,790],[ERROR],[2],LogIF.ErrorLog("ACCESS"); +[2026/08/17 17:21:06,790],[FATAL],[2],LogIF.FatalLog("ACCESS"); ---------------------------------------------------------------------------------------------------- GetMessage: ~メッセージID:I0001に対応する記述(正常系)~ GetMessage: ~メッセージID:E0001に対応する記述(異常系)~ @@ -115,6 +115,41 @@ StringChecker.IsShift_Jis_Zenkaku - aaaaaa: False StringChecker.IsShift_Jis_Zenkaku - 亜亜亜: True StringChecker.IsShift_Jis_Hankaku - aaa: False StringChecker.IsShift_Jis_Hankaku - aaaaaa: True +-------------------------------------------------- +StringChecker.IsHankaku/IsZenkaku - [abc123]: True / False +StringChecker.IsHankaku/IsZenkaku - [アイウ]: True / False +StringChecker.IsHankaku/IsZenkaku - [あいう]: False / True +StringChecker.IsHankaku/IsZenkaku - [abc]: False / True +StringChecker.IsHankaku/IsZenkaku - [abcあ]: False / False +StringChecker.IsHankaku/IsZenkaku - [ ]: True / False +StringChecker.IsHankaku/IsZenkaku - [~]: False / True +StringChecker.IsHankaku/IsZenkaku - []: True / True +StringChecker.IsNumeric - [123]: True(IsNumbers: True) +StringChecker.IsNumeric - [-123]: True(IsNumbers: False) +StringChecker.IsNumeric - [1.5]: True(IsNumbers: False) +StringChecker.IsNumeric - [+1]: True(IsNumbers: False) +StringChecker.IsNumeric - [1e3]: True(IsNumbers: False) +StringChecker.IsNumeric - [123]: True(IsNumbers: True) +StringChecker.IsNumeric - [1.5]: True(IsNumbers: False) +StringChecker.IsNumeric - [12 3]: False(IsNumbers: False) +StringChecker.IsNumeric - [abc]: False(IsNumbers: False) +StringChecker.IsNumeric - []: False(IsNumbers: True) +StringChecker.IsNumeric - [ ]: False(IsNumbers: False) +StringChecker.IsInCodePage - [abc]: shift_jis True / us_ascii True / utf_8 True +StringChecker.IsInCodePage - [あいう]: shift_jis True / us_ascii False / utf_8 True +StringChecker.IsInCodePage - [①]: shift_jis True / us_ascii False / utf_8 True +StringChecker.IsInCodePage - [𩸽]: shift_jis False / us_ascii False / utf_8 True +StringChecker.IsInCodePage - []: shift_jis True / us_ascii True / utf_8 True +StringChecker.Match - [abc123] [0-9]+ : True +StringChecker.Match - [abc123] ^[0-9]+$ : False +StringChecker.Match - [abc] [0-9]+ : False +StringChecker.Match - [ABC] ^abc$ : False +StringChecker.Match - [ABC] ^abc$ (IgnoreCase) : True +StringChecker.Matches - [a1b22c333] [0-9]+ : 3 件 + 位置 1 : 1 + 位置 3 : 22 + 位置 6 : 333 +StringChecker.Matches - [abc] [0-9]+ : 0 件 ---------------------------------------------------------------------------------------------------- TestFormatChecker.IsJpZipCode JpZipCode_Hyphen @@ -2273,6 +2308,21 @@ StringConverter.ToHankaku - アアア: アアア StringConverter.ToZenkaku - アアア: アアア StringConverter.ToHiragana - アアア: あああ StringConverter.ToKatakana - あああ: アアア +-------------------------------------------------- +StringConverter.EditYYYYMMDDString - [20200102]: False → [20200102] +StringConverter.EditYYYYMMDDString - [2020115]: True → [20201105] +StringConverter.EditYYYYMMDDString - [2020155]: True → [20200155] +StringConverter.EditYYYYMMDDString - [202012]: True → [20200102] +StringConverter.EditYYYYMMDDString - [2020]: False → [2020] +StringConverter.EditYYYYMMDDString - [20201a02]: False → [20201a02] +StringConverter.EditYYYYMMDDString - []: False → [] +StringConverter.FormattingForOneLineLog - [SELECT * FROM Orders]: [SELECT * FROM Orders] +StringConverter.FormattingForOneLineLog - [SELECT * FROM T WHERE C = 'a b']: [SELECT * FROM T WHERE C = 'a b'] +StringConverter.FormattingForOneLineLog - [a\r\nb\rc\nd]: [a b c d] +StringConverter.FormattingForOneLineLog - [A & B]: [A & B] +StringConverter.FormattingForOneLineLog - [WHERE C = 'It''s']: [WHERE C = 'It''s'] +StringConverter.FormattingForOneLineLog - []: [] +StringConverter.FormattingForOneLineLog - [ ]: [ ] ---------------------------------------------------------------------------------------------------- FormatConverter.SeirekiToWareki 1977/4/24, ggy年M月d日(ddd): 昭和52年4月24日(日) @@ -2343,6 +2393,51 @@ FormatConverter.Suppress "123456789", 10, '@': @@@abcdefg "123456789", 11, '@': @@@@abcdefg "123456789", 20, '@': @@@@@@@@@@@@@abcdefg +-------------------------------------------------- +FormatConverter.Round_4sya5nyu / Round_Banker + 0.5 → 四捨五入 1 / 銀行家 0 + 1.5 → 四捨五入 2 / 銀行家 2 + 2.5 → 四捨五入 3 / 銀行家 2 + 3.5 → 四捨五入 4 / 銀行家 4 + -0.5 → 四捨五入 -1 / 銀行家 0 + -2.5 → 四捨五入 -3 / 銀行家 -2 + 2.4 → 四捨五入 2 / 銀行家 2 + 2.6 → 四捨五入 3 / 銀行家 3 + 1.005(2桁) → 四捨五入 1.01 / 銀行家 1.00 + "abc" → 四捨五入 0 / 銀行家 0 + "2.5"(文字列) → 四捨五入 3 +-------------------------------------------------- +FormatConverter.Floor / Ceiling + Floor 1.567(2桁) : 既定 1.56 / RZ(0方向) 1.56 / RM(負の無限大) 1.56 + Ceiling 1.567(2桁) : 既定 1.57 / RI(絶対値) 1.57 / RP(正の無限大) 1.57 + Floor -1.567(2桁) : 既定 -1.57 / RZ(0方向) -1.56 / RM(負の無限大) -1.57 + Ceiling -1.567(2桁) : 既定 -1.56 / RI(絶対値) -1.56 / RP(正の無限大) -1.56 + Floor 1.9(0桁) : 1 / Ceiling 1.1(0桁) : 2 + "abc" : Floor 0 / Ceiling 0 +-------------------------------------------------- +FormatConverter.AddFigureX + 1234567.891 を 2桁区切り : 1,23,45,67.891 + 1234567.891 を 3桁区切り : 1,234,567.891 + 1234567.891 を 4桁区切り : 123,4567.891 + -1234567.891 を 3桁区切り : -1,234,567.891 + "abc" を 3桁区切り : 0 +FormatConverter.AddZerosAfterDecimal + 123(3桁) : 123.000 + 123.4(3桁) : 123.400 + 123.456(2桁) : 123.456 + 123(0桁) : 123 + -1.2(3桁) : -1.200 +-------------------------------------------------- +FormatConverter.ToUnixTime / FromUnixTime + 1970/01/01 00:00:00 → 0 → 1970/01/01 00:00:00(Kind=Utc) + 2000/01/01 00:00:00 → 946684800 → 2000/01/01 00:00:00(Kind=Utc) + 2038/01/19 03:14:07 → 2147483647 → 2038/01/19 03:14:07(Kind=Utc) + 1960/01/01 00:00:00 → -315619200 → 1960/01/01 00:00:00(Kind=Utc) + 秒未満 : 0 +FormatConverter.ToW3cTimestamp / FromW3cTimestamp + 既定の書式 : 2020-05-06T07:08:09Z + 書式を指定 : 2020-05-06 07:08:09Z + 戻り : 2020/05/06 07:08:09(Kind=Unspecified) ---------------------------------------------------------------------------------------------------- CustomEncode.HtmlEncode: " id="txtXXXXX" /><script type="text/javascript">alert("XSS!!!")</script><input name="txtXXXXX" type="text" value=" ---------------------------------------------------------------------------------------------------- @@ -2350,6 +2445,42 @@ CustomEncode.UrlEncode: http://www.google.co.jp/search?hl=ja&q=%26 CustomEncode.UrlEncode2: http%3A%2F%2Fwww.google.co.jp%2Fsearch%3Fhl%3Dja%26q%3D%26 CustomEncode.UrlEncode2: http%3A%2F%2Fwww.google.co.jp%2Fsearch%3Fhl%3Dja%26q%3D%3C%3E ---------------------------------------------------------------------------------------------------- +CustomEncode.HtmlDecode: & +CustomEncode.HtmlDecode:

"'&  +CustomEncode.UrlEncode: %E3%81%82%26%3D%3F%2F%20%2B +CustomEncode.UrlDecode: あ&=?/ + +CustomEncode.UrlDecode: [a b c] +CustomEncode.StringToByte(65001): E3 81 82 E3 81 84 E3 81 86 41 42 31 +CustomEncode.ByteToString(65001): あいうAB1 +CustomEncode.StringToByte(932): 82 A0 82 A2 82 A4 41 42 31 +CustomEncode.ByteToString(932): あいうAB1 +CustomEncode.StringToByte(51932): A4 A2 A4 A4 A4 A6 41 42 31 +CustomEncode.ByteToString(51932): あいうAB1 +CustomEncode.StringToByte(empty): [] +CustomEncode.ByteToString(sjis as utf8): EF BF BD EF BF BD +CustomEncode.ToHexString: [00 0F 10 FF] +CustomEncode.FormHexString: [00 0F 10 FF] +CustomEncode.ToHexString(empty): [] +CustomEncode.FormHexString(lower): [0A BC] +CustomEncode.FormHexString(invalid): FormatException +CustomEncode.ToBase64String: 44GT44KT44Gr44Gh44Gv +CustomEncode.FromBase64String: こんにちは +CustomEncode.ToBase64String(empty): [] +CustomEncode.FromBase64String(invalid): FormatException +CustomEncode.ToBase64String : /++/ +CustomEncode.ToBase64UrlString: _--_ +CustomEncode.ToBase64UrlString(1byte): [_w] 長さ%4=2 +CustomEncode.FromBase64UrlString(1byte): [FF] +CustomEncode.ToBase64UrlString(2byte): [__4] 長さ%4=3 +CustomEncode.FromBase64UrlString(2byte): [FF FE] +CustomEncode.ToBase64UrlString(3byte): [__79] 長さ%4=0 +CustomEncode.FromBase64UrlString(3byte): [FF FE FD] +CustomEncode.FromBase64UrlString(invalid): Exception : Illegal base64url string! +CustomEncode.GetEncodings: 行数 141 +CustomEncode.GetEncodings: 列 key, value +CustomEncode.GetEncodings[0]: 0 = shift_jis +CustomEncode.GetEncodings[last]: 65001 = utf-8 +---------------------------------------------------------------------------------------------------- jis2k4.GetStringInfo - あああ: Char長:3; 文字列長3; @@ -2413,6 +2544,23 @@ Three > Three Four > Four ---------------------------------------------------------------------------------------------------- XmlLib > is working properly. +-------------------------------------------------- +XmlLib.GetAttributeByTagName - item/name: [一つ目] +XmlLib.GetAttributeByTagName - item/name(index=1): [一つ目] +XmlLib.GetAttributeByTagName - empty/name: [] +XmlLib.GetAttributeByTagName - none/name: [] +XmlLib.GetAttributeByTagName - item/none: [] +XmlLib.GetAttributeByXPath - //item[@id='i2']/name: [二つ目] +XmlLib.GetAttributeByXPath - //none: [] +XmlLib.GetAttributeFromXmlNode - name: [一つ目] +XmlLib.GetAttributeFromXmlNode - null: [] +XmlLib.GetXmlNodeById - item: item id=i1 +XmlLib.GetXmlNodeById - r1: root id=r1 +XmlLib.GetXmlNodeById - i1: (null) +XmlLib.GetEncodingFromXmlDeclaration - : utf-8 +XmlLib.GetEncodingFromXmlDeclaration - : shift_jis +XmlLib.GetEncodingFromXmlDeclaration - : ArgumentException +XmlLib.GetEncodingFromXmlDeclaration - : ArgumentException ---------------------------------------------------------------------------------------------------- DeflateCompression(FileStream)-1 > is working properly. DeflateCompression(FileStream)-2 > is working properly. diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestCustomEncode.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestCustomEncode.cs index 46d4c7529..aa5785856 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestCustomEncode.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestCustomEncode.cs @@ -1,4 +1,33 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestCustomEncode +//* クラス日本語名 :CustomEncodeのテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2020/07/31 西野 大介 新規作成 +//********************************************************************************** + +using System; +using System.Data; using System.Text; using System.IO; @@ -34,7 +63,188 @@ public static void Root() MyDebug.OutputDebugAndConsole( "CustomEncode.UrlEncode2: " + CustomEncode.UrlEncode2("http://www.google.co.jp/search?hl=ja&q=<>")); + + MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); + + TestCustomEncode.Decode(); + TestCustomEncode.StringAndByte(); + TestCustomEncode.Hex(); + TestCustomEncode.Base64(); + TestCustomEncode.Base64Url(); + TestCustomEncode.Encodings(); } #endregion + + #region private + + ///

デコード(HtmlDecode、UrlDecode) + private static void Decode() + { + // エンコードしたものを戻せるか(往復) + string html = CustomEncode.HtmlEncode("&"); + MyDebug.OutputDebugAndConsole("CustomEncode.HtmlDecode: " + CustomEncode.HtmlDecode(html)); + + // 実体参照を直接 + MyDebug.OutputDebugAndConsole( + "CustomEncode.HtmlDecode: " + CustomEncode.HtmlDecode("<p>"'& ")); + + string url = CustomEncode.UrlEncode("あ&=?/ +"); + MyDebug.OutputDebugAndConsole("CustomEncode.UrlEncode: " + url); + MyDebug.OutputDebugAndConsole("CustomEncode.UrlDecode: " + CustomEncode.UrlDecode(url)); + + // **「+」は空白に戻る。** UrlEncode は空白を「+」にするため、 + // 元から「+」だった文字と区別が付かない。 + MyDebug.OutputDebugAndConsole("CustomEncode.UrlDecode: [" + CustomEncode.UrlDecode("a+b%20c") + "]"); + } + + /// 文字列とバイト配列(StringToByte、ByteToString) + private static void StringAndByte() + { + string str = "あいうAB1"; + + foreach (int cp in new int[] { CustomEncode.UTF_8, CustomEncode.shift_jis, CustomEncode.EUC_JP }) + { + byte[] bytes = CustomEncode.StringToByte(str, cp); + + MyDebug.OutputDebugAndConsole( + "CustomEncode.StringToByte(" + cp.ToString() + "): " + CustomEncode.ToHexString(bytes)); + + MyDebug.OutputDebugAndConsole( + "CustomEncode.ByteToString(" + cp.ToString() + "): " + CustomEncode.ByteToString(bytes, cp)); + } + + // 空文字列 + MyDebug.OutputDebugAndConsole( + "CustomEncode.StringToByte(empty): [" + + CustomEncode.ToHexString(CustomEncode.StringToByte("", CustomEncode.UTF_8)) + "]"); + + // **コードページが違うと戻らない。** shift_jis のバイト列を UTF-8 で読む。 + byte[] sjis = CustomEncode.StringToByte("あ", CustomEncode.shift_jis); + MyDebug.OutputDebugAndConsole( + "CustomEncode.ByteToString(sjis as utf8): " + + CustomEncode.ToHexString(CustomEncode.StringToByte( + CustomEncode.ByteToString(sjis, CustomEncode.UTF_8), CustomEncode.UTF_8))); + } + + /// Hex(ToHexString、FormHexString) + private static void Hex() + { + byte[] bytes = new byte[] { 0x00, 0x0F, 0x10, 0xFF }; + + string hex = CustomEncode.ToHexString(bytes); + MyDebug.OutputDebugAndConsole("CustomEncode.ToHexString: [" + hex + "]"); + + MyDebug.OutputDebugAndConsole( + "CustomEncode.FormHexString: [" + CustomEncode.ToHexString(CustomEncode.FormHexString(hex)) + "]"); + + // **空のバイト配列。** 区切りの空白を削る分岐(0 < ret.Length)を通らない。 + MyDebug.OutputDebugAndConsole( + "CustomEncode.ToHexString(empty): [" + CustomEncode.ToHexString(new byte[0]) + "]"); + + // 小文字の Hex も読めるか + MyDebug.OutputDebugAndConsole( + "CustomEncode.FormHexString(lower): [" + CustomEncode.ToHexString(CustomEncode.FormHexString("0a bc")) + "]"); + + // Hex として読めない + TestCustomEncode.ShowException("CustomEncode.FormHexString(invalid)", + delegate { CustomEncode.FormHexString("ZZ"); }); + } + + /// Base64(ToBase64String、FromBase64String) + private static void Base64() + { + byte[] bytes = CustomEncode.StringToByte("こんにちは", CustomEncode.UTF_8); + + string b64 = CustomEncode.ToBase64String(bytes); + MyDebug.OutputDebugAndConsole("CustomEncode.ToBase64String: " + b64); + + MyDebug.OutputDebugAndConsole( + "CustomEncode.FromBase64String: " + + CustomEncode.ByteToString(CustomEncode.FromBase64String(b64), CustomEncode.UTF_8)); + + // 空のバイト配列 + MyDebug.OutputDebugAndConsole( + "CustomEncode.ToBase64String(empty): [" + CustomEncode.ToBase64String(new byte[0]) + "]"); + + // Base64 として読めない + TestCustomEncode.ShowException("CustomEncode.FromBase64String(invalid)", + delegate { CustomEncode.FromBase64String("!!!!"); }); + } + + /// Base64Url(ToBase64UrlString、FromBase64UrlString) + private static void Base64Url() + { + // **「+」と「/」が出る入力を選ぶ。** そうでないと置換の分岐を通らない。 + // FF EF BF → 標準 Base64 では "/++/" になる。 + byte[] plus = new byte[] { 0xFF, 0xEF, 0xBF }; + MyDebug.OutputDebugAndConsole("CustomEncode.ToBase64String : " + CustomEncode.ToBase64String(plus)); + MyDebug.OutputDebugAndConsole("CustomEncode.ToBase64UrlString: " + CustomEncode.ToBase64UrlString(plus)); + + // **パディングの 3 分岐(余り 0 / 2 / 3)を通す。** + // 3 バイト → 余り 0、1 バイト → 余り 2、2 バイト → 余り 3 + for (int len = 1; len <= 3; len++) + { + byte[] bytes = new byte[len]; + for (int i = 0; i < len; i++) { bytes[i] = (byte)(0xFF - i); } + + string b64url = CustomEncode.ToBase64UrlString(bytes); + + MyDebug.OutputDebugAndConsole( + "CustomEncode.ToBase64UrlString(" + len.ToString() + "byte): [" + b64url + "]" + + " 長さ%4=" + (b64url.Length % 4).ToString()); + + MyDebug.OutputDebugAndConsole( + "CustomEncode.FromBase64UrlString(" + len.ToString() + "byte): [" + + CustomEncode.ToHexString(CustomEncode.FromBase64UrlString(b64url)) + "]"); + } + + // **余り 1 は不正。** default 分岐(Illegal base64url string!)を通す。 + TestCustomEncode.ShowException("CustomEncode.FromBase64UrlString(invalid)", + delegate { CustomEncode.FromBase64UrlString("AAAAA"); }, true); + } + + /// エンコーディングの一覧(GetEncodings) + private static void Encodings() + { + DataTable dt = CustomEncode.GetEncodings(); + + MyDebug.OutputDebugAndConsole("CustomEncode.GetEncodings: 行数 " + dt.Rows.Count.ToString()); + MyDebug.OutputDebugAndConsole( + "CustomEncode.GetEncodings: 列 " + dt.Columns[0].ColumnName + ", " + dt.Columns[1].ColumnName); + + // 先頭と末尾(中身が環境で変わらないことの確認も兼ねる) + MyDebug.OutputDebugAndConsole( + "CustomEncode.GetEncodings[0]: " + dt.Rows[0]["key"].ToString() + " = " + dt.Rows[0]["value"].ToString()); + + DataRow last = dt.Rows[dt.Rows.Count - 1]; + MyDebug.OutputDebugAndConsole( + "CustomEncode.GetEncodings[last]: " + last["key"].ToString() + " = " + last["value"].ToString()); + } + + /// 例外を捕まえて出力する + /// 表題 + /// 実行する処理 + /// メッセージも出力するか + /// + /// **既定では型名しか出さない。** + /// フレームワークが投げる例外のメッセージは地域化されるため、 + /// 環境によって出力が変わってしまう(結果は Result*.txt と突き合わせる)。 + /// Open棟梁が自前で投げているものだけ、メッセージも出す。 + /// + private static void ShowException(string title, Action action, bool showMessage = false) + { + try + { + action(); + MyDebug.OutputDebugAndConsole(title + ": 例外なし"); + } + catch (Exception ex) + { + MyDebug.OutputDebugAndConsole( + title + ": " + ex.GetType().Name + (showMessage ? " : " + ex.Message : "")); + } + } + + #endregion } } \ No newline at end of file diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestDeflateCompression.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestDeflateCompression.cs index c1cfae730..7d3bffd5c 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestDeflateCompression.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestDeflateCompression.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestDeflateCompression +//* クラス日本語名 :DeflateCompressionのテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2019/05/21 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Text; using System.IO; diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestEnumToStringExtensions.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestEnumToStringExtensions.cs index 56ef9a4ae..fef8ac66e 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestEnumToStringExtensions.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestEnumToStringExtensions.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestEnumToStringExtensions +//* クラス日本語名 :EnumToStringExtensionsのテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2019/02/06 西野 大介 新規作成 +//********************************************************************************** + +using System; using Touryo.Infrastructure.Public.FastReflection; using Touryo.Infrastructure.Public.Diagnostics; diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestFormatChecker.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestFormatChecker.cs index 415bf22be..0049ca370 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestFormatChecker.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestFormatChecker.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestFormatChecker +//* クラス日本語名 :FormatCheckerのテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2020/08/01 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Text; using System.IO; diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestFormatConverter.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestFormatConverter.cs index d159ea2ff..faccd5932 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestFormatConverter.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestFormatConverter.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestFormatConverter +//* クラス日本語名 :FormatConverterのテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2020/07/31 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Collections.Generic; using Touryo.Infrastructure.Public.Str; @@ -21,11 +49,192 @@ public static void Root() MyDebug.OutputDebugAndConsole("--------------------------------------------------"); TestFormatConverter.AddFigureAndSuppressTest(); + + MyDebug.OutputDebugAndConsole("--------------------------------------------------"); + + TestFormatConverter.RoundingTest(); + + MyDebug.OutputDebugAndConsole("--------------------------------------------------"); + + TestFormatConverter.FloorAndCeilingTest(); + + MyDebug.OutputDebugAndConsole("--------------------------------------------------"); + + TestFormatConverter.AddFigureXTest(); + + MyDebug.OutputDebugAndConsole("--------------------------------------------------"); + + TestFormatConverter.UnixAndW3cTest(); } #endregion #region private + #region 丸め・桁合わせ + + /// 四捨五入と銀行家の丸め(Round_4sya5nyu、Round_Banker) + /// + /// **同じ入力を並べて対比する。** 差が出るのは 0.5 ちょうどのときだけで、 + /// 片方だけ試しても「違い」が見えない。 + /// + private static void RoundingTest() + { + MyDebug.OutputDebugAndConsole("FormatConverter.Round_4sya5nyu / Round_Banker"); + + object[] numbers = new object[] { 0.5m, 1.5m, 2.5m, 3.5m, -0.5m, -2.5m, 2.4m, 2.6m }; + + foreach (object n in numbers) + { + MyDebug.OutputDebugAndConsole( + " " + n.ToString() + " → 四捨五入 " + FormatConverter.Round_4sya5nyu(n, 0) + + " / 銀行家 " + FormatConverter.Round_Banker(n, 0)); + } + + // 小数点以下の桁数を指定 + MyDebug.OutputDebugAndConsole( + " 1.005(2桁) → 四捨五入 " + FormatConverter.Round_4sya5nyu(1.005m, 2) + + " / 銀行家 " + FormatConverter.Round_Banker(1.005m, 2)); + + // **数値として読めないと "0" を返す。** else 側の分岐。 + MyDebug.OutputDebugAndConsole( + " \"abc\" → 四捨五入 " + FormatConverter.Round_4sya5nyu("abc", 0) + + " / 銀行家 " + FormatConverter.Round_Banker("abc", 0)); + + // 文字列で渡しても、数値として読めれば処理される + MyDebug.OutputDebugAndConsole( + " \"2.5\"(文字列) → 四捨五入 " + FormatConverter.Round_4sya5nyu("2.5", 0)); + } + + /// 切り捨てと切り上げ(Floor、Ceiling) + /// + /// **負の数で向きの違いが出る。** 正の数だけでは RZ と RM、RI と RP が同じ値になり、 + /// 引数の意味が確かめられない。 + /// + private static void FloorAndCeilingTest() + { + MyDebug.OutputDebugAndConsole("FormatConverter.Floor / Ceiling"); + + object[] numbers = new object[] { 1.567m, -1.567m }; + + foreach (object n in numbers) + { + MyDebug.OutputDebugAndConsole( + " Floor " + n.ToString() + "(2桁) : 既定 " + FormatConverter.Floor(n, 2) + + " / RZ(0方向) " + FormatConverter.Floor(n, 2, FloorToward.RZ) + + " / RM(負の無限大) " + FormatConverter.Floor(n, 2, FloorToward.RM)); + + MyDebug.OutputDebugAndConsole( + " Ceiling " + n.ToString() + "(2桁) : 既定 " + FormatConverter.Ceiling(n, 2) + + " / RI(絶対値) " + FormatConverter.Ceiling(n, 2, CeilingToward.RI) + + " / RP(正の無限大) " + FormatConverter.Ceiling(n, 2, CeilingToward.RP)); + } + + // 桁数 0(シフトのループを 1 度も回らない) + MyDebug.OutputDebugAndConsole( + " Floor 1.9(0桁) : " + FormatConverter.Floor(1.9m, 0) + + " / Ceiling 1.1(0桁) : " + FormatConverter.Ceiling(1.1m, 0)); + + // **数値として読めないと "0" を返す。** + MyDebug.OutputDebugAndConsole( + " \"abc\" : Floor " + FormatConverter.Floor("abc", 2) + + " / Ceiling " + FormatConverter.Ceiling("abc", 2)); + } + + /// 桁区切りと 0 の補充(AddFigureX、AddZerosAfterDecimal) + private static void AddFigureXTest() + { + MyDebug.OutputDebugAndConsole("FormatConverter.AddFigureX"); + + foreach (int size in new int[] { 2, 3, 4 }) + { + MyDebug.OutputDebugAndConsole( + " 1234567.891 を " + size.ToString() + "桁区切り : " + + FormatConverter.AddFigureX(1234567.891m, size)); + } + + // 負の数(絶対値にしてから戻す分岐) + MyDebug.OutputDebugAndConsole( + " -1234567.891 を 3桁区切り : " + FormatConverter.AddFigureX(-1234567.891m, 3)); + + // **数値として読めないと "0" を返す。** + MyDebug.OutputDebugAndConsole( + " \"abc\" を 3桁区切り : " + FormatConverter.AddFigureX("abc", 3)); + + MyDebug.OutputDebugAndConsole("FormatConverter.AddZerosAfterDecimal"); + + // **整数部のみ(split の結果が 1 個)と、小数部あり(2 個)で分岐が違う。** + MyDebug.OutputDebugAndConsole(" 123(3桁) : " + FormatConverter.AddZerosAfterDecimal(123m, 3)); + MyDebug.OutputDebugAndConsole(" 123.4(3桁) : " + FormatConverter.AddZerosAfterDecimal(123.4m, 3)); + + // 既に桁数を満たしている(0 を足さない) + MyDebug.OutputDebugAndConsole(" 123.456(2桁) : " + FormatConverter.AddZerosAfterDecimal(123.456m, 2)); + + // 桁数 0(整数部のみのとき、小数点も付かない) + MyDebug.OutputDebugAndConsole(" 123(0桁) : " + FormatConverter.AddZerosAfterDecimal(123m, 0)); + + // 負の数 + MyDebug.OutputDebugAndConsole(" -1.2(3桁) : " + FormatConverter.AddZerosAfterDecimal(-1.2m, 3)); + } + + #endregion + + #region UNIX時間・W3C時間 + + /// UNIX時間と W3C Timestamp(ToUnixTime、FromUnixTime、ToW3cTimestamp、FromW3cTimestamp) + /// + /// **DateTimeKind.Utc を明示する。** + /// ToUnixTime は ToUniversalTime() を通すため、Kind が Local や Unspecified だと + /// **実行環境の時刻帯で結果が変わる。** 結果は Result*.txt と突き合わせるので、 + /// 環境で変わる値をここに出してはいけない。 + /// + /// 出力の書式も明示する。DateTime.ToString() は文化圏で形が変わる。 + /// + private static void UnixAndW3cTest() + { + const string fmt = "yyyy/MM/dd HH:mm:ss"; + + MyDebug.OutputDebugAndConsole("FormatConverter.ToUnixTime / FromUnixTime"); + + DateTime[] times = new DateTime[] + { + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), // epoch + new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc), + new DateTime(2038, 1, 19, 3, 14, 7, DateTimeKind.Utc), // 32bit の上限 + new DateTime(1960, 1, 1, 0, 0, 0, DateTimeKind.Utc) // epoch より前(負の値) + }; + + foreach (DateTime t in times) + { + long unix = FormatConverter.ToUnixTime(t); + + MyDebug.OutputDebugAndConsole( + " " + t.ToString(fmt) + " → " + unix.ToString() + + " → " + FormatConverter.FromUnixTime(unix).ToString(fmt) + + "(Kind=" + FormatConverter.FromUnixTime(unix).Kind.ToString() + ")"); + } + + // **秒未満は切り捨てられる。** TotalSeconds を long にキャストしているため。 + MyDebug.OutputDebugAndConsole( + " 秒未満 : " + FormatConverter.ToUnixTime( + new DateTime(1970, 1, 1, 0, 0, 0, 999, DateTimeKind.Utc)).ToString()); + + MyDebug.OutputDebugAndConsole("FormatConverter.ToW3cTimestamp / FromW3cTimestamp"); + + DateTime utc = new DateTime(2020, 5, 6, 7, 8, 9, DateTimeKind.Utc); + + string w3c = FormatConverter.ToW3cTimestamp(utc); + MyDebug.OutputDebugAndConsole(" 既定の書式 : " + w3c); + + MyDebug.OutputDebugAndConsole( + " 書式を指定 : " + FormatConverter.ToW3cTimestamp(utc, "yyyy-MM-dd HH:mm:ssZ")); + + DateTime back = FormatConverter.FromW3cTimestamp(w3c); + MyDebug.OutputDebugAndConsole( + " 戻り : " + back.ToString(fmt) + "(Kind=" + back.Kind.ToString() + ")"); + } + + #endregion + #region 和暦・西暦 /// SeirekiToWarekiTest private static void SeirekiToWarekiTest() diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestGetMessageAndProperty.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestGetMessageAndProperty.cs index ebe9eeff9..552724e4b 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestGetMessageAndProperty.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestGetMessageAndProperty.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestGetMessageAndProperty +//* クラス日本語名 :メッセージ・プロパティ取得のテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2020/07/31 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Text; using System.IO; diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestJISCode.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestJISCode.cs index a6a2fc967..0c9b36c95 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestJISCode.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestJISCode.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestJISCode +//* クラス日本語名 :JISコード関連のテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2020/07/31 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Text; using System.IO; diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestOutputLog.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestOutputLog.cs index b0f0f98e5..8b5b950ac 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestOutputLog.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestOutputLog.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestOutputLog +//* クラス日本語名 :ログ出力のテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2020/07/31 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Text; using System.IO; diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestStringChecker.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestStringChecker.cs index a6f3f7883..dfad7b38b 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestStringChecker.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestStringChecker.cs @@ -1,5 +1,34 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestStringChecker +//* クラス日本語名 :StringCheckerのテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2020/07/31 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Text; +using System.Text.RegularExpressions; using System.IO; using Touryo.Infrastructure.Public.Str; @@ -166,7 +195,115 @@ public static void Root() MyDebug.OutputDebugAndConsole( "StringChecker.IsShift_Jis_Hankaku - " + temp + ": " + StringChecker.IsShift_Jis_Hankaku(temp)); + + MyDebug.OutputDebugAndConsole("--------------------------------------------------"); + + TestStringChecker.HankakuZenkakuTest(); + TestStringChecker.IsNumericTest(); + TestStringChecker.IsInCodePageTest(); + TestStringChecker.RegexTest(); + } + #endregion + + #region private + + /// 半角・全角の判定(IsHankaku、IsZenkaku) + /// + /// **どちらも「空文字列は true」。** 0 回以上の連続マッチ(*)なので、 + /// 空なら両方 true になる。呼ぶ側が見落としやすい。 + /// + private static void HankakuZenkakuTest() + { + // 半角空白・記号・半角カナも「半角」に含まれる(^[ -~。-゚]*$) + string[] inputs = new string[] { "abc123", "アイウ", "あいう", "abc", "abcあ", " ", "~", "" }; + + foreach (string s in inputs) + { + MyDebug.OutputDebugAndConsole( + "StringChecker.IsHankaku/IsZenkaku - [" + s + "]: " + + StringChecker.IsHankaku(s) + " / " + StringChecker.IsZenkaku(s)); + } + } + + /// 数値として読めるかの判定(IsNumeric) + /// + /// **IsNumbers との違いを見る。** + /// IsNumbers は「数字のみ」、IsNumeric は「数値として読めるか」で、 + /// 符号・小数点・指数が通る。全角は半角化してから判定される。 + /// + private static void IsNumericTest() + { + string[] inputs = new string[] + { + "123", "-123", "1.5", "+1", "1e3", "123", "1.5", "12 3", "abc", "", " " + }; + + foreach (string s in inputs) + { + MyDebug.OutputDebugAndConsole( + "StringChecker.IsNumeric - [" + s + "]: " + StringChecker.IsNumeric(s) + + "(IsNumbers: " + StringChecker.IsNumbers(s) + ")"); + } + } + + /// コードページに収まるかの判定(IsInCodePage) + /// + /// **往復して戻るかで判定している。** 収まらない文字は「?」や U+FFFD に化けるため、 + /// 元と一致しなくなる。 + /// + private static void IsInCodePageTest() + { + string[] inputs = new string[] { "abc", "あいう", "①", "𩸽", "" }; + + foreach (string s in inputs) + { + MyDebug.OutputDebugAndConsole( + "StringChecker.IsInCodePage - [" + s + "]: " + + "shift_jis " + StringChecker.IsInCodePage(s, CustomEncode.shift_jis) + + " / us_ascii " + StringChecker.IsInCodePage(s, CustomEncode.us_ascii) + + " / utf_8 " + StringChecker.IsInCodePage(s, CustomEncode.UTF_8)); + } + } + + /// 正規表現(Match、Matches) + private static void RegexTest() + { + // **Match は「どこかに一致」。** 先頭・末尾を固定しないと部分一致になる。 + MyDebug.OutputDebugAndConsole( + "StringChecker.Match - [abc123] [0-9]+ : " + StringChecker.Match("abc123", "[0-9]+")); + + MyDebug.OutputDebugAndConsole( + "StringChecker.Match - [abc123] ^[0-9]+$ : " + StringChecker.Match("abc123", "^[0-9]+$")); + + MyDebug.OutputDebugAndConsole( + "StringChecker.Match - [abc] [0-9]+ : " + StringChecker.Match("abc", "[0-9]+")); + + // オプション付き(大文字小文字を無視) + MyDebug.OutputDebugAndConsole( + "StringChecker.Match - [ABC] ^abc$ : " + StringChecker.Match("ABC", "^abc$")); + + MyDebug.OutputDebugAndConsole( + "StringChecker.Match - [ABC] ^abc$ (IgnoreCase) : " + + StringChecker.Match("ABC", "^abc$", RegexOptions.IgnoreCase)); + + // Matches は一致の個数と中身 + MatchCollection mc = StringChecker.Matches("a1b22c333", "[0-9]+"); + + MyDebug.OutputDebugAndConsole( + "StringChecker.Matches - [a1b22c333] [0-9]+ : " + mc.Count.ToString() + " 件"); + + foreach (Match m in mc) + { + MyDebug.OutputDebugAndConsole( + " 位置 " + m.Index.ToString() + " : " + m.Value); + } + + // 一致なし(空のコレクション) + MyDebug.OutputDebugAndConsole( + "StringChecker.Matches - [abc] [0-9]+ : " + + StringChecker.Matches("abc", "[0-9]+").Count.ToString() + " 件"); } + #endregion } } \ No newline at end of file diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestStringConverter.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestStringConverter.cs index f69bad39c..e6568647c 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestStringConverter.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestStringConverter.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestStringConverter +//* クラス日本語名 :StringConverterのテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2020/07/31 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Text; using System.IO; @@ -26,7 +54,73 @@ public static void Root() temp = result; result = StringConverter.ToKatakana(temp); MyDebug.OutputDebugAndConsole("StringConverter.ToKatakana - " + temp + ": " + result); + + MyDebug.OutputDebugAndConsole("--------------------------------------------------"); + + TestStringConverter.EditYYYYMMDDStringTest(); + TestStringConverter.FormattingForOneLineLogTest(); + } + #endregion + + #region private + + /// 日付文字列の桁揃え(EditYYYYMMDDString) + /// + /// **7 桁のときの解釈が 2 通りある。** + /// 5〜6 文字目が 13 以上なら「月が 1 桁」、13 未満なら「日が 1 桁」とみなす。 + /// 両方を通さないと、この判定が効いているか分からない。 + /// + private static void EditYYYYMMDDStringTest() + { + string[] inputs = new string[] + { + "20200102", // 8 桁(そのまま) + "2020115", // 7 桁・5〜6文字目 "11" → 13 未満 → 月 11、日 5 + "2020155", // 7 桁・5〜6文字目 "15" → 13 以上 → 月 1、日 55 + "202012", // 6 桁 → 月・日とも 1 桁 + "2020", // 桁が足りない + "20201a02", // 数字以外を含む + "" // 空文字列 + }; + + foreach (string s in inputs) + { + // **ref 引数なので、渡した変数が書き換わる。** + string work = s; + bool ret = StringConverter.EditYYYYMMDDString(ref work); + + MyDebug.OutputDebugAndConsole( + "StringConverter.EditYYYYMMDDString - [" + s + "]: " + ret + " → [" + work + "]"); + } + } + + /// 1 行ログ向けの整形(FormattingForOneLineLog) + /// + /// **文字列の中(シングルクォートで囲まれた範囲)では空白を詰めない。** + /// クォートの内と外を 1 つの入力に混ぜないと、その分岐を通らない。 + /// + private static void FormattingForOneLineLogTest() + { + string[] inputs = new string[] + { + "SELECT * FROM Orders", // 連続した空白を詰める + "SELECT * FROM T WHERE C = 'a b'", // 文字列中の空白は残る + "a\r\nb\rc\nd", // 改行は空白になる + "A & B", // & のエスケープ + "WHERE C = 'It''s'", // エスケープされたシングルクォート + "", // 空文字列 + " " // 空白のみ + }; + + foreach (string s in inputs) + { + MyDebug.OutputDebugAndConsole( + "StringConverter.FormattingForOneLineLog - [" + + s.Replace("\r", "\\r").Replace("\n", "\\n") + "]: [" + + StringConverter.FormattingForOneLineLog(s) + "]"); + } } + #endregion } } \ No newline at end of file diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestXmlLib.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestXmlLib.cs index 2e5203e7e..6c11ef14e 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestXmlLib.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestXmlLib.cs @@ -1,4 +1,35 @@ -using Touryo.Infrastructure.Public.IO; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :TestXmlLib +//* クラス日本語名 :XmlLibのテスト +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2019/05/30 西野 大介 新規作成 +//********************************************************************************** + +using System; +using System.Xml; + +using Touryo.Infrastructure.Public.IO; using Touryo.Infrastructure.Public.Xml; using Touryo.Infrastructure.Public.Diagnostics; @@ -25,7 +56,139 @@ public static void Root() { MyDebug.OutputDebugAndConsole("XmlLib", "is not working properly."); } + + MyDebug.OutputDebugAndConsole("--------------------------------------------------"); + + TestXmlLib.GetAttributeTest(); + TestXmlLib.GetXmlNodeByIdTest(); + TestXmlLib.GetEncodingTest(); + } + #endregion + + #region private + + /// テスト用の XML + /// + /// **埋め込みの TestXml.xml は名前空間つき**なので、XPath に + /// XmlNamespaceManager が要る。属性の取得そのものを見たいので、 + /// ここでは名前空間なしの XML を自前で組む。 + /// + private const string Xml = + "" + + "" + + "" + + "" + + ""; + + /// 属性の取得(GetAttributeByTagName、GetAttributeByXPath、GetAttributeFromXmlNode) + /// + /// **見つからないときは空文字列を返す。** 例外にはならないので、 + /// 呼ぶ側は「空」と「属性値が空」を区別できない。 + /// + private static void GetAttributeTest() + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(TestXmlLib.Xml); + + // タグ名で引く + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByTagName - item/name: [" + XmlLib.GetAttributeByTagName(doc, "item", "name") + "]"); + + // **タグが複数あっても、先頭しか見ない。** index を渡しても変わらない。 + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByTagName - item/name(index=1): [" + + XmlLib.GetAttributeByTagName(doc, "item", "name", 1) + "]"); + + // 属性が無い要素 + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByTagName - empty/name: [" + XmlLib.GetAttributeByTagName(doc, "empty", "name") + "]"); + + // タグが無い + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByTagName - none/name: [" + XmlLib.GetAttributeByTagName(doc, "none", "name") + "]"); + + // 属性名が無い + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByTagName - item/none: [" + XmlLib.GetAttributeByTagName(doc, "item", "none") + "]"); + + // XPath で引く + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByXPath - //item[@id='i2']/name: [" + + XmlLib.GetAttributeByXPath(doc, "//item[@id='i2']", "name") + "]"); + + // XPath が当たらない(SelectSingleNode が null) + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByXPath - //none: [" + XmlLib.GetAttributeByXPath(doc, "//none", "name") + "]"); + + // XmlNode から直接 + XmlNode node = doc.SelectSingleNode("//item[@id='i1']"); + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeFromXmlNode - name: [" + XmlLib.GetAttributeFromXmlNode(node, "name") + "]"); + + // **null を渡しても例外にならない。** + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeFromXmlNode - null: [" + XmlLib.GetAttributeFromXmlNode(null, "name") + "]"); + } + + /// id によるノードの取得(GetXmlNodeById) + /// + /// **2 段構えになっている。** まず「//<引数>」で XPath 検索し、 + /// 当たらなければルート要素の id 属性を見に行く。 + /// 引数はノード名として使われるので、id の値ではない点に注意。 + /// + private static void GetXmlNodeByIdTest() + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(TestXmlLib.Xml); + + // 1 段目:ノード名で当たる + XmlNode n1 = XmlLib.GetXmlNodeById(doc, "item"); + MyDebug.OutputDebugAndConsole( + "XmlLib.GetXmlNodeById - item: " + + (n1 == null ? "(null)" : n1.Name + " id=" + XmlLib.GetAttributeFromXmlNode(n1, "id"))); + + // 2 段目:ノード名では当たらず、ルートの id 属性で当たる + XmlNode n2 = XmlLib.GetXmlNodeById(doc, "r1"); + MyDebug.OutputDebugAndConsole( + "XmlLib.GetXmlNodeById - r1: " + + (n2 == null ? "(null)" : n2.Name + " id=" + XmlLib.GetAttributeFromXmlNode(n2, "id"))); + + // どちらでも当たらない + XmlNode n3 = XmlLib.GetXmlNodeById(doc, "i1"); + MyDebug.OutputDebugAndConsole( + "XmlLib.GetXmlNodeById - i1: " + + (n3 == null ? "(null)" : n3.Name + " id=" + XmlLib.GetAttributeFromXmlNode(n3, "id"))); + } + + /// XML 宣言からのエンコーディング取得(GetEncodingFromXmlDeclaration) + private static void GetEncodingTest() + { + string[] declarations = new string[] + { + "", + "", + "", // encoding が無い + "" // 知らないエンコーディング + }; + + foreach (string d in declarations) + { + try + { + MyDebug.OutputDebugAndConsole( + "XmlLib.GetEncodingFromXmlDeclaration - " + d + " : " + + XmlLib.GetEncodingFromXmlDeclaration(d).WebName); + } + catch (Exception ex) + { + // **例外は ArgumentException に包み直される。** + // メッセージは Open棟梁 のリソース由来なので、型名だけを出す。 + MyDebug.OutputDebugAndConsole( + "XmlLib.GetEncodingFromXmlDeclaration - " + d + " : " + ex.GetType().Name); + } + } } + #endregion } } \ No newline at end of file diff --git a/root/programs/CS/Frameworks/Tests/TestLog/Form1.cs b/root/programs/CS/Frameworks/Tests/TestLog/Form1.cs index 9110e4087..16544dca9 100644 --- a/root/programs/CS/Frameworks/Tests/TestLog/Form1.cs +++ b/root/programs/CS/Frameworks/Tests/TestLog/Form1.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :Form1 +//* クラス日本語名 :ログ出力テストの画面 +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2025/06/15 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.IO; using System.Text; using System.Data; diff --git a/root/programs/CS/Frameworks/Tests/TestLog/Form2.cs b/root/programs/CS/Frameworks/Tests/TestLog/Form2.cs index c0ece0cde..a94693d2d 100644 --- a/root/programs/CS/Frameworks/Tests/TestLog/Form2.cs +++ b/root/programs/CS/Frameworks/Tests/TestLog/Form2.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :Form2 +//* クラス日本語名 :ログ出力テストの画面 +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2025/06/15 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.IO; using System.Text; using System.Data; diff --git a/root/programs/CS/Frameworks/Tests/TestLog/Form3.cs b/root/programs/CS/Frameworks/Tests/TestLog/Form3.cs index 5b22a775b..6d8b097c7 100644 --- a/root/programs/CS/Frameworks/Tests/TestLog/Form3.cs +++ b/root/programs/CS/Frameworks/Tests/TestLog/Form3.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :Form3 +//* クラス日本語名 :ログ出力テストの画面 +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2025/06/15 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.IO; using System.Text; using System.Data; diff --git a/root/programs/CS/Frameworks/Tests/TestLog/Program.cs b/root/programs/CS/Frameworks/Tests/TestLog/Program.cs index 81cfaf853..34a32965e 100644 --- a/root/programs/CS/Frameworks/Tests/TestLog/Program.cs +++ b/root/programs/CS/Frameworks/Tests/TestLog/Program.cs @@ -1,4 +1,32 @@ -using System; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :Program +//* クラス日本語名 :ログ出力テストのエントリ ポイント +//* +//* 作成者 :西野 大介 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2025/06/15 西野 大介 新規作成 +//********************************************************************************** + +using System; using System.Threading; using System.Resources; using System.Globalization; diff --git a/root/programs/CS/Frameworks/Tests/TestTransmission/AssemblyInfo.cs b/root/programs/CS/Frameworks/Tests/TestTransmission/AssemblyInfo.cs index ad3236bcb..8ebed1aa4 100644 --- a/root/programs/CS/Frameworks/Tests/TestTransmission/AssemblyInfo.cs +++ b/root/programs/CS/Frameworks/Tests/TestTransmission/AssemblyInfo.cs @@ -1,4 +1,32 @@ -using System.Reflection; +#region Apache License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +//********************************************************************************** +//* クラス名 :AssemblyInfo +//* クラス日本語名 :アセンブリ情報 +//* +//* 作成者 :玄人 幸道 +//* 更新履歴 : +//* +//* 日時 更新者 内容 +//* ---------- ---------------- ------------------------------------------------- +//* 2026/08/14 玄人 幸道 新規作成 +//********************************************************************************** + +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/root/programs/CompareResult.ps1 b/root/programs/CompareResult.ps1 index 86b52c054..07b73ee7b 100644 --- a/root/programs/CompareResult.ps1 +++ b/root/programs/CompareResult.ps1 @@ -41,6 +41,8 @@ ---------- ---------------- ------------------------------------------------- 2026/08/01 玄人 幸道 新規作成(リリース ワークのエージェント化) 2026/08/05 玄人 幸道 OSメッセージの正規化を追加(CI の英語環境への対応) + 2026/08/17 玄人 幸道 -SyncWindow 20 をやめた(#552)。20 行を超える +                 挿入で再同期できず、以降が全部差分になっていた。 #> [CmdletBinding()] param( @@ -151,7 +153,17 @@ if (-not (Test-Path $Actual)) { Write-Error "実行結果ファイルが見つ $expLines = Normalize (Get-Content $Expected -Encoding UTF8) $actLines = Normalize (Get-Content $Actual -Encoding UTF8) -$diff = @(Compare-Object $expLines $actLines -SyncWindow 20) +# **同期窓を狭めない。**(#552) +# -SyncWindow 20 では「20 行を超える挿入」で再同期できず、 +# **挿入位置より後ろが全部差分になる。** +# テストケースを 36 行足しただけで 532 件と報告された(実測)。 +# +# 広げると、正確になるうえに速い(結果の集合が小さくなるため)。 +# 窓 20 : 532 件 / 112 ms +# 窓 100〜 : 46 件 / 8 ms ← git diff の +41/-5 と一致する +# +# 既定([Int32]::MaxValue)で 39 ms なので、狭める理由が無い。 +$diff = @(Compare-Object $expLines $actLines) Write-Host "" Write-Host "=== 比較結果 ===" From 63b6730023bb2122ee2d9f3f69d53cdc6e2f9927 Mon Sep 17 00:00:00 2001 From: nishi_74322014 Date: Mon, 17 Aug 2026 23:18:37 +0900 Subject: [PATCH 3/7] fixed #552 --- .../CS/Frameworks/Tests/TestCode/Result48.txt | 125 ++++++++++++++++- .../Tests/TestCode/ResultCore100.txt | 127 +++++++++++++++++- .../CS/Frameworks/Tests/TestCode/TestDto.cs | 67 +++++++++ .../Tests/TestCode/TestOutputLog.cs | 26 ++++ .../Tests/TestCode/TestResourceLoader.cs | 29 ++++ .../CS/Frameworks/Tests/TestCode/TestUtil.cs | 124 +++++++++++++++++ 6 files changed, 488 insertions(+), 10 deletions(-) diff --git a/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt b/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt index 544a05dea..cd333fd4b 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt +++ b/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt @@ -70,11 +70,87 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level log4net: Created Appender [OPERATION2] log4net: Adding appender named [OPERATION2] to logger [OPERATION]. log4net: Hierarchy Threshold [] -[2026/08/17 17:21:01,782],[DEBUG],[1],LogIF.DebugLog("ACCESS"); -[2026/08/17 17:21:01,795],[INFO ],[1],LogIF.InfoLog("ACCESS"); -[2026/08/17 17:21:01,796],[WARN ],[1],LogIF.WarnLog("ACCESS"); -[2026/08/17 17:21:01,796],[ERROR],[1],LogIF.ErrorLog("ACCESS"); -[2026/08/17 17:21:01,796],[FATAL],[1],LogIF.FatalLog("ACCESS"); +[2026/08/17 22:30:31,294],[DEBUG],[1],LogIF.DebugLog("ACCESS"); +[2026/08/17 22:30:31,314],[INFO ],[1],LogIF.InfoLog("ACCESS"); +[2026/08/17 22:30:31,315],[WARN ],[1],LogIF.WarnLog("ACCESS"); +[2026/08/17 22:30:31,316],[ERROR],[1],LogIF.ErrorLog("ACCESS"); +[2026/08/17 22:30:31,317],[FATAL],[1],LogIF.FatalLog("ACCESS"); +LogIF.Is*Enabled - ACCESS : Debug True / Info True / Warn True / Error True / Fatal True +log4net: configuring repository [log4net-default-repository] using stream +log4net: loading XML configuration +log4net: Configuring Repository [log4net-default-repository] +log4net: Configuration update mode [Merge]. +log4net: Retrieving an instance of log4net.Repository.Logger for logger [ACCESS]. +log4net: Setting [ACCESS] additivity to [True]. +log4net: Logger [ACCESS] Level string is [All]. +log4net: Logger [ACCESS] level set to [name="ALL",value=-2147483648]. +log4net: Loading Appender [ACCESS2] type: [log4net.Appender.ConsoleAppender] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [ConversionPattern] to String value [[%date{yyyy/MM/dd HH:mm:ss,fff}],[%-5level],[%thread],%message%newline] +log4net: Converter [literal] Option [[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [date] Option [yyyy/MM/dd HH:mm:ss,fff] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [level] Option [] Format [min=5,max=2147483647,leftAlign=True] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [thread] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [Layout] to object [log4net.Layout.PatternLayout] +log4net: Setting Property [LevelMin] to Level value [DEBUG] +log4net: Setting Property [LevelMax] to Level value [FATAL] +log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.LevelRangeFilter] +log4net: Created Appender [ACCESS2] +log4net: Adding appender named [ACCESS2] to logger [ACCESS]. +log4net: Retrieving an instance of log4net.Repository.Logger for logger [SQLTRACE]. +log4net: Setting [SQLTRACE] additivity to [True]. +log4net: Logger [SQLTRACE] Level string is [All]. +log4net: Logger [SQLTRACE] level set to [name="ALL",value=-2147483648]. +log4net: Loading Appender [SQLTRACE2] type: [log4net.Appender.ConsoleAppender] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [ConversionPattern] to String value [[%date{yyyy/MM/dd HH:mm:ss,fff}],[%-5level],[%thread],%message%newline] +log4net: Converter [literal] Option [[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [date] Option [yyyy/MM/dd HH:mm:ss,fff] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [level] Option [] Format [min=5,max=2147483647,leftAlign=True] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [thread] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [Layout] to object [log4net.Layout.PatternLayout] +log4net: Setting Property [LevelMin] to Level value [DEBUG] +log4net: Setting Property [LevelMax] to Level value [FATAL] +log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.LevelRangeFilter] +log4net: Created Appender [SQLTRACE2] +log4net: Adding appender named [SQLTRACE2] to logger [SQLTRACE]. +log4net: Retrieving an instance of log4net.Repository.Logger for logger [OPERATION]. +log4net: Setting [OPERATION] additivity to [True]. +log4net: Logger [OPERATION] Level string is [All]. +log4net: Logger [OPERATION] level set to [name="ALL",value=-2147483648]. +log4net: Loading Appender [OPERATION2] type: [log4net.Appender.ConsoleAppender] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [ConversionPattern] to String value [[%date{yyyy/MM/dd HH:mm:ss,fff}],[%-5level],[%thread],%message%newline] +log4net: Converter [literal] Option [[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [date] Option [yyyy/MM/dd HH:mm:ss,fff] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [level] Option [] Format [min=5,max=2147483647,leftAlign=True] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [thread] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [Layout] to object [log4net.Layout.PatternLayout] +log4net: Setting Property [LevelMin] to Level value [DEBUG] +log4net: Setting Property [LevelMax] to Level value [FATAL] +log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.LevelRangeFilter] +log4net: Created Appender [OPERATION2] +log4net: Adding appender named [OPERATION2] to logger [OPERATION]. +log4net: Hierarchy Threshold [] +LogIF.Is*Enabled - UNDEFINED : Debug True / Info True / Warn True / Error True / Fatal True ---------------------------------------------------------------------------------------------------- GetMessage: ~メッセージID:I0001に対応する記述(正常系)~ GetMessage: ~メッセージID:E0001に対応する記述(異常系)~ @@ -2303,6 +2379,24 @@ GetConfigValue(FxBusinessMessageCulture) : ja-JP EnvInfo.MachineName が取れる : True EnvInfo.OsVersionString が取れる : True EnvInfo.ProcessBit が 32 または 64 : True +GetConfigParameter +GetAnyConfigValue / GetAnyConfigSection : .NET Core 側のみのため対象外 +GetConnectionString(定義が無い名前。例外にならず空が返る) : null または空 +PubCmnFunction.GetPropsFromPropString + [a=1;b=2;] → 2 件 : A=[1], B=[2] + [UserName=fxuser;Password=fxpass;] → 2 件 : USERNAME=[fxuser], PASSWORD=[fxpass] + [a=1] → 1 件 : A=[1] + [] → 0 件 : + [a=;b=2;] → 2 件 : A=[], B=[2] +PubCmnFunction.BuiltStringIntoEnvironmentVariable + [%OT_TEST_VAR%] → [展開後] + [前%OT_TEST_VAR%後] → [前展開後後] + [C:\%OT_TEST_VAR%\end] → [C:\展開後\end] + [%OT_TEST_NOT_EXIST%] → [] + [変数なし] → [変数なし] +PubCmnFunction.GetCommandArgs + prefix='/' : 名前付き 0 件 / 値のみ 0 件 + prefix='-' : 名前付き 0 件 / 値のみ 0 件 ---------------------------------------------------------------------------------------------------- StringConverter.ToHankaku - アアア: アアア StringConverter.ToZenkaku - アアア: アアア @@ -2564,6 +2658,9 @@ EmbeddedResourceLoader 読み込み : 埋め込まれたリソースの内容 / embedded resource 存在しない : False 存在しない(例外) : System.ArgumentException +LoadAsStream : 埋め込まれたリソースの内容 / embedded resource +LoadXMLAsString : 長さ 292 / ルート要素 bookstore +LoadXMLAsString : XML宣言が残るか True ---------------------------------------------------------------------------------------------------- ZipperV2 → UnZipperV2(往復) 解凍した件数 : 4 @@ -2706,6 +2803,24 @@ DataSet との JSON 往復 行 : Id=3, Note="削除される", RowState=Deleted 行 : Id=4, Note="追加", RowState=Added ---------------------------------------------------------------------------------------------------- +DTTables の中間生成物(JsonTables)との往復 +[中間生成物] 表の数 : 2 + 表名 TestTypes / 列 7 / 行 4 + 列 : Id:Int32, Name:String, Price:Decimal, Rate:Double, Ordered:DateTime, Flag:Boolean, Blob:ByteArray + 表名 TestOdd / 列 2 / 行 1 + 列 : state:String, cels:Int32 +[戻した DTTables] +表名 : TestTypes +列 : Id(Int32), Name(String), Price(Decimal), Rate(Double), Ordered(DateTime), Flag(Boolean), Blob(ByteArray) +行 : Id=1, Name="あいう", Price=1234.56, Rate=0.30000000000000004, Ordered=2026-08-14T12:34:56.7890000, Flag=True, Blob=byte[3]:00-01-FF, RowState=Added +行 : Id=2, Name="", Price=-0.01, Rate=1.7976931348623157E+308, Ordered=1977-04-24T00:00:00.0000000, Flag=False, Blob=byte[0]:, RowState=Added +行 : Id=3, Name="あ\r\nい", Price=0, Rate=0, Ordered=2000-01-01T00:00:00.0000000, Flag=False, Blob=byte[1]:01, RowState=Added +行 : Id=4, Name=(null), Price=(null), Rate=(null), Ordered=(null), Flag=(null), Blob=(null), RowState=Added +表名 : TestOdd +列 : state(String), cels(Int32) +行 : state="わな", cels=7, RowState=Added +[空の DTTables] 列挙の回数 : 0 +---------------------------------------------------------------------------------------------------- ObjectInspector.Inspect : 基本の型 [null] null diff --git a/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt b/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt index 68711fb71..3b6ebe545 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt +++ b/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt @@ -70,11 +70,87 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level log4net: Created Appender [OPERATION2] log4net: Adding appender named [OPERATION2] to logger [OPERATION]. log4net: Hierarchy Threshold [] -[2026/08/17 17:21:06,781],[DEBUG],[2],LogIF.DebugLog("ACCESS"); -[2026/08/17 17:21:06,789],[INFO ],[2],LogIF.InfoLog("ACCESS"); -[2026/08/17 17:21:06,789],[WARN ],[2],LogIF.WarnLog("ACCESS"); -[2026/08/17 17:21:06,790],[ERROR],[2],LogIF.ErrorLog("ACCESS"); -[2026/08/17 17:21:06,790],[FATAL],[2],LogIF.FatalLog("ACCESS"); +[2026/08/17 22:30:46,582],[DEBUG],[2],LogIF.DebugLog("ACCESS"); +[2026/08/17 22:30:46,619],[INFO ],[2],LogIF.InfoLog("ACCESS"); +[2026/08/17 22:30:46,623],[WARN ],[2],LogIF.WarnLog("ACCESS"); +[2026/08/17 22:30:46,624],[ERROR],[2],LogIF.ErrorLog("ACCESS"); +[2026/08/17 22:30:46,625],[FATAL],[2],LogIF.FatalLog("ACCESS"); +LogIF.Is*Enabled - ACCESS : Debug True / Info True / Warn True / Error True / Fatal True +log4net: configuring repository [log4net-default-repository] using stream +log4net: loading XML configuration +log4net: Configuring Repository [log4net-default-repository] +log4net: Configuration update mode [Merge]. +log4net: Retrieving an instance of log4net.Repository.Logger for logger [ACCESS]. +log4net: Setting [ACCESS] additivity to [True]. +log4net: Logger [ACCESS] Level string is [All]. +log4net: Logger [ACCESS] level set to [name="ALL",value=-2147483648]. +log4net: Loading Appender [ACCESS2] type: [log4net.Appender.ConsoleAppender] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [ConversionPattern] to String value [[%date{yyyy/MM/dd HH:mm:ss,fff}],[%-5level],[%thread],%message%newline] +log4net: Converter [literal] Option [[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [date] Option [yyyy/MM/dd HH:mm:ss,fff] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [level] Option [] Format [min=5,max=2147483647,leftAlign=True] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [thread] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [Layout] to object [log4net.Layout.PatternLayout] +log4net: Setting Property [LevelMin] to Level value [DEBUG] +log4net: Setting Property [LevelMax] to Level value [FATAL] +log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.LevelRangeFilter] +log4net: Created Appender [ACCESS2] +log4net: Adding appender named [ACCESS2] to logger [ACCESS]. +log4net: Retrieving an instance of log4net.Repository.Logger for logger [SQLTRACE]. +log4net: Setting [SQLTRACE] additivity to [True]. +log4net: Logger [SQLTRACE] Level string is [All]. +log4net: Logger [SQLTRACE] level set to [name="ALL",value=-2147483648]. +log4net: Loading Appender [SQLTRACE2] type: [log4net.Appender.ConsoleAppender] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [ConversionPattern] to String value [[%date{yyyy/MM/dd HH:mm:ss,fff}],[%-5level],[%thread],%message%newline] +log4net: Converter [literal] Option [[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [date] Option [yyyy/MM/dd HH:mm:ss,fff] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [level] Option [] Format [min=5,max=2147483647,leftAlign=True] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [thread] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [Layout] to object [log4net.Layout.PatternLayout] +log4net: Setting Property [LevelMin] to Level value [DEBUG] +log4net: Setting Property [LevelMax] to Level value [FATAL] +log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.LevelRangeFilter] +log4net: Created Appender [SQLTRACE2] +log4net: Adding appender named [SQLTRACE2] to logger [SQLTRACE]. +log4net: Retrieving an instance of log4net.Repository.Logger for logger [OPERATION]. +log4net: Setting [OPERATION] additivity to [True]. +log4net: Logger [OPERATION] Level string is [All]. +log4net: Logger [OPERATION] level set to [name="ALL",value=-2147483648]. +log4net: Loading Appender [OPERATION2] type: [log4net.Appender.ConsoleAppender] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [ConversionPattern] to String value [[%date{yyyy/MM/dd HH:mm:ss,fff}],[%-5level],[%thread],%message%newline] +log4net: Converter [literal] Option [[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [date] Option [yyyy/MM/dd HH:mm:ss,fff] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [level] Option [] Format [min=5,max=2147483647,leftAlign=True] +log4net: Converter [literal] Option [],[] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [thread] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [literal] Option [],] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False] +log4net: Setting Property [Layout] to object [log4net.Layout.PatternLayout] +log4net: Setting Property [LevelMin] to Level value [DEBUG] +log4net: Setting Property [LevelMax] to Level value [FATAL] +log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.LevelRangeFilter] +log4net: Created Appender [OPERATION2] +log4net: Adding appender named [OPERATION2] to logger [OPERATION]. +log4net: Hierarchy Threshold [] +LogIF.Is*Enabled - UNDEFINED : Debug True / Info True / Warn True / Error True / Fatal True ---------------------------------------------------------------------------------------------------- GetMessage: ~メッセージID:I0001に対応する記述(正常系)~ GetMessage: ~メッセージID:E0001に対応する記述(異常系)~ @@ -2303,6 +2379,26 @@ GetConfigValue(FxBusinessMessageCulture) : ja-JP EnvInfo.MachineName が取れる : True EnvInfo.OsVersionString が取れる : True EnvInfo.ProcessBit が 32 または 64 : True +GetConfigParameter +GetAnyConfigValue(appSettings:FxBusinessMessageCulture) : ja-JP +GetAnyConfigValue(無いキー) : null または空 +GetAnyConfigSection(appSettings) が取れる : True +GetConnectionString(定義が無い名前。例外にならず空が返る) : null または空 +PubCmnFunction.GetPropsFromPropString + [a=1;b=2;] → 2 件 : A=[1], B=[2] + [UserName=fxuser;Password=fxpass;] → 2 件 : USERNAME=[fxuser], PASSWORD=[fxpass] + [a=1] → 1 件 : A=[1] + [] → 0 件 : + [a=;b=2;] → 2 件 : A=[], B=[2] +PubCmnFunction.BuiltStringIntoEnvironmentVariable + [%OT_TEST_VAR%] → [展開後] + [前%OT_TEST_VAR%後] → [前展開後後] + [C:\%OT_TEST_VAR%\end] → [C:\展開後\end] + [%OT_TEST_NOT_EXIST%] → [] + [変数なし] → [変数なし] +PubCmnFunction.GetCommandArgs + prefix='/' : 名前付き 0 件 / 値のみ 0 件 + prefix='-' : 名前付き 0 件 / 値のみ 0 件 ---------------------------------------------------------------------------------------------------- StringConverter.ToHankaku - アアア: アアア StringConverter.ToZenkaku - アアア: アアア @@ -2583,6 +2679,9 @@ EmbeddedResourceLoader 読み込み : 埋め込まれたリソースの内容 / embedded resource 存在しない : False 存在しない(例外) : System.ArgumentException +LoadAsStream : 埋め込まれたリソースの内容 / embedded resource +LoadXMLAsString : 長さ 292 / ルート要素 bookstore +LoadXMLAsString : XML宣言が残るか True ---------------------------------------------------------------------------------------------------- ZipperV2 → UnZipperV2(往復) 解凍した件数 : 4 @@ -2725,6 +2824,24 @@ DataSet との JSON 往復 行 : Id=3, Note="削除される", RowState=Deleted 行 : Id=4, Note="追加", RowState=Added ---------------------------------------------------------------------------------------------------- +DTTables の中間生成物(JsonTables)との往復 +[中間生成物] 表の数 : 2 + 表名 TestTypes / 列 7 / 行 4 + 列 : Id:Int32, Name:String, Price:Decimal, Rate:Double, Ordered:DateTime, Flag:Boolean, Blob:ByteArray + 表名 TestOdd / 列 2 / 行 1 + 列 : state:String, cels:Int32 +[戻した DTTables] +表名 : TestTypes +列 : Id(Int32), Name(String), Price(Decimal), Rate(Double), Ordered(DateTime), Flag(Boolean), Blob(ByteArray) +行 : Id=1, Name="あいう", Price=1234.56, Rate=0.30000000000000004, Ordered=2026-08-14T12:34:56.7890000, Flag=True, Blob=byte[3]:00-01-FF, RowState=Added +行 : Id=2, Name="", Price=-0.01, Rate=1.7976931348623157E+308, Ordered=1977-04-24T00:00:00.0000000, Flag=False, Blob=byte[0]:, RowState=Added +行 : Id=3, Name="あ\r\nい", Price=0, Rate=0, Ordered=2000-01-01T00:00:00.0000000, Flag=False, Blob=byte[1]:01, RowState=Added +行 : Id=4, Name=(null), Price=(null), Rate=(null), Ordered=(null), Flag=(null), Blob=(null), RowState=Added +表名 : TestOdd +列 : state(String), cels(Int32) +行 : state="わな", cels=7, RowState=Added +[空の DTTables] 列挙の回数 : 0 +---------------------------------------------------------------------------------------------------- ObjectInspector.Inspect : 基本の型 [null] null diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestDto.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestDto.cs index 6f3d29ba3..6646f6d59 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestDto.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestDto.cs @@ -77,6 +77,73 @@ public static void Root() MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); TestDto.TestRowState(); + + MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); + + TestDto.TestDTTablesIntermediate(); + } + + #endregion + + #region 中間生成物と列挙 + + /// 中間生成物との往復と列挙(ToJsonObject、FromJsonObject、GetEnumerator) + /// + /// **JSON 文字列を経由しない経路。** + /// `DTTablesToJson` / `JsonToDTTables` は内部でこれを使うが、 + /// **中間生成物(JsonTables)を直接触る口も公開されている。** + /// 文字列を挟まないので、シリアライザの都合と切り分けて確かめられる。 + /// + private static void TestDTTablesIntermediate() + { + MyDebug.OutputDebugAndConsole("DTTables の中間生成物(JsonTables)との往復"); + + DTTables dtts = new DTTables(); + dtts.Add(DTTable.FromDataTable(TestDto.MakeTypedTable())); + dtts.Add(DTTable.FromDataTable(TestDto.MakeOddTable())); + + // 中間生成物へ + DTTables.JsonTables jTbls = dtts.ToJsonObject(); + + MyDebug.OutputDebugAndConsole( + "[中間生成物] 表の数 : " + jTbls.tbls.Count.ToString()); + + foreach (DTTables.JsonTable jTbl in jTbls.tbls) + { + MyDebug.OutputDebugAndConsole( + " 表名 " + jTbl.tbl + " / 列 " + jTbl.cols.Count.ToString() + + " / 行 " + jTbl.rows.Count.ToString()); + + // **列は順序に意味がある。** 名前と型を順に出す。 + string cols = ""; + foreach (DTTables.JsonColumn c in jTbl.cols) + { + cols += (cols == "" ? "" : ", ") + c.name + ":" + c.type; + } + MyDebug.OutputDebugAndConsole(" 列 : " + cols); + } + + // 中間生成物から戻す + DTTables restored = new DTTables(); + restored.FromJsonObject(jTbls); + + MyDebug.OutputDebugAndConsole("[戻した DTTables]"); + foreach (DTTable dtt in restored) + { + // **foreach が回るかを見ている。** これが GetEnumerator の確認になる。 + // + // **OutputDTTable は使えない。** あちらは row["OrderID"] のように + // 列名を決め打ちしており、別の表を渡すと例外になる。 + // 列を走査する OutputDTTableAll を使う。 + TestDto.OutputDTTableAll(dtt); + } + + // **空でも列挙できるか。** 1 度も回らずに抜けること。 + DTTables empty = new DTTables(); + int count = 0; + foreach (DTTable dtt in empty) { count++; } + + MyDebug.OutputDebugAndConsole("[空の DTTables] 列挙の回数 : " + count.ToString()); } #endregion diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestOutputLog.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestOutputLog.cs index 8b5b950ac..6c0b66d03 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestOutputLog.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestOutputLog.cs @@ -47,7 +47,33 @@ public static void Root() LogIF.WarnLog("ACCESS", "LogIF.WarnLog(\"ACCESS\");"); LogIF.ErrorLog("ACCESS", "LogIF.ErrorLog(\"ACCESS\");"); LogIF.FatalLog("ACCESS", "LogIF.FatalLog(\"ACCESS\");"); + + TestOutputLog.IsEnabledTest(); } #endregion + + #region private + + /// ログ レベルの有効・無効(IsDebugEnabled ほか) + /// + /// **ロガー名ごとに設定が違う。** + /// SampleLogConf.xml で ACCESS は ALL、他は既定になっているので、 + /// **設定に無いロガー名も渡して差を見る。** + /// + private static void IsEnabledTest() + { + foreach (string logger in new string[] { "ACCESS", "UNDEFINED" }) + { + MyDebug.OutputDebugAndConsole( + "LogIF.Is*Enabled - " + logger + " : " + + "Debug " + LogIF.IsDebugEnabled(logger) + + " / Info " + LogIF.IsInfoEnabled(logger) + + " / Warn " + LogIF.IsWarnEnabled(logger) + + " / Error " + LogIF.IsErrorEnabled(logger) + + " / Fatal " + LogIF.IsFatalEnabled(logger)); + } + } + + #endregion } } \ No newline at end of file diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestResourceLoader.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestResourceLoader.cs index a21583891..aeb226e15 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestResourceLoader.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestResourceLoader.cs @@ -192,6 +192,35 @@ private static void TestEmbedded() { MyDebug.OutputDebugAndConsole("存在しない(例外) : " + ex.GetType().FullName); } + + // **ストリームで読む。** LoadAsString と同じ中身が返るはず。 + using (Stream st = EmbeddedResourceLoader.LoadAsStream(TestResourceLoader.EmbeddedName)) + { + if (st == null) + { + MyDebug.OutputDebugAndConsole("LoadAsStream : (null)"); + } + else + { + using (StreamReader sr = new StreamReader(st, Encoding.UTF8)) + { + MyDebug.OutputDebugAndConsole("LoadAsStream : " + sr.ReadToEnd().Trim()); + } + } + } + + // **XML として読む。** 埋め込みの TestXml.xml は OpenTouryo.Public 側にある。 + string xml = EmbeddedResourceLoader.LoadXMLAsString( + "OpenTouryo.Public", "Touryo.Infrastructure.Public.Xml.TestXml.xml"); + + MyDebug.OutputDebugAndConsole( + "LoadXMLAsString : 長さ " + xml.Length.ToString() + + " / ルート要素 " + (xml.Contains("設定の取得(GetAnyConfigValue、GetAnyConfigSection、GetConnectionString)
+ /// + /// **InitConfiguration は呼ばない。** + /// 静的な構成を差し替える副作用があり、後続のテストを巻き込む。 + /// (TestCode の Program.Main が起動時に 1 度だけ呼んでいる) + /// + private static void TestGetConfigParameter() + { + MyDebug.OutputDebugAndConsole("GetConfigParameter"); + +#if (NETSTD || NETCOREAPP) + // **GetAnyConfigValue / GetAnyConfigSection は .NET Core 側にしか無い。** + // net48 では appSettings 以外のセクションを引く口が無いため、 + // 条件コンパイルで分けられている。出力も net48 と core で変わる。 + + // appSettings 配下のキー(GetConfigValue と同じ値になるはず) + MyDebug.OutputDebugAndConsole( + "GetAnyConfigValue(appSettings:FxBusinessMessageCulture) : " + + GetConfigParameter.GetAnyConfigValue("appSettings:FxBusinessMessageCulture")); + + // 無いキー + string v = GetConfigParameter.GetAnyConfigValue("appSettings:OpenTouryo_NotExistKey"); + MyDebug.OutputDebugAndConsole( + "GetAnyConfigValue(無いキー) : " + (string.IsNullOrEmpty(v) ? "null または空" : "値あり")); + + // **セクションそのものが取れるか。** 中身は環境で変わりうるので、取得の可否だけを見る。 + MyDebug.OutputDebugAndConsole( + "GetAnyConfigSection(appSettings) が取れる : " + + (GetConfigParameter.GetAnyConfigSection("appSettings") != null)); +#else + MyDebug.OutputDebugAndConsole( + "GetAnyConfigValue / GetAnyConfigSection : .NET Core 側のみのため対象外"); +#endif + + // **TestCode の設定には connectionStrings が無い。** + // よって「取れない」側しか通せない。値そのものは、あっても出さない + // (サーバ名などが環境で変わり、Result*.txt と突き合わせられなくなる)。 + // 接続文字列を実際に使う経路は TestDataAccess が見ている。 + MyDebug.OutputDebugAndConsole( + "GetConnectionString(定義が無い名前。例外にならず空が返る) : " + + (string.IsNullOrEmpty(GetConfigParameter.GetConnectionString("OpenTouryo_NotExist")) + ? "null または空" : "値あり")); + } + + /// プロパティ文字列と環境変数(GetPropsFromPropString、BuiltStringIntoEnvironmentVariable) + private static void TestPropStringAndEnvVariable() + { + MyDebug.OutputDebugAndConsole("PubCmnFunction.GetPropsFromPropString"); + + string[] props = new string[] + { + "a=1;b=2;", + "UserName=fxuser;Password=fxpass;", + "a=1", // 末尾の ; が無い + "", // 空文字列 + "a=;b=2;" // 値が空 + }; + + foreach (string p in props) + { + Dictionary dic = PubCmnFunction.GetPropsFromPropString(p); + + string ret = ""; + foreach (string k in dic.Keys) + { + ret += (ret == "" ? "" : ", ") + k + "=[" + dic[k] + "]"; + } + + MyDebug.OutputDebugAndConsole(" [" + p + "] → " + dic.Count.ToString() + " 件 : " + ret); + } + + MyDebug.OutputDebugAndConsole("PubCmnFunction.BuiltStringIntoEnvironmentVariable"); + + // **テストの中で環境変数を設定してから呼ぶ。** + // 既存の環境変数を使うと、実行環境で結果が変わってしまう。 + Environment.SetEnvironmentVariable("OT_TEST_VAR", "展開後"); + + string[] inputs = new string[] + { + @"%OT_TEST_VAR%", + @"前%OT_TEST_VAR%後", + @"C:\%OT_TEST_VAR%\end", + @"%OT_TEST_NOT_EXIST%", // 無い変数 + @"変数なし" + }; + + foreach (string s in inputs) + { + MyDebug.OutputDebugAndConsole( + " [" + s + "] → [" + PubCmnFunction.BuiltStringIntoEnvironmentVariable(s) + "]"); + } + } + + /// コマンドライン引数の解析(GetCommandArgs) + /// + /// **このテストは引数なしで起動される**ため、結果は空になる。 + /// 値そのものより「例外なく呼べて、空が返る」ことを見る。 + /// + private static void TestGetCommandArgs() + { + MyDebug.OutputDebugAndConsole("PubCmnFunction.GetCommandArgs"); + + Dictionary argsDic; + List valsLst; + + PubCmnFunction.GetCommandArgs('/', out argsDic, out valsLst); + + MyDebug.OutputDebugAndConsole( + " prefix='/' : 名前付き " + argsDic.Count.ToString() + + " 件 / 値のみ " + valsLst.Count.ToString() + " 件"); + + StringVariableOperator.GetCommandArgs('-', out argsDic, out valsLst); + + MyDebug.OutputDebugAndConsole( + " prefix='-' : 名前付き " + argsDic.Count.ToString() + + " 件 / 値のみ " + valsLst.Count.ToString() + " 件"); } #endregion From 9fc2fc40f117d80d3211f700f5fc985ec9375bd4 Mon Sep 17 00:00:00 2001 From: nishi_74322014 Date: Mon, 17 Aug 2026 23:32:18 +0900 Subject: [PATCH 4/7] =?UTF-8?q?=E6=BC=8F=E3=82=8C=20#552?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Public/Str/StringChecker.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Str/StringChecker.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Str/StringChecker.cs index f8dd287ea..6dffb5bc6 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Str/StringChecker.cs +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Str/StringChecker.cs @@ -38,6 +38,8 @@ //* 2013/08/30 西野 大介 IsInCodePageメソッドを追加 //* 2018/03/28 西野 大介 .NET Standard対応で、Microsoft.VisualBasicのサポート無し。 //* 2019/10/28 西野 大介 VisualBasic → Zipanguで、IsNumericを復帰 +//* 2026/08/17 玄人 幸道 IsNumericとIsNumbersで、空文字列の結果が逆になる +//* 理由をコメントに追記(#552)。挙動は変えていない。 //********************************************************************************** using System.Text; @@ -70,6 +72,15 @@ public class StringChecker /// double.TryParseでの実装。 /// http://support.microsoft.com/kb/329488/ja /// 全角文字もチェック可能(半角変換後にチェック)。 + /// + /// 空文字列が指定された場合は、falseが返ります。 + /// 「入力が単体の"数値(Number)"として評価・変換できるか」を見るメソッドであり、 + /// 空文字列は数値として評価できないためです。 + /// + /// IsNumbersとは、空文字列に対する結果が逆になります(あちらはtrue)。 + /// あちらは「すべての文字が"数字(Digits)"か」を見るので、 + /// 文字が1つも無ければ条件に反する文字も無く、trueになります。 + /// 名前が似ていますが問いが違うので、用途に合う方を選んでください。 /// public static bool IsNumeric(string input) { @@ -94,7 +105,15 @@ public static bool IsNumeric(string input) /// /// 数値チェックという意味ではParse、TryParse /// メソッドを使用すべきかどうかも検討下さい。 + /// /// 空文字列が指定された場合は、trueが返ります。 + /// 「すべての文字が"数字(Digits)"か」を見るメソッドなので、 + /// 文字が1つも無ければ条件に反する文字も無く、trueになるためです。 + /// (連続一致が0回以上(*)なのも、必須入力チェックと被らせないためです) + /// + /// IsNumericとは、空文字列に対する結果が逆になります(あちらはfalse)。 + /// あちらは「単体の"数値(Number)"として評価・変換できるか」を見ます。 + /// 名前が似ていますが問いが違うので、用途に合う方を選んでください。 /// public static bool IsNumbers(string input) { From bef5bc2bade3cf5aa2e1f4ed15103bb4f42be972 Mon Sep 17 00:00:00 2001 From: nishi_74322014 Date: Tue, 18 Aug 2026 00:32:06 +0900 Subject: [PATCH 5/7] fixed #562 --- root/programs/2_RunAllTests.ps1 | 40 ++++++++++++------- root/programs/CompareResult.ps1 | 69 ++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 17 deletions(-) diff --git a/root/programs/2_RunAllTests.ps1 b/root/programs/2_RunAllTests.ps1 index 429ecb4b0..cd27a4f1b 100644 --- a/root/programs/2_RunAllTests.ps1 +++ b/root/programs/2_RunAllTests.ps1 @@ -53,6 +53,7 @@ 日時 更新者 内容 ---------- ---------------- ------------------------------------------------- 2026/08/01 玄人 幸道 新規作成(リリース ワークのエージェント化) + 2026/08/17 玄人 幸道 テストごとに Base64 の正規化を選べるようにした(#562) #> [CmdletBinding()] param( @@ -166,38 +167,42 @@ function Copy-TestCertificates # = 期待値でもある(HEAD 版と比較する) # Bat : ビルドとテスト実行を行うバッチ(root\programs\CS 配下) # SkipLog4net : log4net の内部トレースを比較対象から外すか +# NormBase64 : Base64 らしき長い値を伏せるか(#562) +# **伏せると、その範囲の変化は差分に出ない。** +# 識別子(IsHankaku/IsZenkaku など)にも当たるため、 +# **実行のたびに値が変わるテストだけ $true にする。** $tests = @( @{ Name = "TestCode (net48)"; Bat = "y_Build_TestCode_Public.bat" - Result = "TestCode\Result48.txt"; SkipLog4net = $false + Result = "TestCode\Result48.txt"; SkipLog4net = $false; NormBase64 = $false } @{ Name = "TestCode (net10.0)"; Bat = "y_Build_TestCode_Public.bat" - Result = "TestCode\ResultCore100.txt"; SkipLog4net = $false + Result = "TestCode\ResultCore100.txt"; SkipLog4net = $false; NormBase64 = $false } @{ Name = "TestDataAccess (net48)"; Bat = "y_Build_TestCode_DataAccess.bat" - Result = "TestDataAccess\Result48.txt"; SkipLog4net = $false + Result = "TestDataAccess\Result48.txt"; SkipLog4net = $false; NormBase64 = $false } @{ Name = "TestDataAccess (net10.0)"; Bat = "y_Build_TestCode_DataAccess.bat" - Result = "TestDataAccess\ResultCore100.txt"; SkipLog4net = $false + Result = "TestDataAccess\ResultCore100.txt"; SkipLog4net = $false; NormBase64 = $false } @{ Name = "SimpleBatch (net48)"; Bat = "y_Build_TestCode_Batch.bat" - Result = "TestBatch\ResultSimpleBatch48.txt"; SkipLog4net = $true + Result = "TestBatch\ResultSimpleBatch48.txt"; SkipLog4net = $true; NormBase64 = $false } @{ Name = "SimpleBatch (net10.0)"; Bat = "y_Build_TestCode_Batch.bat" - Result = "TestBatch\ResultSimpleBatchCore100.txt"; SkipLog4net = $true + Result = "TestBatch\ResultSimpleBatchCore100.txt"; SkipLog4net = $true; NormBase64 = $false } @{ Name = "EncAndDecUtilCUI (net48)"; Bat = "y_Build_TestCode_SecCUI.bat" - Result = "EncAndDecUtilCUI\Result48.txt"; SkipLog4net = $false + Result = "EncAndDecUtilCUI\Result48.txt"; SkipLog4net = $false; NormBase64 = $true } @{ Name = "EncAndDecUtilCUI (net10.0)"; Bat = "y_Build_TestCode_SecCUI.bat" - Result = "EncAndDecUtilCUI\ResultCore100.txt"; SkipLog4net = $false + Result = "EncAndDecUtilCUI\ResultCore100.txt"; SkipLog4net = $false; NormBase64 = $true } ) @@ -296,15 +301,20 @@ foreach ($t in $tests) # CompareResult.ps1 は画面表示に Write-Host を使うため、 # 件数は -PassThru のオブジェクトで受け取る。 - if ($t.SkipLog4net) - { - $cmpResult = & $cmp -Expected $expectedOf[$t.Name] -Actual $actual -SkipLog4netTrace -PassThru - } - else - { - $cmpResult = & $cmp -Expected $expectedOf[$t.Name] -Actual $actual -PassThru + # + # **スイッチはスプラッティングで渡す。**(#562) + # if で組み合わせを書き分けると、スイッチが増えるたびに分岐が倍になる。 + $cmpArgs = @{ + Expected = $expectedOf[$t.Name] + Actual = $actual + PassThru = $true } + if ($t.SkipLog4net) { $cmpArgs["SkipLog4netTrace"] = $true } + if ($t.NormBase64) { $cmpArgs["NormalizeBase64"] = $true } + + $cmpResult = & $cmp @cmpArgs + $results += [pscustomobject]@{ テスト = $t.Name 結果 = $cmpResult.Result diff --git a/root/programs/CompareResult.ps1 b/root/programs/CompareResult.ps1 index 07b73ee7b..7ac177c31 100644 --- a/root/programs/CompareResult.ps1 +++ b/root/programs/CompareResult.ps1 @@ -24,6 +24,26 @@ log4net の内部トレース("log4net: " で始まる行)を比較対象から除外する。 設定内容の確認が目的でなければ、除外した方が判定が読みやすい。 +.PARAMETER NormalizeBase64 + Base64 らしき長い文字列(16 文字以上の英数字の並び)を伏せる。**既定は off。** + + <伏せると退行を隠す> + + パターンは識別子にも当たる。実際、次のように伏せられていた。 + + StringChecker.IsHankaku/IsZenkaku → StringChecker. + FormatConverter.AddZerosAfterDecimal → FormatConverter. + -------------------------------------------------- → + + **伏せた範囲の変化は、差分として現れない。** + + <それでも必要なテストがある> + + EncAndDecUtilCUI は鍵・署名・IV を出力し、**実行のたびに値が変わる。** + しかも 32 文字未満のものが 94 種あるため、閾値を上げても避けられない。 + + **だから、要るテストだけ on にする。**(#562) + .PARAMETER ShowAll 差分の全件を表示する(既定は先頭 20 件)。 @@ -43,12 +63,18 @@ 2026/08/05 玄人 幸道 OSメッセージの正規化を追加(CI の英語環境への対応) 2026/08/17 玄人 幸道 -SyncWindow 20 をやめた(#552)。20 行を超える                 挿入で再同期できず、以降が全部差分になっていた。 + 2026/08/17 玄人 幸道 Base64の正規化を -NormalizeBase64 で選べるようにし、 +                 既定を off にした(#562)。識別子まで伏せていた。 #> [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$Expected, [Parameter(Mandatory = $true)][string]$Actual, [switch]$SkipLog4netTrace, + # Base64 らしき長い文字列を伏せる(既定は off)。 + # **伏せると、その範囲の変化は差分として現れない。** + # 実行のたびに変わる値(鍵・署名・IV 等)が出るテストでだけ指定すること。 + [switch]$NormalizeBase64, [switch]$ShowAll, # 判定結果をオブジェクトとして出力する(呼び出し元での集計用)。 # 画面表示は Write-Host で行っておりパイプラインに乗らないため、 @@ -88,12 +114,40 @@ $normalizers = @( # ※ 値そのものは Base64 だが、実行のたびに変わるうえ改行を含まないため # 汎用の Base64 パターンより先に処理する。 @{ Name = 'XML署名値'; Pattern = '<(SignatureValue|DigestValue|Modulus|Exponent)>[^<]*'; Replace = '<$1>' } + # スレッド ID(実行ごとに変わり得る) + @{ Name = 'スレッドID'; Pattern = '(?<=\],)\[\d+\](?=,)'; Replace = '[]' } +) + +# ------------------------------------------------------------------ +# Base64 の正規化(-NormalizeBase64 のときだけ使う) +# ------------------------------------------------------------------ +# **既定では使わない。**(#562) +# +# <なぜ既定を off にしたか> +# +# パターンは「16 文字以上の英数字の並び」で、**識別子にも当たる。** +# StringChecker.IsHankaku/IsZenkaku → StringChecker. +# FormatConverter.AddZerosAfterDecimal → FormatConverter. +# -------------------------------------------------- (区切り線)→ +# +# 伏せた範囲の変化は**差分として現れない。** +# つまり **退行を隠す。** +# +# <閾値では直せない> +# +# EncAndDecUtilCUI には **32 文字未満の本物の Base64 が 94 種**ある +# (163HYybmVTXys2wJbcU など)。実行のたびに変わるので、伏せないと常に NG になる。 +# 16 という閾値は、そのために要る。 +# +# <だから、要るテストだけ on にする> +# +# 伏せるのは「毎回変わる値が出る」という**例外的な事情がある場合だけ**でよい。 +# TestCode では 16 文字以上の一致 206 件のうち、**伏せる必要のあるものが無い。** +$base64Normalizers = @( # Base64URL の長い値(JWT・署名・鍵・IV・認証タグ等) @{ Name = 'Base64URL'; Pattern = '[A-Za-z0-9_\-]{16,}'; Replace = '' } # Base64 の長い値(鍵・ハッシュ等) @{ Name = 'Base64'; Pattern = '[A-Za-z0-9+/]{16,}={0,2}'; Replace = '' } - # スレッド ID(実行ごとに変わり得る) - @{ Name = 'スレッドID'; Pattern = '(?<=\],)\[\d+\](?=,)'; Replace = '[]' } ) function Normalize([string[]]$lines) @@ -132,6 +186,17 @@ function Normalize([string[]]$lines) $s = [regex]::Replace($s, $n.Pattern, $n.Replace) } + # **Base64 は最後に処理する。**(#562) + # 「XML署名値」が要素ごと潰したあとでないと、 + # 中身の Base64 が先に伏せられて要素の対応が取れなくなる。 + if ($NormalizeBase64) + { + foreach ($n in $base64Normalizers) + { + $s = [regex]::Replace($s, $n.Pattern, $n.Replace) + } + } + $result.Add($s.TrimEnd()) } From ab8167f82eb28d93431c252723de8c7964eae1ee Mon Sep 17 00:00:00 2001 From: nishi_74322014 Date: Tue, 18 Aug 2026 01:21:56 +0900 Subject: [PATCH 6/7] fixed #564 --- root/programs/2_RunAllTests.ps1 | 79 ++++++++++++++- .../CS/Frameworks/Tests/TestCode/Program.cs | 97 +++++++++++-------- 2 files changed, 135 insertions(+), 41 deletions(-) diff --git a/root/programs/2_RunAllTests.ps1 b/root/programs/2_RunAllTests.ps1 index cd27a4f1b..65781fafc 100644 --- a/root/programs/2_RunAllTests.ps1 +++ b/root/programs/2_RunAllTests.ps1 @@ -246,6 +246,11 @@ Write-Host (" 期待値(HEAD 版)を {0} 件退避しました。" -f $expe # ------------------------------------------------------------------ # 1 本のバッチが net48 版と .NET (Core) 版の両方をビルド・実行するため、 # 同一バッチは 1 回だけ実行する。 +# **ビルドが失敗したバッチ名を覚えておく。**(#564) +# 失敗しても前回の実行ファイルが動くため、 +# 結果は期待値と一致し「OK」になってしまう。 +$buildFailed = @{} + if (-not $SkipBuild) { foreach ($bat in ($tests | ForEach-Object { $_.Bat } | Select-Object -Unique)) @@ -257,6 +262,7 @@ if (-not $SkipBuild) if (-not (Test-Path $path)) { Write-Host " バッチが見つかりません" -ForegroundColor Red + $buildFailed[$bat] = @("バッチが見つかりません : $path") continue } @@ -269,7 +275,48 @@ if (-not $SkipBuild) Pop-Location $sw.Stop() - Write-Host (" 完了 ({0:N1} 秒) ログ : {1}" -f $sw.Elapsed.TotalSeconds, $log) + + # ------------------------------------------------------------------ + # **ログからビルド エラーを拾う。**(#564) + # ------------------------------------------------------------------ + # <終了コードを見ない理由> + # バッチは msbuild を 2 回、テストの実行を 2 回、最後に pause を呼ぶ。 + # $LASTEXITCODE は**最後のものしか返らない**ので、 + # ビルドの成否を表さない。 + # + # <1_BuildAll.ps1 と同じ見方にする> + # コード部分はロケールによらないため、これを拾う。 + # "ビルドに失敗しました" 等のサマリ文言は日本語環境でしか出ない。 + # コードを伴わない「: error :」形式(NuGet の restore 失敗)もあるため、 + # コード部分は省略可能として扱う。 + $buildErrors = @( + Get-Content $log -Encoding UTF8 -EA SilentlyContinue | + Where-Object { $_ -match ':\s*error(\s+[A-Za-z]+\d+)?\s*:' -or $_ -match '^\s*\[ERROR\]' } | + ForEach-Object { $_.Trim() } | + Select-Object -Unique) + + if ($buildErrors.Count -eq 0) + { + Write-Host (" 完了 ({0:N1} 秒) ログ : {1}" -f $sw.Elapsed.TotalSeconds, $log) + } + else + { + $buildFailed[$bat] = $buildErrors + + Write-Host (" **ビルド エラー {0} 件** ({1:N1} 秒) ログ : {2}" -f ` + $buildErrors.Count, $sw.Elapsed.TotalSeconds, $log) -ForegroundColor Red + + foreach ($e in ($buildErrors | Select-Object -First 5)) + { + Write-Host (" " + $e) -ForegroundColor Red + } + if ($buildErrors.Count -gt 5) + { + Write-Host (" ... 他 {0} 件(ログを参照)" -f ($buildErrors.Count - 5)) -ForegroundColor Red + } + + Write-Host " **前回の実行ファイルが動くため、比較は当てになりません。**" -ForegroundColor Red + } } } @@ -286,6 +333,20 @@ foreach ($t in $tests) $actual = Join-Path $testsRoot $t.Result + # **ビルドが失敗していたら、比較しても意味が無い。**(#564) + # 前回の実行ファイルが動いているため、結果は期待値と一致し「OK」になる。 + # 比較を省いて、ビルド失敗として報告する。 + if ($buildFailed.ContainsKey($t.Bat)) + { + Write-Host " **ビルドに失敗しているため、比較しません。**" -ForegroundColor Red + Write-Host (" ({0} のエラー {1} 件)" -f $t.Bat, $buildFailed[$t.Bat].Count) -ForegroundColor Red + + $results += [pscustomobject]@{ + テスト = $t.Name; 結果 = "ビルド失敗"; 差分 = "-" + } + continue + } + if (-not $expectedOf.ContainsKey($t.Name)) { Write-Host " 期待値(HEAD 版)が無いため比較できません" -ForegroundColor Red @@ -333,6 +394,22 @@ Write-Host "" Write-Host (" 期待値・ログ : {0}" -f $OutputDir) Write-Host " 実測値 : ワーキング ツリーの Result*.txt(git diff で確認できます)" +# **ビルド失敗は、差分より先に伝える。**(#564) +# 放っておくと前回の実行ファイルの結果を見ることになり、 +# 「OK」の意味が変わってしまう。 +if ($buildFailed.Count -gt 0) +{ + Write-Host "" + Write-Host (" **ビルドに失敗したバッチが {0} 本あります。**" -f $buildFailed.Count) -ForegroundColor Red + + foreach ($bat in ($buildFailed.Keys | Sort-Object)) + { + Write-Host (" {0} : エラー {1} 件" -f $bat, $buildFailed[$bat].Count) -ForegroundColor Red + } + + Write-Host " **直さない限り、テストは前回の実行ファイルで動きます。**" -ForegroundColor Red +} + $ng = @($results | Where-Object { $_.結果 -ne "OK" }) if ($ng.Count -eq 0) { diff --git a/root/programs/CS/Frameworks/Tests/TestCode/Program.cs b/root/programs/CS/Frameworks/Tests/TestCode/Program.cs index c9e9c3298..1008e51e6 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/Program.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/Program.cs @@ -24,6 +24,8 @@ //* 日時 更新者 内容 //* ---------- ---------------- ------------------------------------------------- //* 2019/02/06 西野 大介 新規作成 +//* 2026/08/18 玄人 幸道 各テストを個別に try で囲むようにした(#564)。 +//* 1 つが例外を投げると、以降が実行されなかった。 //********************************************************************************** using System; @@ -56,69 +58,49 @@ public static void Main(string[] args) { #region Public #region Basic - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestOutputLog.Root(); + Program.Run("TestOutputLog", TestOutputLog.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestGetMessageAndProperty.Root(); + Program.Run("TestGetMessageAndProperty", TestGetMessageAndProperty.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestStringChecker.Root(); + Program.Run("TestStringChecker", TestStringChecker.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestFormatChecker.Root(); + Program.Run("TestFormatChecker", TestFormatChecker.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestStringVariableOperator.Root(); + Program.Run("TestStringVariableOperator", TestStringVariableOperator.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestStringExtractor.Root(); + Program.Run("TestStringExtractor", TestStringExtractor.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestUtil.Root(); + Program.Run("TestUtil", TestUtil.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestStringConverter.Root(); + Program.Run("TestStringConverter", TestStringConverter.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestFormatConverter.Root(); + Program.Run("TestFormatConverter", TestFormatConverter.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestCustomEncode.Root(); + Program.Run("TestCustomEncode", TestCustomEncode.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - JISCode.Root(); + Program.Run("JISCode", JISCode.Root); #endregion #region Extension - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestEnumToStringExtensions.Root(); + Program.Run("TestEnumToStringExtensions", TestEnumToStringExtensions.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestXmlLib.Root(); + Program.Run("TestXmlLib", TestXmlLib.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestDeflateCompression.Root(); + Program.Run("TestDeflateCompression", TestDeflateCompression.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestResourceLoader.Root(); + Program.Run("TestResourceLoader", TestResourceLoader.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestZipV2.Root(); + Program.Run("TestZipV2", TestZipV2.Root); #endregion #region Dto - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestDto.Root(); + Program.Run("TestDto", TestDto.Root); #endregion #region Diagnostics - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestObjectInspector.Root(); + Program.Run("TestObjectInspector", TestObjectInspector.Root); #endregion #region Reflection - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestLatebind.Root(); + Program.Run("TestLatebind", TestLatebind.Root); - MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); - TestFastReflection.Root(); + Program.Run("TestFastReflection", TestFastReflection.Root); #endregion // Db は TestDataAccess へ移した(#520)。 // DB に接続するテストと前提が異なるため、プロジェクトを分けている。 @@ -144,5 +126,40 @@ public static void Main(string[] args) MyDebug.OutputDebugAndConsole(ex.ToString()); } } + + /// 1 つのテストを実行する + /// テスト名 + /// テストの入口(Root) + /// + /// **1 つ壊れても、残りは走らせる。**(#564) + /// + /// 以前は全テストを 1 つの try で囲んでいたため、 + /// **どれか 1 つが例外を投げると、以降が丸ごと実行されなかった。** + /// その分の網羅がまとめて失われるうえ、結果ファイルの欠け方から + /// 「どこで止まったか」を推測するしかなかった。 + /// + /// <スタック トレースを出さない> + /// + /// 結果は Result*.txt と突き合わせるため、**環境で変わる値を出せない。** + /// スタック トレースには**ファイル パスと行番号**が入る。 + /// 型名とメッセージだけなら、どのテストで何が起きたかは分かる。 + /// + /// 詳しく見たいときは、デバッガか、このメソッドを一時的に外して実行する。 + /// + private static void Run(string name, Action test) + { + MyDebug.OutputDebugAndConsole("----------------------------------------------------------------------------------------------------"); + + try + { + test(); + } + catch (Exception ex) + { + // **型名とメッセージだけ。** スタック トレースは環境依存になる。 + MyDebug.OutputDebugAndConsole( + "[!] " + name + " で例外 : " + ex.GetType().FullName + " : " + ex.Message); + } + } } } From 8b7686754416ca35a1034d08a6a2c4b714942325 Mon Sep 17 00:00:00 2001 From: nishi_74322014 Date: Tue, 18 Aug 2026 02:40:58 +0900 Subject: [PATCH 7/7] fixed #563 --- .../Infrastructure/Public/Xml/XmlLib.cs | 73 +++++++++++++++---- .../CS/Frameworks/Tests/TestCode/Result48.txt | 17 +++-- .../Tests/TestCode/ResultCore100.txt | 17 +++-- .../Frameworks/Tests/TestCode/TestXmlLib.cs | 22 +++++- 4 files changed, 101 insertions(+), 28 deletions(-) diff --git a/root/programs/CS/Frameworks/Infrastructure/Public/Xml/XmlLib.cs b/root/programs/CS/Frameworks/Infrastructure/Public/Xml/XmlLib.cs index 513dd87e6..cc5f620df 100644 --- a/root/programs/CS/Frameworks/Infrastructure/Public/Xml/XmlLib.cs +++ b/root/programs/CS/Frameworks/Infrastructure/Public/Xml/XmlLib.cs @@ -28,6 +28,10 @@ //* 日時 更新者 内容 //* ---------- ---------------- ------------------------------------------------- //* 2019/05/29 西野 大介 新規作成(分割 +//* 2026/08/18 玄人 幸道 index引数が使われていなかったので実装した(#563)。 +//* GetAttributeFromXmlNodeのindexは、属性名が一意で +//* 意味を持たないため、引数なしのオーバーロードを +//* 追加し、引数ありをObsoleteとした。 //********************************************************************************** using System; @@ -53,35 +57,52 @@ public static class XmlLib /// string /// string /// XmlNamespaceManager - /// string + /// + /// XPath に一致するノードのうち、何番目を見るか(0 始まり、既定は 0) + /// /// attr string + /// + /// 見つからない場合は空文字列を返します(例外にはなりません)。 + /// index が一致件数の範囲外の場合も同様です。 + /// + /// index を効かせるため SelectNodes を使っていますが、 + /// index が 0(既定)のときの結果は SelectSingleNode と同じです。 + /// public static string GetAttributeByXPath( XmlDocument xmlDoc, string xPath, string attrName, XmlNamespaceManager xmlnsManager = null, int index = 0) { - return XmlLib.GetAttributeFromXmlNode( - xmlDoc.SelectSingleNode(xPath, xmlnsManager), attrName, index); + XmlNodeList xmlNodeList = xmlDoc.SelectNodes(xPath, xmlnsManager); + + // 範囲外は「見つからない」として扱う(例外にしない)。 + if (xmlNodeList != null && 0 <= index && index < xmlNodeList.Count) + { + return XmlLib.GetAttributeFromXmlNode(xmlNodeList[index], attrName); + } + + return ""; } /// GetAttributeByTagName /// XmlDocument /// string /// string - /// string + /// + /// タグ名が一致する要素のうち、何番目を見るか(0 始まり、既定は 0) + /// /// attr string + /// + /// 見つからない場合は空文字列を返します(例外にはなりません)。 + /// index が要素数の範囲外の場合も同様です。 + /// public static string GetAttributeByTagName(XmlDocument xmlDoc, string tagName, string attrName, int index = 0) { XmlNodeList xmlNodeList = xmlDoc.GetElementsByTagName(tagName); - if (xmlNodeList.Count != 0) + // 範囲外は「見つからない」として扱う(例外にしない)。 + if (0 <= index && index < xmlNodeList.Count) { - if (xmlNodeList[0].Attributes != null) - { - if (xmlNodeList[0].Attributes[attrName] != null) - { - return xmlNodeList[0].Attributes[attrName].Value; - } - } + return XmlLib.GetAttributeFromXmlNode(xmlNodeList[index], attrName); } return ""; @@ -127,10 +148,34 @@ public static XmlNode GetXmlNodeById(XmlDocument xmlDoc, string referenceId) /// GetAttributeFromXmlNode /// XmlNode /// string - /// int + /// 使用しません。 /// attrValue + /// + /// <index を使わない理由> + /// 1つの XmlNode の中で属性名は一意なので、 + /// 「何番目の属性か」という指定が成り立ちません。 + /// 「何番目のノードか」を指定したい場合は、 + /// GetAttributeByTagName / GetAttributeByXPath の index を使ってください。 + /// + /// 長らく未使用のまま公開されていたため、 + /// 引数の無いオーバーロードへ移行してください(#563)。 + /// + [Obsolete("This method is deprecated, please use another overload instead.")] + public static string GetAttributeFromXmlNode( + XmlNode xmlNode, string attrName, int index) + { + return XmlLib.GetAttributeFromXmlNode(xmlNode, attrName); + } + + /// GetAttributeFromXmlNode + /// XmlNode + /// string + /// attrValue + /// + /// 見つからない場合は空文字列を返します(xmlNode が null でも例外になりません)。 + /// public static string GetAttributeFromXmlNode( - XmlNode xmlNode, string attrName, int index = 0) + XmlNode xmlNode, string attrName) { if (xmlNode != null) { diff --git a/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt b/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt index cd333fd4b..fa3664aa8 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt +++ b/root/programs/CS/Frameworks/Tests/TestCode/Result48.txt @@ -70,11 +70,11 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level log4net: Created Appender [OPERATION2] log4net: Adding appender named [OPERATION2] to logger [OPERATION]. log4net: Hierarchy Threshold [] -[2026/08/17 22:30:31,294],[DEBUG],[1],LogIF.DebugLog("ACCESS"); -[2026/08/17 22:30:31,314],[INFO ],[1],LogIF.InfoLog("ACCESS"); -[2026/08/17 22:30:31,315],[WARN ],[1],LogIF.WarnLog("ACCESS"); -[2026/08/17 22:30:31,316],[ERROR],[1],LogIF.ErrorLog("ACCESS"); -[2026/08/17 22:30:31,317],[FATAL],[1],LogIF.FatalLog("ACCESS"); +[2026/08/18 02:24:36,941],[DEBUG],[1],LogIF.DebugLog("ACCESS"); +[2026/08/18 02:24:36,948],[INFO ],[1],LogIF.InfoLog("ACCESS"); +[2026/08/18 02:24:36,949],[WARN ],[1],LogIF.WarnLog("ACCESS"); +[2026/08/18 02:24:36,949],[ERROR],[1],LogIF.ErrorLog("ACCESS"); +[2026/08/18 02:24:36,949],[FATAL],[1],LogIF.FatalLog("ACCESS"); LogIF.Is*Enabled - ACCESS : Debug True / Info True / Warn True / Error True / Fatal True log4net: configuring repository [log4net-default-repository] using stream log4net: loading XML configuration @@ -2621,12 +2621,17 @@ Four > Four XmlLib > is working properly. -------------------------------------------------- XmlLib.GetAttributeByTagName - item/name: [一つ目] -XmlLib.GetAttributeByTagName - item/name(index=1): [一つ目] +XmlLib.GetAttributeByTagName - item/name(index=1): [二つ目] +XmlLib.GetAttributeByTagName - item/name(index=9): [] +XmlLib.GetAttributeByTagName - item/name(index=-1): [] XmlLib.GetAttributeByTagName - empty/name: [] XmlLib.GetAttributeByTagName - none/name: [] XmlLib.GetAttributeByTagName - item/none: [] XmlLib.GetAttributeByXPath - //item[@id='i2']/name: [二つ目] XmlLib.GetAttributeByXPath - //none: [] +XmlLib.GetAttributeByXPath - //item(index=0): [一つ目] +XmlLib.GetAttributeByXPath - //item(index=1): [二つ目] +XmlLib.GetAttributeByXPath - //item(index=2): [] XmlLib.GetAttributeFromXmlNode - name: [一つ目] XmlLib.GetAttributeFromXmlNode - null: [] XmlLib.GetXmlNodeById - item: item id=i1 diff --git a/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt b/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt index 3b6ebe545..36370c84d 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt +++ b/root/programs/CS/Frameworks/Tests/TestCode/ResultCore100.txt @@ -70,11 +70,11 @@ log4net: Setting Collection Property [AddFilter] to object [log4net.Filter.Level log4net: Created Appender [OPERATION2] log4net: Adding appender named [OPERATION2] to logger [OPERATION]. log4net: Hierarchy Threshold [] -[2026/08/17 22:30:46,582],[DEBUG],[2],LogIF.DebugLog("ACCESS"); -[2026/08/17 22:30:46,619],[INFO ],[2],LogIF.InfoLog("ACCESS"); -[2026/08/17 22:30:46,623],[WARN ],[2],LogIF.WarnLog("ACCESS"); -[2026/08/17 22:30:46,624],[ERROR],[2],LogIF.ErrorLog("ACCESS"); -[2026/08/17 22:30:46,625],[FATAL],[2],LogIF.FatalLog("ACCESS"); +[2026/08/18 02:24:41,605],[DEBUG],[2],LogIF.DebugLog("ACCESS"); +[2026/08/18 02:24:41,619],[INFO ],[2],LogIF.InfoLog("ACCESS"); +[2026/08/18 02:24:41,620],[WARN ],[2],LogIF.WarnLog("ACCESS"); +[2026/08/18 02:24:41,620],[ERROR],[2],LogIF.ErrorLog("ACCESS"); +[2026/08/18 02:24:41,621],[FATAL],[2],LogIF.FatalLog("ACCESS"); LogIF.Is*Enabled - ACCESS : Debug True / Info True / Warn True / Error True / Fatal True log4net: configuring repository [log4net-default-repository] using stream log4net: loading XML configuration @@ -2642,12 +2642,17 @@ Four > Four XmlLib > is working properly. -------------------------------------------------- XmlLib.GetAttributeByTagName - item/name: [一つ目] -XmlLib.GetAttributeByTagName - item/name(index=1): [一つ目] +XmlLib.GetAttributeByTagName - item/name(index=1): [二つ目] +XmlLib.GetAttributeByTagName - item/name(index=9): [] +XmlLib.GetAttributeByTagName - item/name(index=-1): [] XmlLib.GetAttributeByTagName - empty/name: [] XmlLib.GetAttributeByTagName - none/name: [] XmlLib.GetAttributeByTagName - item/none: [] XmlLib.GetAttributeByXPath - //item[@id='i2']/name: [二つ目] XmlLib.GetAttributeByXPath - //none: [] +XmlLib.GetAttributeByXPath - //item(index=0): [一つ目] +XmlLib.GetAttributeByXPath - //item(index=1): [二つ目] +XmlLib.GetAttributeByXPath - //item(index=2): [] XmlLib.GetAttributeFromXmlNode - name: [一つ目] XmlLib.GetAttributeFromXmlNode - null: [] XmlLib.GetXmlNodeById - item: item id=i1 diff --git a/root/programs/CS/Frameworks/Tests/TestCode/TestXmlLib.cs b/root/programs/CS/Frameworks/Tests/TestCode/TestXmlLib.cs index 6c11ef14e..04c14c4c2 100644 --- a/root/programs/CS/Frameworks/Tests/TestCode/TestXmlLib.cs +++ b/root/programs/CS/Frameworks/Tests/TestCode/TestXmlLib.cs @@ -94,11 +94,20 @@ private static void GetAttributeTest() MyDebug.OutputDebugAndConsole( "XmlLib.GetAttributeByTagName - item/name: [" + XmlLib.GetAttributeByTagName(doc, "item", "name") + "]"); - // **タグが複数あっても、先頭しか見ない。** index を渡しても変わらない。 + // **index で何番目かを選べる。**(#563 で実装された) MyDebug.OutputDebugAndConsole( "XmlLib.GetAttributeByTagName - item/name(index=1): [" + XmlLib.GetAttributeByTagName(doc, "item", "name", 1) + "]"); + // **範囲外は空文字列。** 例外にはならない。 + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByTagName - item/name(index=9): [" + + XmlLib.GetAttributeByTagName(doc, "item", "name", 9) + "]"); + + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByTagName - item/name(index=-1): [" + + XmlLib.GetAttributeByTagName(doc, "item", "name", -1) + "]"); + // 属性が無い要素 MyDebug.OutputDebugAndConsole( "XmlLib.GetAttributeByTagName - empty/name: [" + XmlLib.GetAttributeByTagName(doc, "empty", "name") + "]"); @@ -116,10 +125,19 @@ private static void GetAttributeTest() "XmlLib.GetAttributeByXPath - //item[@id='i2']/name: [" + XmlLib.GetAttributeByXPath(doc, "//item[@id='i2']", "name") + "]"); - // XPath が当たらない(SelectSingleNode が null) + // XPath が当たらない MyDebug.OutputDebugAndConsole( "XmlLib.GetAttributeByXPath - //none: [" + XmlLib.GetAttributeByXPath(doc, "//none", "name") + "]"); + // **複数当たる XPath で index を効かせる。**(#563 で実装された) + // index=0 の結果は、SelectSingleNode を使っていた頃と同じであること。 + for (int i = 0; i <= 2; i++) + { + MyDebug.OutputDebugAndConsole( + "XmlLib.GetAttributeByXPath - //item(index=" + i.ToString() + "): [" + + XmlLib.GetAttributeByXPath(doc, "//item", "name", null, i) + "]"); + } + // XmlNode から直接 XmlNode node = doc.SelectSingleNode("//item[@id='i1']"); MyDebug.OutputDebugAndConsole(