0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

外部プログラムを実行するプログラム=>理想のプログラムテストにできるんじゃないか説 4

Last updated at Posted at 2025-04-12

前回の試作プログラムからもう少し改善したバージョンを作成しました。
例によって、作りかけなのできちんと動作確認できてないので注意。

check_resultの自動設定上書きと設定上書きの仕組み見直し

  1. "check_result": {}の記述は実際の結果を予想の設定(expect)に上書きする動作に変更。

  2. 上記のcheck_resultの設定上書きを含む、プログラム実行後の結果を設定へ反映する動作を追加。
    関数の引数はdictionary型のsettings1つの構成result=func(settings)にしている。
    関数実行結果によって設定を変えたい場合は子の関数内でsettingsを変更すると、実行後に開始時と終了時のsettingsの差を親へと伝えていく。
    多少、トリッキーな動作だが・・・、resultに設定のフィードバック情報を入れるのは通常使用に支障きたすし、消去法的に引数を介して伝えるしかない。幸い、settingsはdictionaryなのでいくらでも変数を追加できる自由度、拡張性がある。また、シンプルな挙動なのでルールが分かりやすい。
    ただし、簡単に変えられすぎるので、バグで設定を書き換える危険もある。ファイルに保存する段階でユーザー確認の機構を追加する必要があるかもしれない。

RunPlanLists(settings={"data_store":{},"planlists":{},"run_plan_name_list":[]}}):

settings内にPlanlists内のrun_plan_name_listで指定されたPlanlist(複数のPlan)を順次実行。

↓ settings指定 ↑settings変更点を親に反映

ExecutePlan(settings={"data_store":{},"planlists":{},"run_plan_name":"","run_plan_number":0}):

planで設定された"type"の内部Programを起動する。

↓ settings指定 ↑settings変更点を親に反映
内部Program

RunPlanLists(settings=...):
ExecuteProgram(settings=...):

試作テスト所感X クラスの扱いが練られてない

  • 現状ではinstanceの再利用までは考えないことにした。
  • classの子にmethodが複数あり、それらの結果を親に通知するのが大変かと思っていましたが、シンプルにdictionaryにまとめて保存する事としました。
    各methodはresultの設定で指定した名前でresult_dictionaryの子として保存する。全てのmethodの結果を親のclassの実行結果として返す。
Classの結果はmethodの結果をまとめて返す
"check_result": {
    "expected": {
                "result_Equals": false,
                "result_GetHashCode": 52399374,
                "result_GetType": {
                    "Error": "System.RuntimeTypecan not convert this object type",
                    "Result_type": "System.RuntimeType"
                },
                "result_ToString": "ConfigSettingDiff.HelpEntry"
}

ちなみに、この私のJSON記述法だと、settings内で"result":"result_data_name"と書くと内部メモリdata_storeに結果が格納されると動作になります。子methodの結果保存はこの応用です。

実行結果例
{
    "test_dll": [
        {
            "settings": {
                "argument_names": [],
                "argument_types": [],
                "arguments": [],
                "check_result": {
                    "expected": {
                        "result_Equals": false,
                        "result_GetHashCode": 52399374,
                        "result_GetType": {
                            "Error": "System.RuntimeTypecan not convert this object type",
                            "Result_type": "System.RuntimeType"
                        },
                        "result_ToString": "ConfigSettingDiff.HelpEntry"
                    }
                },
                "class_name": "ConfigSettingDiff.HelpEntry",
                "methods": [
                    {
                        "argument_names": [
                            "obj"
                        ],
                        "argument_types": [
                            "Object"
                        ],
                        "arguments": [
                            {
                                "key": "value"
                            }
                        ],
                        "check_result": {
                            "expected": null
                        },
                        "method_name": "Equals",
                        "result": "result_Equals"
                    },
                    {
                        "argument_names": [],
                        "argument_types": [],
                        "arguments": [],
                        "check_result": {
                            "expected": 52399374
                        },
                        "method_name": "GetHashCode",
                        "result": "result_GetHashCode"
                    },

さて、Sourceは以下の通りです。
足りてないライブラリは以前のとおり、大したものではないので省略

上記以外にPlanlistのパース部、data_storeの実装など、外部プログラムを連続実行する時に欲しい機能を少し追加しました。

FunctionControl.py
import ast
import inspect
import random
import json
import importlib.util
import os
import JSON_Control
from typing import List, Any
import ListControl
import DLLControl
import clr
import itertools
import copy
import SharedMemory
import SettingUtility
import ClassControl
import DictionaryControl
def get_file_type(file_path: str) -> str:
    """ファイルパスからファイルタイプを判別する純粋関数"""
    _, ext = os.path.splitext(file_path)
    return ext.lower()

def load_python_file(file_path: str) -> ast.Module:
    """Pythonファイルをロードする純粋関数"""
    with open(file_path, 'r', encoding='utf-8') as file:
        return ast.parse(file.read())

def get_function_info(file_path: str, options: dict={}) -> list:
    file_type = get_file_type(file_path)
    """ロードされたファイルから関数情報を取得する純粋関数"""
    function_informations = []
    if file_type == '.cs':
        file_name, ext = os.path.splitext(file_path)
        dll_path = file_name + ".dll"
        references = options.get("references","")
        DLLControl.compile_source_to_dll(file_path, dll_path, references=references)
        # DLL側のメソッド情報を取得
        function_informations = DLLControl.get_dll_function_info(dll_path)
        program_path = dll_path
    elif file_type == '.dll':
        function_informations = DLLControl.get_dll_function_info(file_path)
        program_path = file_path
    elif file_type == '.py':
        loaded_file = load_python_file(file_path)
        # Pythonの関数情報を取得
        function_informations = get_python_function_info(loaded_file)
        program_path = file_path
    else:
        raise ValueError(f"Unsupported file type: {file_type}")
    for function_information in function_informations:
        function_information["program_path"] = program_path
    return function_informations

def get_python_function_info(module: ast.Module) -> list:
    """Pythonファイルから関数情報を取得する純粋関数"""
    function_infos = []
    for node in ast.walk(module):
        if isinstance(node, ast.FunctionDef):
            # クラス内の __init__ はクラスの初期化情報として別途扱うため除外
            if node.name == "__init__":
                continue
            function_name = node.name
            arguments = [arg.arg for arg in node.args.args]
            function_infos.append({
                "function_name": function_name,
                "class_name": "",  # 関数の場合は空文字列
                "argument_names": arguments,
                "argument_types": [None] * len(arguments)  # Pythonでは型ヒントがない場合、Noneとする
            })
    return function_infos

def get_python_class_init_info(module: ast.Module) -> list:
    """
    Pythonファイルからクラス定義を探し、コンストラクタ(__init__)の引数情報を取得する。
    クラスの初期化テストとして、テスト設定に "type""InstantiateClass" として追加する。
    """
    constructor_infos = []
    for node in ast.walk(module):
        if isinstance(node, ast.ClassDef):
            class_name = node.name
            # クラス内から __init__ を探す
            init_args = []
            init_types = []
            for item in node.body:
                if isinstance(item, ast.FunctionDef) and item.name == "__init__":
                    # 第一引数 self を除く
                    init_args = [arg.arg for arg in item.args.args[1:]]
                    init_types = [None] * len(init_args)
                    break
            # コンストラクタ情報をテストケースとして追加(存在しなくても、デフォルトコンストラクタのテスト)
            constructor_infos.append({
                "function_name": "__init__",
                "class_name": class_name,
                "argument_names": init_args,
                "argument_types": init_types,
                "is_constructor": True  # フラグでコンストラクタであることを明示
            })
    return constructor_infos

def infer_argument_type(argument_name, type_inference_rules):
    """引数の名前から型を推測する"""
    lower_name = argument_name.lower()
    if any(keyword in lower_name for keyword in type_inference_rules["numeric_keywords"]):
        return "numeric"
    elif any(keyword in lower_name for keyword in type_inference_rules["string_keywords"]):
        return "string"
    elif any(keyword in lower_name for keyword in type_inference_rules["list_keywords"]):
        return "list"
    elif any(keyword in lower_name for keyword in type_inference_rules["dictionary_keywords"]):
        return "dictionary"
    else:
        return "unknown"


def generate_test_cases(function_informations, type_inference_rules):
    """テストケースを生成する(同値分割、境界値分析、ペアワイズテスト)
    
    ・function_informationに "methods" キーが存在する場合はクラス情報として扱い、
      初期化(コンストラクタ)の引数と各メソッドのテスト設定をひとまとめにする。
    ・それ以外はグローバルな関数として扱う。
    """
    test_cases = []
    for info in function_informations:
        program_path = info.get("program_path", "")
        
        # クラス情報の場合("methods"キーが存在する場合)
        if "methods" in info:
            class_name = info.get("class_name", "")
            # コンストラクタの引数情報
            init_arg_names = info.get("init_argument_names", [])
            init_arg_types = info.get("init_argument_types", [])
            # 推論処理:不足分は型推論で補完
            init_types = []
            for i, arg in enumerate(init_arg_names):
                if i < len(init_arg_types) and init_arg_types[i]:
                    init_types.append(init_arg_types[i])
                else:
                    init_types.append(infer_argument_type(arg, type_inference_rules))
            # コンストラクタ引数のテスト値の組み合わせを生成
            init_arg_values_sets = [generate_argument_values(arg_type) for arg_type in init_types]
            if len(init_arg_values_sets) == 0:
                init_combinations = [[]]
            else:
                # 全組み合わせの直積を生成
                init_combinations = list(itertools.product(*init_arg_values_sets))
            
            # 各メソッドについてテストケースを生成
            methods_info = info.get("methods", [])
            # 各メソッドごとに、テスト値の組み合わせからテストオブジェクトのリストを作成
            method_tests_lists = []
            for method in methods_info:
                method_name = method.get("method_name", "")
                m_arg_names = method.get("argument_names", [])
                m_arg_types = method.get("argument_types", [])
                # 推論処理
                m_types = []
                for i, arg in enumerate(m_arg_names):
                    if i < len(m_arg_types) and m_arg_types[i]:
                        m_types.append(m_arg_types[i])
                    else:
                        m_types.append(infer_argument_type(arg, type_inference_rules))
                # テスト値の組み合わせを生成
                m_arg_values_sets = [generate_argument_values(arg_type) for arg_type in m_types]
                if len(m_arg_values_sets) == 0:
                    m_combinations = [[]]
                else:
                    m_combinations = list(itertools.product(*m_arg_values_sets))
                # 各組み合わせごとにメソッドテスト設定を作成
                method_tests = []
                for comb in m_combinations:
                    method_tests.append({
                        "method_name": method_name,
                        "argument_names": m_arg_names,
                        "argument_types": m_types,
                        "arguments": list(comb),
                        "check_result": {},
                        "result": "result_"+method_name
                    })
                method_tests_lists.append(method_tests)
            
            # クラスのテストケースは、初期化引数の各組み合わせと、各メソッドのテスト設定の直積により生成
            for init_comb in init_combinations:
                # 各メソッドのテストケースの直積(全メソッドに対して1組み合わせずつ選ぶ)
                for methods_comb in itertools.product(*method_tests_lists):
                    test_case = {
                        "type": "ExecuteProgram",
                        "settings": {
                            "program_path": program_path,
                            "class_name": class_name,
                            "argument_names": init_arg_names,
                            "argument_types": init_types,
                            "arguments": list(init_comb),
                            "methods": list(methods_comb),
                            "check_result": {},
                            "result": "result_"+class_name
                        }
                    }
                    test_cases.append(test_case)
        else:
            # グローバルな関数の場合の処理
            class_name = info.get("class_name", "")
            function_name = info.get("function_name", "")
            argument_names = info.get("argument_names", [])
            arguments_types = info.get("argument_types", None)
            arg_types = []
            if arguments_types:
                for i, arg in enumerate(argument_names):
                    if i < len(arguments_types) and arguments_types[i]:
                        arg_types.append(arguments_types[i])
                    else:
                        arg_types.append(infer_argument_type(arg, type_inference_rules))
            else:
                arg_types = [infer_argument_type(arg, type_inference_rules) for arg in argument_names]
            arg_values_sets = [generate_argument_values(arg_type) for arg_type in arg_types]
            if len(arg_values_sets) == 0:
                combinations = [[]]
            else:
                combinations = list(itertools.product(*arg_values_sets))
            for arg_values in combinations:
                test_case = {
                    "type": "ExecuteProgram",
                    "settings": {
                        "program_path": program_path,
                        "function_name": function_name,
                        "class_name": class_name,
                        "argument_names": argument_names,
                        "argument_types": arg_types,
                        "arguments": list(arg_values),
                        "check_result": {},
                        "result": "result_"+class_name
                    }
                }
                test_cases.append(test_case)
    return test_cases

def generate_argument_values(arg_type):
    """引数の型に基づいて代表的な値を生成する(同値分割、境界値分析)"""
    if arg_type == "numeric":
        return [1, -1, 0, 2147483647, -2147483647]
    elif arg_type == "string":
        return ["", "test", "a" * 10, None]
    elif arg_type == "list":
        return [[], [1]]
    elif arg_type == "dictionary":
        return [{}, {"key": "value"}]
    elif arg_type == "int":
        return [1, -1, 0, 2147483647, -2147483648]
    elif arg_type == "double":
        return [1.0, -1.0, 0.0, 3.14, float('inf'), float('-inf'), float('nan')]
    elif arg_type == "bool":
        return [True, False]
    elif arg_type == "DateTime":
        return ["2023-10-27T10:00:00", "1970-01-01T00:00:00", None]
    elif arg_type == "String":
        return ["", "test", "a" * 1024, None]
    elif arg_type == "String[]":
        return [[""], ["test"], ["test1", "test2"], None]
    elif arg_type == "List<string>":
        return [[""], ["test"], ["test1", "test2"], None]
    elif arg_type in ("Dictionary<string,string>", "Dictionary`2"):
        return [ {"key": "value"}, {},None]
    elif arg_type == "Object":
        return [{"key": "value"}, None]
    else:
        return [None]

def pairwise_combinations(lists):
    """ペアワイズの組み合わせを生成する"""
    if not lists:
        yield []
    elif len(lists) == 1:
        for item in lists[0]:
            yield [item]
    else:
        for pair in itertools.product(lists[0], lists[1]):
            rest_lists = lists[2:]
            if not rest_lists:
                yield list(pair)
            else:
                for rest in generate_combinations(rest_lists):
                    yield list(pair) + rest

def generate_combinations(lists):
    """リストの組み合わせを生成する"""
    if not lists:
        yield []
    else:
        for item in lists[0]:
            for rest in generate_combinations(lists[1:]):
                yield [item] + rest


# --- グローバル関数用テスト実行 ---
def execute_function(settings):
    program_path = settings.get("program_path", "")
    function_name = settings.get("function_name", "")
    class_name = settings.get("class_name", "")
    argument_values = settings.get("arguments", [])
    argument_names = settings.get("argument_names", [])
    argument_types = settings.get("argument_types", [])
    
    try:
        if program_path.lower().endswith(".dll"):
            # C#のDLLの場合
            assembly = DLLControl.load_dll(os.path.abspath(program_path))
            target_type = assembly.GetType(class_name)
            if target_type:
                method = target_type.GetMethod(function_name)
                if method:
                    converted_args = [DLLControl.convert_python_to_cs(val, typ) for val, typ in zip(argument_values, argument_types)]
                    format_str = ",".join(ListControl.format_merge_multiple_list(
                        "{list3} {list1} = {list2}", "",
                        list1=argument_names, list2=converted_args, list3=argument_types))
                    print(f"{class_name}().{function_name}({format_str})")
                    if method.IsStatic:
                        result_value = method.Invoke(None, converted_args)
                    else:
                        # インスタンス生成(デフォルトコンストラクタ)してメソッド実行
                        instance = DLLControl.create_instance(assembly, class_name, *[])
                        result_value = instance.GetType().GetMethod(function_name).Invoke(instance, converted_args)
                    return result_value
                else:
                    raise AttributeError(f"メソッドが見つかりません: {function_name}")
            else:
                raise AttributeError(f"クラスが見つかりません: {class_name}")
        else:
            # Pythonファイルの場合
            spec = importlib.util.spec_from_file_location(function_name, program_path)
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)
            function = getattr(module, function_name)
            argument_names = get_argument_names(function)
            format_str = ",".join(ListControl.format_merge_multiple_list(
                "{list1} = {list2}", "",
                list1=argument_names, list2=argument_values))
            print(f"Function: {function_name}({format_str})")
            result_value = function(*argument_values)
        return result_value
    except FileNotFoundError:
        raise(f"ファイルが見つかりません: {program_path}")
    except AttributeError as e:
        raise(f"クラスのインスタンス化またはメソッド呼び出しに失敗しました: {e}")
    except Exception as e:
        raise(f"エラーが発生しました: {e}")


# --- クラステスト用:インスタンス生成+各メソッド実行 ---
def execute_class(settings):
    program_path = settings.get("program_path", "")
    class_name = settings.get("class_name", "")
    init_argument_names = settings.get("argument_names", [])
    init_argument_types = settings.get("argument_types", [])
    init_argument_values = settings.get("arguments", [])
    methods = settings.get("methods", [])
    
    try:
        if program_path.lower().endswith(".dll"):
            # C#のDLLの場合
            assembly = DLLControl.load_dll(os.path.abspath(program_path))
            target_type = assembly.GetType(class_name)
            if not target_type:
                raise AttributeError(f"クラスが見つかりません: {class_name}")
            converted_init_args = [DLLControl.convert_python_to_cs(val, typ) for val, typ in zip(init_argument_values, init_argument_types)]
            instance = DLLControl.create_instance(assembly, class_name, *converted_init_args)
            class_fmt = ",".join(ListControl.format_merge_multiple_list("{list3} {list1}={list2}", "", list1=init_argument_names, list2=converted_init_args,list3 = init_argument_types))
            
            # 各メソッド実行
            for method_setting in methods:
                method_name = method_setting.get("method_name", "")
                m_argument_names = method_setting.get("argument_names", [])
                m_argument_types = method_setting.get("argument_types", [])
                m_argument_values = method_setting.get("arguments", [])
                fmt = ",".join(ListControl.format_merge_multiple_list("{list3} {list1}={list2}", "", list1=m_argument_names, list2=m_argument_values,list3 = m_argument_types))
                converted_m_args = [DLLControl.convert_python_to_cs(val, typ) for val, typ in zip(m_argument_values, m_argument_types)]
                print(f"{class_name}({class_fmt}).{method_name}({fmt})")
                method = instance.GetType().GetMethod(method_name)
                if not method:
                    raise AttributeError(f"メソッドが見つかりません: {method_name}")
                cs_result = method.Invoke(instance, converted_m_args)
                result = DLLControl.convert_cs_to_python(cs_result)
                method_setting = CheckResult(method_setting,result)

        else:
            # Pythonクラスの場合
            spec = importlib.util.spec_from_file_location(class_name, program_path)
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)
            target = getattr(module, class_name)
            if not inspect.isclass(target):
                raise AttributeError(f"{class_name} is not a class")
            instance = target(*init_argument_values)
            print(f"{class_name} インスタンス生成: 引数 {list(zip(init_argument_names, init_argument_values))}")
            
            # 各メソッド実行
            for method_setting in methods:
                method_name = method_setting.get("method_name", "")
                m_argument_names = method_setting.get("argument_names", [])
                m_argument_types = method_setting.get("argument_types", [])
                m_argument_values = method_setting.get("arguments", [])
                fmt = ",".join(ListControl.format_merge_multiple_list("{list1}={list2}", "", list1=m_argument_names, list2=m_argument_values))
                print(f"{class_name}.{method_name}({fmt})")
                if not hasattr(instance, method_name):
                    raise AttributeError(f"メソッドが見つかりません: {method_name}")
                method = getattr(instance, method_name)
                result = method(*m_argument_values)
                method_setting = CheckResult(method_setting,result)
        settings["methods"] = methods 
        return  settings         
    except FileNotFoundError:
        print(f"ファイルが見つかりません: {program_path}")
    except AttributeError as e:
        print(f"クラスのインスタンス生成またはメソッド呼び出しに失敗しました: {e}")
    except Exception as e:
        print(f"エラーが発生しました: {e}")


def create_instance(settings):
    program_path = settings.get("program_path", "")
    class_name = settings.get("class_name", "")
    init_argument_names = settings.get("argument_names", [])
    init_argument_types = settings.get("argument_types", [])
    init_argument_values = settings.get("arguments", [])
    
    if program_path.lower().endswith(".dll"):
        # C# の DLL の場合
        assembly = DLLControl.load_dll(os.path.abspath(program_path))
        target_type = assembly.GetType(class_name)
        if not target_type:
            raise AttributeError(f"クラスが見つかりません: {class_name}")
        converted_init_args = [DLLControl.convert_python_to_cs(val, typ) for val, typ in zip(init_argument_values, init_argument_types)]
        instance = DLLControl.create_instance(assembly, class_name, *converted_init_args)
    else:
        # Python クラスの場合
        spec = importlib.util.spec_from_file_location(class_name, program_path)
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)
        target = getattr(module, class_name)
        if not inspect.isclass(target):
            raise AttributeError(f"{class_name} is not a class")
        instance = target(*init_argument_values)
    
    return instance

def execute_methods_cs(instance,  method_setting):
    method_name = method_setting.get("method_name", "")
    m_argument_types = method_setting.get("argument_types", [])
    m_argument_values = method_setting.get("arguments", [])
    
    converted_m_args = [DLLControl.convert_python_to_cs(val, typ) for val, typ in zip(m_argument_values, m_argument_types)]
    method = instance.GetType().GetMethod(method_name)
    if not method:
        raise AttributeError(f"メソッドが見つかりません: {method_name}")
    cs_result = method.Invoke(instance, converted_m_args)
    result = DLLControl.convert_cs_to_python(cs_result)
    return result

def execute_methods_python(instance,  method_setting):
    method_name = method_setting.get("method_name", "")
    m_argument_values = method_setting.get("arguments", [])
    if not hasattr(instance, method_name):
        raise AttributeError(f"メソッドが見つかりません: {method_name}")
    method = getattr(instance, method_name)
    result = method(*m_argument_values)
    method_setting = CheckResult(method_setting,result)
    return result




def method_format(method_setting):
    method_name = method_setting.get("method_name", "")
    m_argument_names = method_setting.get("argument_names", [])
    m_argument_types = method_setting.get("argument_types", [])
    m_argument_values = method_setting.get("arguments", [])

    fmt = ",".join(ListControl.format_merge_multiple_list("{list3} {list1}={list2}", "", list1=m_argument_names, list2=m_argument_values, list3=m_argument_types))
    return f"{method_name}({fmt})"

# --- テストケース全体の実行 ---
def ExecuteProgram(settings):
    """テストケースに基づいてプログラムを実行する"""
    class_name = settings.get("class_name", "")
    methods = settings.get("methods", [])
    program_path = settings.get("program_path", "")
    result_dictionary={}
    if methods:
        try:
            if program_path.lower().endswith(".dll"):
                instance = create_instance(settings)
                for i, method_setting in enumerate(methods):
                    fmt = method_format(method_setting)
                    print(f"{program_path}-{i} {class_name}.{fmt}")
                    try:
                        result_value = execute_methods_cs(instance, method_setting)
                    except Exception as e:
                        result_value = {"success": False, "error": str(e)}
                    methods[i] = CheckResult(method_setting, result_value)
                    result_settings = method_setting.get("result")
                    if result_settings:
                        WriteDatas(result_settings,result_dictionary,result_value)
            else:
                instance = create_instance(settings)
                for method_setting in methods:
                    fmt = method_format(method_setting)
                    print(f"{program_path}-{class_name}.{fmt}")
                    try:
                        result_value = execute_methods_python(instance, method_setting)
                    except Exception as e:
                        result_value ={"success":False,"error":str(e)}
                    method_setting = CheckResult(method_setting,result_value)
                    result_settings = method_setting.get("result")
                    if result_settings:
                        WriteDatas(result_settings,result_dictionary,result_value)
            return result_dictionary
        except FileNotFoundError:
            raise(f"ファイルが見つかりません: {settings.get('program_path', '')}")
        except AttributeError as e:
            raise(f"クラスのインスタンス生成またはメソッド呼び出しに失敗しました: {e}")
        except Exception as e:
            raise(f"エラーが発生しました: {e}")
    else:
        return execute_function(settings)
        
        
def write_test_cases(program_path, output_file_path, test_data_name, type_inference_rules, options={}):
    function_informations = get_function_info(program_path, options)
    if not function_informations:
        return
    test_cases = generate_test_cases(function_informations, type_inference_rules)
    test_plan_list = {test_data_name: test_cases}
    # JSONファイルに出力
    JSON_Control.WriteDictionary(output_file_path, test_plan_list)
    return test_plan_list

def get_argument_names(func: Any) -> list:
    """
    定義された引数名を取得する関数
    """
    sig = inspect.signature(func)
    arg_names = list(sig.parameters.keys())
    return arg_names

def normalize_program_output(output):
    """
    外部プログラムの戻り値 output を解析し、以下のキーを持つ辞書を返す:
      - "success": 実行の成否(True/False)
      - "result_value": 得られた結果または判断理由
      - "error": エラー情報(あれば)
    """
    if isinstance(output, dict):
        result = output.get("result_value", None)
        error = output.get("error", None)
        success_judge = False if error else True
        if result:
            result_dictionary = {
                "success": output.get("success", success_judge),
                "result_value": result,
            }
            result_dictionary.update(output)
            return result_dictionary
        else:
            if error:
                return {"success": output.get("success", success_judge), "result_value": output, "error": error}
            else:
                return {"success": output.get("success", success_judge), "result_value": output}
    elif isinstance(output, (tuple, list)):
        return {"success": True, "result_value": output}
    elif isinstance(output, bool):
        return {"success": True, "result_value": output, "error": None}
    elif isinstance(output, str):
        return {"success": True, "result_value": output, "error": None}
    else:
        return {"success": True, "result_value": output, "error": None}
def RunPlanLists(settings):
    data_store = settings.get("data_store",{})
    plan_lists = settings.get("plan_lists",{})
    run_plan_name_list = settings.get("run_plan_name_list")
    if run_plan_name_list == "":
        details = {"success":False,"error":"run_plan_name_list is nothing."}
        return details
    if not isinstance(run_plan_name_list,list):
        run_plan_name_list = [run_plan_name_list]
    for run_plan_name in run_plan_name_list:        
        plan_list = plan_lists.get(run_plan_name,"")
        if plan_list:
            if not isinstance(plan_list,list):
                plan_list=[plan_list]
            for num,plan in enumerate(plan_list):
                plan_list_filepath = settings.get("plan_lists_file_path","")
                if plan_list_filepath:
                    plan_lists = JSON_Control.ReadDictionary(plan_list_filepath,{},details = details)
                    if details.get("success",True) == False:
                        print(str(details))
                        input("file error.please push and skip."+plan_list_filepath)
                plan_settings = {
                    "data_store":data_store,
                    "plan_lists":plan_lists,
                    "run_plan_name":run_plan_name,
                    "run_plan_number": num
                }
                result = ExecutePlan(plan_settings)
        
def ExecutePlan(settings):
    # 実行する設定の読込
    data_store = settings.get("data_store",{})
    plan_lists = settings.get("plan_lists",{})
    run_plan_name = settings.get("run_plan_name","")
    run_plan_number = settings.get("run_plan_number","")

    plan_list = plan_lists.get(run_plan_name)
    if not plan_list:
        return {"success":False,"error":"plan_list is nothing"}
    if not isinstance(plan_list,list):
        plan_list=[plan_list]
    plan = plan_list[run_plan_number]
    if not plan:
        return {"success":False,"error":"plan is nothing"}
    plan_copy = copy.deepcopy(plan)
    
    plan_type= plan_copy.get("type","Action")
    plan_settings = plan_copy.get("settings",{})        
    check_result = plan_settings.get("check_result",None)
    # 実行するPlan内容の表示
    if check_result:
        plan_name =plan_copy.get("name","")
        message= + plan_name+"("+str(settings)+")" 
        print(message)
    # referenceの内容をdata_store,shared_memoryから参照して反映する。      
    apply_reference_values(plan_settings,data_store,shared_memory_file_path="")
    #関数を実行する
    try:
        current_module = inspect.getmodule(inspect.currentframe())
        loaded_function = ClassControl.load_function_from_module(current_module,plan_type)
        plan_settings_backup = copy.deepcopy(plan_settings)
        result_value = loaded_function(plan_settings)    
    except Exception as e:
        message = str(e)
        result_value = {"success":False,"error": message}
    plan_settings["result_value"]=result_value
    check_result = plan_settings.get("check_result",None)
    if check_result is not None:
        check_result_settings ={
            "result_value":result_value,
            "check_result":check_result                    
        }
        CheckResult(check_result_settings)
    result_diff = DictionaryControl.DiffDictionaries(plan_settings_backup,plan_settings)
    if result_diff:
        plan["settings"] = DictionaryControl.ChangeDictionary(result_diff,plan.get("settings",{}))
    result_settings = plan_settings.get("result")
    if result_settings:
        WriteDatas(result_settings,data_store,result_value)

def CheckResult(settings,result_value={}):
    check_result = settings.get("check_result", None)
    if check_result == None:
        return settings 
    if not result_value:
        result_value = settings.get("result_value")
    if check_result=={}:
        check_result["expected"] = result_value
        settings["check_result"] = check_result
        print(f"Write expected: {result_value}")

    elif check_result:
        expected = check_result.get("expected")
        if result_value == expected:
            print(f"OK Result: {result_value}")
        else:
            print(f"NG Result: {result_value}, expected: {expected}")
            user_input = input("OverWrite the expected result setting with the actual results? :(W) ")
            if user_input.upper() == "W":
                check_result["expected"] = result_value
                settings["check_result"] = check_result
    return settings 
def WriteDatas(settings,data_store,datas):
    if isinstance(settings,str):
        key = settings
        data_store[key] = datas
        print("WriteDatas("+key+":"+str(datas)+")")
    if isinstance(settings,dict):
        # 設定に基づいてデータを抽出して保存
        for key,data_key in settings.items():
            if data_key in datas:
                value =datas[data_key]
                data_store[key] = value
                print("WriteDatas("+key+":"+str(value)+")")

def apply_reference_values(settings,data_store,shared_memory_file_path=""):
    reference_list = settings.get("reference", [])
    if not reference_list:
        return settings
    if not isinstance(reference_list,list):
        reference_list =[reference_list]
    for reference in reference_list:
        if isinstance(reference,dict):
            source_type = reference.get("source_type","data_store")
            if source_type =="share_memory":
                shared_memory = SharedMemory.read_from_shared_memory(shared_memory_file_path,{})
                shared_memory_name = settings.get("shared_memory_name","shared_memory_name")
                shared_data=shared_memory.get(shared_memory_name,{})
                for key, reference_key in reference.items():
                    value = shared_data.get(reference_key, "")
                    if value:
                        settings[key] = value
                    else:
                        value = data_store.get(reference_key,"")
                        if value:
                            settings[key] = value
            elif source_type =="data_store":
                for key, reference_key in reference.items():
                    value = data_store.get(reference_key,"")
                    if value:
                        settings[key] = value

def main():
    type_inference_rules = {
        "numeric_keywords": {'size', 'count', 'width', 'height', 'ratio', 'length', 'number', 'index'},
        "string_keywords": {'name', 'text', 'message', 'title', 'path', 'filename'},
        "list_keywords": {"list"},
        "dictionary_keywords": {"dict", "dictionary"}
    }
    options = {"references": [".\\Reference\\Newtonsoft.Json.13.0.3\\lib\\net35\\Newtonsoft.Json.dll"]}
    data_store ={"key1":"value1","key2":"value2"}
    
    # C# ソース(.cs)のテスト
    program_path = "../../../Tools/diff.cs"
    output_filename = "Test_cs.json"
    data_name = "test"
    test_cases = write_test_cases(program_path, output_filename, data_name, type_inference_rules, options)
    if test_cases:
        settings ={
            "plan_lists":test_cases,
            "run_plan_name_list":data_name
        }
        RunPlanLists(settings)
        test_cases=settings.get("plan_lists")
        JSON_Control.WriteDictionary(output_filename, test_cases)
        #RunPlanLists(settings)
        
    # DLL のテスト
    program_path = "../../../Tools/diff.dll"
    output_filename = "Test_dll.json"
    data_name = "test_dll"
    test_cases = write_test_cases(program_path, output_filename, data_name, type_inference_rules, options)
    if test_cases:
        settings ={
            "plan_lists":test_cases,
            "run_plan_name_list":data_name
        }
        RunPlanLists(settings)
        test_cases=settings.get("plan_lists")
        JSON_Control.WriteDictionary(output_filename, test_cases)
        #RunPlanLists(settings)
      
    # Python ファイルのテスト(クラスの初期化やメソッドのテストを含む)
    program_path = "./Sources/Common/Text.py"
    output_filename = "Test_py.json"
    data_name = "test"
    test_cases = write_test_cases(program_path, output_filename, data_name, type_inference_rules, options)
    if test_cases:
        settings ={
            "plan_lists":test_cases,
            "run_plan_name_list":data_name
        }
        RunPlanLists(settings)
        test_cases=settings.get("plan_lists")
        JSON_Control.WriteDictionary(output_filename, test_cases)
        #RunPlanLists(settings)
      
if __name__ == "__main__":
    main()

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?