From 4ed88e1694987e6bfb76d2f4546e7f246ae49528 Mon Sep 17 00:00:00 2001 From: "Jisung, Ahn" Date: Fri, 30 May 2025 10:36:31 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B0=9C=EC=84=A0:=20=EB=85=B8=EB=93=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=ED=99=94=20=EC=98=81=EB=AC=B8=ED=99=94=20?= =?UTF-8?q?=EB=B0=8F=20README=20=EC=B6=94=EA=B0=80,=20StringConcatenator?= =?UTF-8?q?=20=EB=85=B8=EB=93=9C=20=EB=93=B1=EB=A1=9D,=20NumberRangeIndex?= =?UTF-8?q?=20=EA=B8=B0=EB=B3=B8=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 112 +++++++++++++++ README_ko.md | 110 +++++++++++++++ TODO.txt | 15 ++ __init__.py | 8 +- boolean_index_adder.py | 23 ++- number_condition_checker.py | 16 ++- number_range_index.py | 100 ++++++++----- progress-2025-05-30.md | 30 ++++ string_concatenator.py | 85 +++++++++++ switch_by_number.py | 30 +++- test_boolean_index_adder.py | 79 +++++++++++ test_number_condition_checker.py | 212 ++++++++++++++++++++++++++++ test_number_range_index.py | 179 +++++++++++++++++++++++ test_number_range_index_boundary.py | 76 ++++++++++ test_number_range_index_debug.py | 46 ++++++ test_number_range_index_issue.py | 45 ++++++ test_string_concatenator.py | 71 ++++++++++ test_switch_by_number.py | 88 ++++++++++++ 18 files changed, 1276 insertions(+), 49 deletions(-) create mode 100644 README.md create mode 100644 README_ko.md create mode 100644 TODO.txt create mode 100644 progress-2025-05-30.md create mode 100644 string_concatenator.py create mode 100644 test_boolean_index_adder.py create mode 100644 test_number_condition_checker.py create mode 100644 test_number_range_index.py create mode 100644 test_number_range_index_boundary.py create mode 100644 test_number_range_index_debug.py create mode 100644 test_number_range_index_issue.py create mode 100644 test_string_concatenator.py create mode 100644 test_switch_by_number.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..a1f5c1b --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +# ComfyUI Logic Support + +A collection of logic and utility nodes for ComfyUI to enhance workflow capabilities. + +[한국어 README](README_ko.md) + +## Overview + +ComfyUI Logic Support is a custom node extension that provides various logic and utility nodes to enhance your ComfyUI workflows. These nodes enable more complex conditional logic, string manipulation, and numerical operations. + +## Installation + +1. Clone this repository into your ComfyUI custom_nodes directory: + ```bash + cd /path/to/ComfyUI/custom_nodes + git clone https://github.com/yourusername/Comfyui-Logic-Support.git + ``` + +2. Restart ComfyUI or refresh the page if it's already running. + +## Nodes + +### NumberRangeIndex + +Determines which range a number belongs to and returns the corresponding index. + +- **Inputs**: + - `number`: The input number to check + - `base_type`: Range calculation method ("L <= n < R" or "L < n <= R") + - `value_1` to `value_15`: Boundary values for ranges + - `default_index`: Starting index for results (default: 0) + +- **Outputs**: + - `INDEX`: The index of the range the number belongs to + +### BooleanIndexAdder + +Adds the index of the first true value among 15 boolean inputs to a base number. + +- **Inputs**: + - `base_number`: The number to add the index to + - `bool1` to `bool15`: Boolean inputs + +- **Outputs**: + - `NUMBER`: The result of adding the index to the base number + +### NumberConditionChecker + +Checks if a number is within specified bounds and returns a boolean result. + +- **Inputs**: + - `lower_value`: Lower bound value + - `lower_op`: Lower bound operator ("NONE", "<", "<=") + - `number`: The number to check + - `upper_op`: Upper bound operator ("NONE", "<", "<=") + - `upper_value`: Upper bound value + +- **Outputs**: + - `BOOLEAN`: True if the number satisfies all conditions, otherwise False + +### StringConcatenator + +Concatenates up to 15 input strings into a single output string with a specified separator. + +- **Inputs**: + - `separator_type`: Type of separator ("EMPTY", "COMMA", "SPACE", "PIPE") + - `string1` to `string15`: Strings to concatenate + +- **Outputs**: + - `STRING`: The concatenated string + +### StringSwitchByNumber + +Outputs a string based on a numeric index input, supporting up to 15 strings. + +- **Inputs**: + - `number`: Index of the string to output (1-based) + - `string1` to `string15`: Available strings + +- **Outputs**: + - `STRING`: The selected string + +## Examples + +### Using NumberRangeIndex for Conditional Processing + +``` +[Number Input: 15] → [NumberRangeIndex] → [Index determines further processing] +``` + +### Creating Dynamic Text with StringConcatenator + +``` +[Text Input 1: "Hello"] → [StringConcatenator: SPACE] → [Output: "Hello World"] +[Text Input 2: "World"] ↗ +``` + +### Conditional Logic with BooleanIndexAdder + +``` +[Base Number: 10] → [BooleanIndexAdder] → [Output: 12] +[Boolean Input 1: False] ↗ +[Boolean Input 2: True] ↗ +``` + +## License + +MIT License + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. diff --git a/README_ko.md b/README_ko.md new file mode 100644 index 0000000..139711e --- /dev/null +++ b/README_ko.md @@ -0,0 +1,110 @@ +# ComfyUI Logic Support + +ComfyUI 워크플로우 기능을 향상시키기 위한 로직 및 유틸리티 노드 모음입니다. + +## 개요 + +ComfyUI Logic Support는 ComfyUI 워크플로우를 향상시키기 위한 다양한 로직 및 유틸리티 노드를 제공하는 커스텀 노드 확장입니다. 이 노드들을 통해 더 복잡한 조건부 로직, 문자열 조작 및 숫자 연산을 수행할 수 있습니다. + +## 설치 방법 + +1. 이 저장소를 ComfyUI의 custom_nodes 디렉토리에 클론합니다: + ```bash + cd /path/to/ComfyUI/custom_nodes + git clone https://github.com/yourusername/Comfyui-Logic-Support.git + ``` + +2. ComfyUI를 재시작하거나, 이미 실행 중이라면 페이지를 새로고침합니다. + +## 노드 설명 + +### NumberRangeIndex + +입력된 숫자가 어떤 범위에 속하는지 확인하고 해당 범위의 인덱스를 출력합니다. + +- **입력**: + - `number`: 확인할 입력 숫자 + - `base_type`: 범위 계산 방식 ("L <= n < R" 또는 "L < n <= R") + - `value_1`부터 `value_15`: 범위의 경계값 + - `default_index`: 결과의 시작 인덱스 (기본값: 0) + +- **출력**: + - `INDEX`: 숫자가 속한 범위의 인덱스 + +### BooleanIndexAdder + +15개의 boolean 값 중 true인 첫 번째 값의 인덱스를 입력 숫자에 더합니다. + +- **입력**: + - `base_number`: 인덱스를 더할 기본 숫자 + - `bool1`부터 `bool15`: Boolean 입력값 + +- **출력**: + - `NUMBER`: 인덱스를 기본 숫자에 더한 결과 + +### NumberConditionChecker + +입력된 숫자가 지정된 범위 안에 들어오면 true, 아니면 false를 출력합니다. + +- **입력**: + - `lower_value`: 하한값 + - `lower_op`: 하한 연산자 ("NONE", "<", "<=") + - `number`: 확인할 숫자 + - `upper_op`: 상한 연산자 ("NONE", "<", "<=") + - `upper_value`: 상한값 + +- **출력**: + - `BOOLEAN`: 숫자가 모든 조건을 만족하면 True, 그렇지 않으면 False + +### StringConcatenator + +최대 15개의 문자열을 입력받아 지정된 구분자로 연결하여 하나의 문자열로 출력합니다. + +- **입력**: + - `separator_type`: 구분자 유형 ("EMPTY", "COMMA", "SPACE", "PIPE") + - `string1`부터 `string15`: 연결할 문자열 + +- **출력**: + - `STRING`: 연결된 문자열 + +### StringSwitchByNumber + +숫자 입력에 따라 해당 인덱스의 문자열을 출력합니다. 최대 15개의 문자열을 지원합니다. + +- **입력**: + - `number`: 출력할 문자열의 인덱스 (1부터 시작) + - `string1`부터 `string15`: 사용 가능한 문자열 + +- **출력**: + - `STRING`: 선택된 문자열 + +## 사용 예시 + +### NumberRangeIndex를 이용한 조건부 처리 + +``` +[숫자 입력: 15] → [NumberRangeIndex] → [인덱스에 따른 추가 처리] +``` + +### StringConcatenator를 이용한 동적 텍스트 생성 + +``` +[텍스트 입력 1: "안녕하세요"] → [StringConcatenator: SPACE] → [출력: "안녕하세요 세계"] +[텍스트 입력 2: "세계"] ↗ +``` + +### BooleanIndexAdder를 이용한 조건부 로직 + +``` +[기본 숫자: 10] → [BooleanIndexAdder] → [출력: 12] +[Boolean 입력 1: False] ↗ +[Boolean 입력 2: True] ↗ +``` + +## 라이센스 + +MIT License + +## 기여하기 + +기여는 언제나 환영합니다! Pull Request를 제출해 주세요. diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..e55de2a --- /dev/null +++ b/TODO.txt @@ -0,0 +1,15 @@ +# 다음 작업 계획 + +## 기능 개선 +1. 다른 노드들의 기본값 설정 검토 - 사용자 친화적인 기본값으로 조정 +2. 문서화 개선 - 각 노드의 기능과 사용법에 대한 상세한 설명 추가 +3. 에러 처리 강화 - 잘못된 입력에 대한 더 명확한 오류 메시지 제공 + +## 테스트 개선 +1. 엣지 케이스 테스트 추가 - 극단적인 값이나 특수한 상황에 대한 테스트 케이스 추가 +2. 통합 테스트 추가 - 여러 노드를 함께 사용하는 시나리오에 대한 테스트 추가 + +## 새로운 기능 아이디어 +1. 문자열 처리 기능 확장 - 정규식 지원, 문자열 변환 등 +2. 숫자 연산 노드 추가 - 복잡한 수학적 연산을 지원하는 노드 개발 +3. 조건부 실행 노드 - 특정 조건에 따라 다른 노드를 실행할 수 있는 제어 노드 개발 diff --git a/__init__.py b/__init__.py index d08529b..db18fd3 100644 --- a/__init__.py +++ b/__init__.py @@ -6,19 +6,23 @@ from .boolean_index_adder import NODE_CLASS_MAPPINGS as BOOLEAN_NODE_CLASS_MAPPI from .boolean_index_adder import NODE_DISPLAY_NAME_MAPPINGS as BOOLEAN_NODE_DISPLAY_NAME_MAPPINGS from .number_range_index import NODE_CLASS_MAPPINGS as RANGE_NODE_CLASS_MAPPINGS from .number_range_index import NODE_DISPLAY_NAME_MAPPINGS as RANGE_NODE_DISPLAY_NAME_MAPPINGS +from .string_concatenator import NODE_CLASS_MAPPINGS as CONCAT_NODE_CLASS_MAPPINGS +from .string_concatenator import NODE_DISPLAY_NAME_MAPPINGS as CONCAT_NODE_DISPLAY_NAME_MAPPINGS NODE_CLASS_MAPPINGS = { **SWITCH_NODE_CLASS_MAPPINGS, **CONDITION_NODE_CLASS_MAPPINGS, **BOOLEAN_NODE_CLASS_MAPPINGS, - **RANGE_NODE_CLASS_MAPPINGS + **RANGE_NODE_CLASS_MAPPINGS, + **CONCAT_NODE_CLASS_MAPPINGS } NODE_DISPLAY_NAME_MAPPINGS = { **SWITCH_NODE_DISPLAY_NAME_MAPPINGS, **CONDITION_NODE_DISPLAY_NAME_MAPPINGS, **BOOLEAN_NODE_DISPLAY_NAME_MAPPINGS, - **RANGE_NODE_DISPLAY_NAME_MAPPINGS + **RANGE_NODE_DISPLAY_NAME_MAPPINGS, + **CONCAT_NODE_DISPLAY_NAME_MAPPINGS } __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] diff --git a/boolean_index_adder.py b/boolean_index_adder.py index 581924e..45d26f5 100644 --- a/boolean_index_adder.py +++ b/boolean_index_adder.py @@ -1,6 +1,17 @@ class BooleanIndexAdder: """ - Boolean 인덱스 더하기 노드: 10개의 boolean 값 중 true인 첫 번째 값의 인덱스를 입력 숫자에 더합니다. + Boolean Index Adder Node: Adds the index of the first true value among 15 boolean inputs to a base number. + + This node takes up to 15 boolean inputs and a base number. It finds the first boolean input that is true + and adds its index (1-based) to the base number. If no boolean input is true, it returns the base number unchanged. + + The node is useful for creating conditional logic flows where different paths need to be selected based on + boolean conditions, and the result needs to be used as an index or identifier. + + Usage examples: + - With base_number=10 and bool1=False, bool2=True, bool3=False: returns 10+2=12 + - With base_number=5 and all boolean inputs False: returns 5 + - With base_number=0 and bool4=True: returns 0+4=4 """ @classmethod @@ -18,6 +29,11 @@ class BooleanIndexAdder: "bool8": ("BOOLEAN", {"default": False}), "bool9": ("BOOLEAN", {"default": False}), "bool10": ("BOOLEAN", {"default": False}), + "bool11": ("BOOLEAN", {"default": False}), + "bool12": ("BOOLEAN", {"default": False}), + "bool13": ("BOOLEAN", {"default": False}), + "bool14": ("BOOLEAN", {"default": False}), + "bool15": ("BOOLEAN", {"default": False}), }, } @@ -27,10 +43,11 @@ class BooleanIndexAdder: CATEGORY = "Logic-Support" def add_index(self, base_number, bool1, bool2, bool3, bool4, bool5, - bool6, bool7, bool8, bool9, bool10): + bool6, bool7, bool8, bool9, bool10, bool11, bool12, bool13, bool14, bool15): # 모든 boolean 값을 리스트로 모음 bools = [bool1, bool2, bool3, bool4, bool5, - bool6, bool7, bool8, bool9, bool10] + bool6, bool7, bool8, bool9, bool10, + bool11, bool12, bool13, bool14, bool15] # true인 첫 번째 값의 인덱스 찾기 (1부터 시작) index_to_add = 0 diff --git a/number_condition_checker.py b/number_condition_checker.py index eff190f..0448c73 100644 --- a/number_condition_checker.py +++ b/number_condition_checker.py @@ -1,6 +1,20 @@ class NumberConditionChecker: """ - 숫자 범위 체크 노드: 입력된 숫자가 지정된 범위 안에 들어오면 true, 아니면 false를 출력합니다. + Number Condition Checker Node: Checks if a number is within specified bounds and returns a boolean result. + + This node evaluates whether an input number satisfies specified conditions using lower and upper bounds. + The conditions can be configured using operators: + - Lower bound: NONE, < (less than), <= (less than or equal to) + - Upper bound: NONE, < (less than), <= (less than or equal to) + + When a bound is set to "NONE", that particular bound check is ignored. + + The node returns TRUE if the number satisfies all specified conditions, otherwise FALSE. + + Usage examples: + - Check if a number is between 0 and 10: lower_value=0, lower_op="<=", number=5, upper_op="<", upper_value=10 → TRUE + - Check if a number is positive: lower_value=0, lower_op="<", number=5, upper_op="NONE", upper_value=0 → TRUE + - Check if a number is at most 100: lower_value=0, lower_op="NONE", number=75, upper_op="<=", upper_value=100 → TRUE """ @classmethod diff --git a/number_range_index.py b/number_range_index.py index 5062991..c884beb 100644 --- a/number_range_index.py +++ b/number_range_index.py @@ -1,7 +1,26 @@ class NumberRangeIndex: """ - 숫자 범위 인덱스 노드: 입력된 숫자가 어떤 범위에 속하는지 확인하고 해당 범위의 인덱스를 출력합니다. - 숫자 범위는 자동으로 계산되며, 범위의 수는 입력된 경계값의 수에 따라 결정됩니다. + Number Range Index Node: Determines which range a number belongs to and returns the corresponding index. + + This node checks which range the input number belongs to among the specified boundary values and returns the index of that range. + Up to 15 boundary values can be specified, and only non-zero values are treated as valid boundaries. + The boundary values are automatically sorted to calculate the ranges. + + There are two methods for calculating ranges: + 1. L <= n < R: Range from lower bound (inclusive) to upper bound (exclusive) + 2. L < n <= R: Range from lower bound (exclusive) to upper bound (inclusive) + + The default_index parameter determines the starting point of the result value. The default is 0. + For values that do not belong to any range, default_index is returned. + For values that belong to a range, default_index + range index is returned. + + Usage examples: + - With boundary values [10, 20, 30] and base_type "L <= n < R": + - Input value 5 -> default_index (not in any range) + - Input value 10 -> default_index + 0 (first range) + - Input value 15 -> default_index + 0 (first range) + - Input value 20 -> default_index + 1 (second range) + - Input value 35 -> default_index + 2 (beyond the last range) """ @classmethod @@ -9,8 +28,8 @@ class NumberRangeIndex: return { "required": { "number": ("INT", {"default": 5}), - "base_type": (["base 0", "base 1"], {"default": "base 0"}), - "value_1": ("INT", {"default": 0}), + "base_type": (["L <= n < R", "L < n <= R"], {"default": "L <= n < R"}), + "value_1": ("INT", {"default": 1}), "value_2": ("INT", {"default": 10}), "value_3": ("INT", {"default": 20}), "value_4": ("INT", {"default": 30}), @@ -25,7 +44,7 @@ class NumberRangeIndex: "value_13": ("INT", {"default": 120}), "value_14": ("INT", {"default": 130}), "value_15": ("INT", {"default": 140}), - "default_index": ("INT", {"default": -1}), + "default_index": ("INT", {"default": 0}), }, } @@ -38,52 +57,57 @@ class NumberRangeIndex: value_1, value_2, value_3, value_4, value_5, value_6, value_7, value_8, value_9, value_10, value_11, value_12, value_13, value_14, value_15, - default_index): - # 경계값들을 리스트로 정리 + default_index=0): + # 경계값 목록 생성 values = [value_1, value_2, value_3, value_4, value_5, value_6, value_7, value_8, value_9, value_10, value_11, value_12, value_13, value_14, value_15] - - # 유효한 경계값만 필터링 (0이 아닌 값만 포함) - filtered_values = [v for v in values if v != 0] - - # 경계값이 없으면 기본 인덱스 반환 + # 중복 제거 및 정렬 + # 0도 유효한 경계값으로 처리 + filtered_values = list(set(values)) + # 경계값가 없는 경우 처리 if not filtered_values: return (default_index,) - # 경계값 정렬 + # 경계값 오름차순 정렬 filtered_values.sort() - # base_type에 따라 범위 체크 방식 결정 - is_base_0 = base_type == "base 0" + # 기준 타입 확인 (L <= n < R 또는 L < n <= R) + is_base_0 = base_type == "L <= n < R" - # 마지막 경계값보다 크면 마지막 인덱스 반환 - if number >= filtered_values[-1]: - if is_base_0: - return (len(filtered_values) - 1,) - else: - return (len(filtered_values),) - - # 각 범위 체크 - for i in range(len(filtered_values) - 1): - # 현재 경계값과 다음 경계값 사이에 있는지 확인 - if is_base_0: - # base 0: value_i <= number < value_i+1 - if filtered_values[i] <= number < filtered_values[i+1]: - return (i,) - else: - # base 1: value_i < number <= value_i+1 - if filtered_values[i] < number <= filtered_values[i+1]: - return (i+1,) + # 범위 인덱스 계산 + range_index = default_index # 기본값으로 초기화 # 첫 번째 경계값보다 작은 경우 if number < filtered_values[0]: - if is_base_0: - return (0,) # base 0에서는 처음 범위에 포함 - else: - return (default_index,) # base 1에서는 어떤 범위에도 포함되지 않음 + return (default_index,) # 어떤 범위에도 속하지 않음 - # 어떤 범위에도 속하지 않으면 기본 인덱스 반환 + # 마지맅 경계값보다 큰 경우 + if number > filtered_values[-1]: + # 마지맅 범위에 속함 + if is_base_0: + return (default_index + len(filtered_values) - 1,) # L <= n < R + else: + return (default_index + len(filtered_values),) # L < n <= R + + # 경계값과 정확히 일치하는 경우 처리 + for i, val in enumerate(filtered_values): + if number == val: + if is_base_0 or i > 0: # L <= n < R 또는 첫 번째가 아닌 경계값인 경우 + return (default_index + i,) + else: # L < n <= R이고 첫 번째 경계값인 경우 + return (default_index,) + + # 경계값 사이에 있는 경우 + for i in range(len(filtered_values) - 1): + if is_base_0: # L <= n < R + if filtered_values[i] <= number < filtered_values[i+1]: + return (default_index + i,) + else: # L < n <= R + if filtered_values[i] < number <= filtered_values[i+1]: + return (default_index + i+1,) + + # 어떤 범위에도 포함되지 않는 경우 (이 부분은 실행되지 않아야 함) return (default_index,) # 더 이상 필요하지 않음 diff --git a/progress-2025-05-30.md b/progress-2025-05-30.md new file mode 100644 index 0000000..1d3791e --- /dev/null +++ b/progress-2025-05-30.md @@ -0,0 +1,30 @@ +# 2025-05-30 진행 상황 + +## 완료된 작업 + +### 1. NumberRangeIndex 클래스 개선 +- `default_index`의 기본값을 -1에서 0으로 변경하여 프로그래머들에게 더 친숙한 인덱싱 방식 제공 +- 0도 유효한 경계값으로 처리하도록 코드 수정 +- 범위 인덱스 계산 로직을 완전히 재작성하여 더 명확하고 일관된 결과 제공 +- 테스트 케이스 추가 및 수정하여 모든 기능이 올바르게 작동하는지 확인 + +### 2. 전체 테스트 실행 결과 +- `NumberRangeIndex`: 모든 테스트 통과 +- `BooleanIndexAdder`: 모든 테스트 통과 +- `StringSwitchByNumber`: 모든 테스트 통과 +- `StringConcatenator`: 모든 테스트 통과 +- `NumberConditionChecker`: 모든 테스트 통과 + +## 주요 변경 사항 + +### NumberRangeIndex 클래스 개선 +1. `default_index`가 결과값의 시작점이 되도록 수정 +2. 범위에 속하지 않는 값에 대해서는 `default_index`가 반환되도록 구현 +3. 범위에 속하는 값에 대해서는 `default_index + 범위 인덱스`가 반환되도록 구현 +4. 0도 유효한 경계값으로 처리하도록 수정 + +## 테스트 케이스 추가 +1. `test_number_range_index_boundary.py`: 경계값 테스트 케이스 추가 +2. `test_number_range_index_debug.py`: 디버깅용 테스트 케이스 추가 + +모든 테스트가 성공적으로 통과하여 코드가 의도한 대로 작동하는 것을 확인했습니다. diff --git a/string_concatenator.py b/string_concatenator.py new file mode 100644 index 0000000..aa3e71b --- /dev/null +++ b/string_concatenator.py @@ -0,0 +1,85 @@ +class StringConcatenator: + """ + String Concatenator Node: Concatenates up to 15 input strings into a single output string with a specified separator. + + This node takes up to 15 string inputs and joins them together using a specified separator. + Empty strings are automatically filtered out before concatenation. + + Available separator types: + - EMPTY: No separator (direct concatenation) + - COMMA: Comma separator ", " + - SPACE: Space separator " " + - PIPE: Pipe separator " | " + + The node is useful for creating dynamic text combinations, building prompts, or generating formatted output strings. + + Usage examples: + - With separator_type="COMMA" and string1="Hello", string2="World": returns "Hello, World" + - With separator_type="SPACE" and string1="ComfyUI", string2="Logic", string3="Support": returns "ComfyUI Logic Support" + - With separator_type="EMPTY" and string1="prefix_", string2="suffix": returns "prefix_suffix" + """ + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "separator_type": (["EMPTY", "COMMA", "SPACE", "PIPE"], {"default": "EMPTY"}), + "string1": ("STRING", {"default": ""}), + "string2": ("STRING", {"default": ""}), + "string3": ("STRING", {"default": ""}), + "string4": ("STRING", {"default": ""}), + "string5": ("STRING", {"default": ""}), + }, + "optional": { + "string6": ("STRING", {"default": ""}), + "string7": ("STRING", {"default": ""}), + "string8": ("STRING", {"default": ""}), + "string9": ("STRING", {"default": ""}), + "string10": ("STRING", {"default": ""}), + "string11": ("STRING", {"default": ""}), + "string12": ("STRING", {"default": ""}), + "string13": ("STRING", {"default": ""}), + "string14": ("STRING", {"default": ""}), + "string15": ("STRING", {"default": ""}), + } + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = "CONCATENATED_STRING" + FUNCTION = "concatenate" + CATEGORY = "Logic-Support" + + def concatenate(self, string1, string2, string3, string4, string5, + string6="", string7="", string8="", string9="", string10="", + string11="", string12="", string13="", string14="", string15="", + separator_type="EMPTY"): + # 모든 문자열을 리스트로 모음 + strings = [string1, string2, string3, string4, string5, + string6, string7, string8, string9, string10, + string11, string12, string13, string14, string15] + + # 빈 문자열이 아닌 것만 필터링 + filtered_strings = [s for s in strings if s] + + # 구분자 타입에 따라 적절한 구분자 선택 + separator = "" + if separator_type == "COMMA": + separator = "," + elif separator_type == "SPACE": + separator = " " + elif separator_type == "PIPE": + separator = "|" + # EMPTY는 기본값인 공백 문자열 사용 + + # 구분자를 사용하여 문자열 연결 + result = separator.join(filtered_strings) + + return (result,) + +NODE_CLASS_MAPPINGS = { + "StringConcatenator": StringConcatenator +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "StringConcatenator": "String Concatenator" +} diff --git a/switch_by_number.py b/switch_by_number.py index f11398e..bcb21a7 100644 --- a/switch_by_number.py +++ b/switch_by_number.py @@ -1,13 +1,26 @@ class StringSwitchByNumber: """ - 문자열 스위치 노드: 숫자 입력에 따라 해당 인덱스의 문자열을 출력합니다. + String Switch By Number Node: Outputs a string based on a numeric index input, supporting up to 15 strings. + + This node selects and outputs one of the input strings based on the provided numeric index. + The index is 1-based, meaning index 1 selects string1, index 2 selects string2, and so on. + + If the provided index is out of range (less than 1 or greater than 15), the node defaults to returning string1. + + The node is useful for creating dynamic content selection, implementing conditional text outputs, + or building decision-based workflows where different text options need to be selected. + + Usage examples: + - With number=2 and string1="Option A", string2="Option B", string3="Option C": returns "Option B" + - With number=5 and five different strings configured: returns the fifth string + - With number=0 (out of range): returns string1 as the default """ @classmethod def INPUT_TYPES(cls): return { "required": { - "number": ("INT", {"default": 1, "min": 1, "max": 10, "step": 1}), + "number": ("INT", {"default": 1, "min": 1, "max": 15, "step": 1}), "string1": ("STRING", {"default": "문자열1"}), "string2": ("STRING", {"default": "문자열2"}), "string3": ("STRING", {"default": "문자열3"}), @@ -18,6 +31,11 @@ class StringSwitchByNumber: "string8": ("STRING", {"default": "문자열8"}), "string9": ("STRING", {"default": "문자열9"}), "string10": ("STRING", {"default": "문자열10"}), + "string11": ("STRING", {"default": "문자열11"}), + "string12": ("STRING", {"default": "문자열12"}), + "string13": ("STRING", {"default": "문자열13"}), + "string14": ("STRING", {"default": "문자열14"}), + "string15": ("STRING", {"default": "문자열15"}), }, } @@ -27,14 +45,16 @@ class StringSwitchByNumber: CATEGORY = "Logic-Support" def switch(self, number, string1, string2, string3, string4, string5, - string6, string7, string8, string9, string10): + string6, string7, string8, string9, string10, + string11, string12, string13, string14, string15): # 숫자 값이 범위를 벗어나면 기본값으로 1 사용 - if number < 1 or number > 10: + if number < 1 or number > 15: number = 1 # 숫자에 따라 해당 문자열 반환 strings = [string1, string2, string3, string4, string5, - string6, string7, string8, string9, string10] + string6, string7, string8, string9, string10, + string11, string12, string13, string14, string15] selected_string = strings[number - 1] # 인덱스는 0부터 시작하므로 -1 처리 diff --git a/test_boolean_index_adder.py b/test_boolean_index_adder.py new file mode 100644 index 0000000..49ddfb0 --- /dev/null +++ b/test_boolean_index_adder.py @@ -0,0 +1,79 @@ +import unittest +from boolean_index_adder import BooleanIndexAdder + +class TestBooleanIndexAdder(unittest.TestCase): + def setUp(self): + self.node = BooleanIndexAdder() + + def test_no_true_values(self): + # 모든 boolean 값이 False인 경우, base_number가 그대로 반환되어야 함 + result = self.node.add_index( + base_number=10, + bool1=False, bool2=False, bool3=False, bool4=False, bool5=False, + bool6=False, bool7=False, bool8=False, bool9=False, bool10=False, + bool11=False, bool12=False, bool13=False, bool14=False, bool15=False + ) + self.assertEqual(result[0], 10, "모든 값이 False일 때 base_number가 그대로 반환되어야 함") + + def test_first_true_value(self): + # 첫 번째 값이 True인 경우, base_number + 1이 반환되어야 함 + result = self.node.add_index( + base_number=10, + bool1=True, bool2=False, bool3=False, bool4=False, bool5=False, + bool6=False, bool7=False, bool8=False, bool9=False, bool10=False, + bool11=False, bool12=False, bool13=False, bool14=False, bool15=False + ) + self.assertEqual(result[0], 11, "첫 번째 값이 True일 때 base_number + 1이 반환되어야 함") + + def test_middle_true_value(self): + # 중간 값이 True인 경우, base_number + 해당 인덱스가 반환되어야 함 + result = self.node.add_index( + base_number=10, + bool1=False, bool2=False, bool3=False, bool4=False, bool5=False, + bool6=False, bool7=True, bool8=False, bool9=False, bool10=False, + bool11=False, bool12=False, bool13=False, bool14=False, bool15=False + ) + self.assertEqual(result[0], 17, "7번째 값이 True일 때 base_number + 7이 반환되어야 함") + + def test_last_true_value(self): + # 마지막 값이 True인 경우, base_number + 15가 반환되어야 함 + result = self.node.add_index( + base_number=10, + bool1=False, bool2=False, bool3=False, bool4=False, bool5=False, + bool6=False, bool7=False, bool8=False, bool9=False, bool10=False, + bool11=False, bool12=False, bool13=False, bool14=False, bool15=True + ) + self.assertEqual(result[0], 25, "15번째 값이 True일 때 base_number + 15가 반환되어야 함") + + def test_multiple_true_values(self): + # 여러 값이 True인 경우, 첫 번째 True 값의 인덱스가 사용되어야 함 + result = self.node.add_index( + base_number=10, + bool1=False, bool2=False, bool3=True, bool4=False, bool5=True, + bool6=False, bool7=True, bool8=False, bool9=True, bool10=False, + bool11=True, bool12=False, bool13=True, bool14=False, bool15=True + ) + self.assertEqual(result[0], 13, "여러 값이 True일 때 첫 번째 True 값(3번째)의 인덱스가 사용되어야 함") + + def test_negative_base_number(self): + # base_number가 음수인 경우 + result = self.node.add_index( + base_number=-10, + bool1=False, bool2=False, bool3=False, bool4=True, bool5=False, + bool6=False, bool7=False, bool8=False, bool9=False, bool10=False, + bool11=False, bool12=False, bool13=False, bool14=False, bool15=False + ) + self.assertEqual(result[0], -6, "base_number가 -10이고 4번째 값이 True일 때 -10 + 4 = -6이 반환되어야 함") + + def test_added_boolean_values(self): + # 추가된 boolean 값(11-15)이 제대로 작동하는지 테스트 + result = self.node.add_index( + base_number=5, + bool1=False, bool2=False, bool3=False, bool4=False, bool5=False, + bool6=False, bool7=False, bool8=False, bool9=False, bool10=False, + bool11=False, bool12=True, bool13=False, bool14=False, bool15=False + ) + self.assertEqual(result[0], 17, "12번째 값이 True일 때 base_number + 12 = 5 + 12 = 17이 반환되어야 함") + +if __name__ == "__main__": + unittest.main() diff --git a/test_number_condition_checker.py b/test_number_condition_checker.py new file mode 100644 index 0000000..dacb605 --- /dev/null +++ b/test_number_condition_checker.py @@ -0,0 +1,212 @@ +import unittest +from number_condition_checker import NumberConditionChecker + +class TestNumberConditionChecker(unittest.TestCase): + def setUp(self): + self.node = NumberConditionChecker() + + def test_lower_bound_less_than(self): + # 하한 조건: < (미만) + # 테스트 케이스: 5 < 10 (참) + result = self.node.check_condition( + lower_value=5, + lower_op="<", + number=10, + upper_op="NONE", + upper_value=0 + ) + self.assertTrue(result[0], "5 < 10은 참이어야 함") + + # 테스트 케이스: 10 < 10 (거짓) + result = self.node.check_condition( + lower_value=10, + lower_op="<", + number=10, + upper_op="NONE", + upper_value=0 + ) + self.assertFalse(result[0], "10 < 10은 거짓이어야 함") + + # 테스트 케이스: 15 < 10 (거짓) + result = self.node.check_condition( + lower_value=15, + lower_op="<", + number=10, + upper_op="NONE", + upper_value=0 + ) + self.assertFalse(result[0], "15 < 10은 거짓이어야 함") + + def test_lower_bound_less_than_or_equal(self): + # 하한 조건: <= (이하) + # 테스트 케이스: 5 <= 10 (참) + result = self.node.check_condition( + lower_value=5, + lower_op="<=", + number=10, + upper_op="NONE", + upper_value=0 + ) + self.assertTrue(result[0], "5 <= 10은 참이어야 함") + + # 테스트 케이스: 10 <= 10 (참) + result = self.node.check_condition( + lower_value=10, + lower_op="<=", + number=10, + upper_op="NONE", + upper_value=0 + ) + self.assertTrue(result[0], "10 <= 10은 참이어야 함") + + # 테스트 케이스: 15 <= 10 (거짓) + result = self.node.check_condition( + lower_value=15, + lower_op="<=", + number=10, + upper_op="NONE", + upper_value=0 + ) + self.assertFalse(result[0], "15 <= 10은 거짓이어야 함") + + def test_upper_bound_less_than(self): + # 상한 조건: < (미만) + # 테스트 케이스: 5 < 10 (참) + result = self.node.check_condition( + lower_value=0, + lower_op="NONE", + number=5, + upper_op="<", + upper_value=10 + ) + self.assertTrue(result[0], "5 < 10은 참이어야 함") + + # 테스트 케이스: 10 < 10 (거짓) + result = self.node.check_condition( + lower_value=0, + lower_op="NONE", + number=10, + upper_op="<", + upper_value=10 + ) + self.assertFalse(result[0], "10 < 10은 거짓이어야 함") + + # 테스트 케이스: 15 < 10 (거짓) + result = self.node.check_condition( + lower_value=0, + lower_op="NONE", + number=15, + upper_op="<", + upper_value=10 + ) + self.assertFalse(result[0], "15 < 10은 거짓이어야 함") + + def test_upper_bound_less_than_or_equal(self): + # 상한 조건: <= (이하) + # 테스트 케이스: 5 <= 10 (참) + result = self.node.check_condition( + lower_value=0, + lower_op="NONE", + number=5, + upper_op="<=", + upper_value=10 + ) + self.assertTrue(result[0], "5 <= 10은 참이어야 함") + + # 테스트 케이스: 10 <= 10 (참) + result = self.node.check_condition( + lower_value=0, + lower_op="NONE", + number=10, + upper_op="<=", + upper_value=10 + ) + self.assertTrue(result[0], "10 <= 10은 참이어야 함") + + # 테스트 케이스: 15 <= 10 (거짓) + result = self.node.check_condition( + lower_value=0, + lower_op="NONE", + number=15, + upper_op="<=", + upper_value=10 + ) + self.assertFalse(result[0], "15 <= 10은 거짓이어야 함") + + def test_both_bounds(self): + # 하한과 상한 조건 모두 사용 + # 테스트 케이스: 5 <= 7 < 10 (참) + result = self.node.check_condition( + lower_value=5, + lower_op="<=", + number=7, + upper_op="<", + upper_value=10 + ) + self.assertTrue(result[0], "5 <= 7 < 10은 참이어야 함") + + # 테스트 케이스: 5 <= 5 < 10 (참) + result = self.node.check_condition( + lower_value=5, + lower_op="<=", + number=5, + upper_op="<", + upper_value=10 + ) + self.assertTrue(result[0], "5 <= 5 < 10은 참이어야 함") + + # 테스트 케이스: 5 <= 10 < 10 (거짓, 상한 조건 위반) + result = self.node.check_condition( + lower_value=5, + lower_op="<=", + number=10, + upper_op="<", + upper_value=10 + ) + self.assertFalse(result[0], "5 <= 10 < 10은 거짓이어야 함 (상한 조건 위반)") + + # 테스트 케이스: 5 <= 4 < 10 (거짓, 하한 조건 위반) + result = self.node.check_condition( + lower_value=5, + lower_op="<=", + number=4, + upper_op="<", + upper_value=10 + ) + self.assertFalse(result[0], "5 <= 4 < 10은 거짓이어야 함 (하한 조건 위반)") + + def test_no_bounds(self): + # 하한과 상한 조건 모두 NONE + result = self.node.check_condition( + lower_value=0, + lower_op="NONE", + number=100, + upper_op="NONE", + upper_value=0 + ) + self.assertTrue(result[0], "조건이 없을 때는 항상 참이어야 함") + + def test_negative_numbers(self): + # 음수 테스트 + # 테스트 케이스: -10 <= -5 < 0 (참) + result = self.node.check_condition( + lower_value=-10, + lower_op="<=", + number=-5, + upper_op="<", + upper_value=0 + ) + self.assertTrue(result[0], "-10 <= -5 < 0은 참이어야 함") + + # 테스트 케이스: -10 <= -15 < 0 (거짓, 하한 조건 위반) + result = self.node.check_condition( + lower_value=-10, + lower_op="<=", + number=-15, + upper_op="<", + upper_value=0 + ) + self.assertFalse(result[0], "-10 <= -15 < 0은 거짓이어야 함 (하한 조건 위반)") + +if __name__ == "__main__": + unittest.main() diff --git a/test_number_range_index.py b/test_number_range_index.py new file mode 100644 index 0000000..99dc650 --- /dev/null +++ b/test_number_range_index.py @@ -0,0 +1,179 @@ +import unittest +from number_range_index import NumberRangeIndex + +class TestNumberRangeIndex(unittest.TestCase): + def setUp(self): + self.node = NumberRangeIndex() + + def test_comprehensive_boundaries(self): + # 경계값: 0, 10, 15, 20, 25, 30, 35, 40, 50, 60, 80 + # 이 테스트에서는 다양한 입력값에 대해 "L <= n < R" 모드에서 올바른 인덱스를 반환하는지 확인합니다. + + # 경계값 설정 + boundaries = [0, 10, 15, 20, 25, 30, 35, 40, 50, 60, 80] + + # 테스트 케이스: (입력값, 예상 인덱스) + test_cases_base_0 = [ + (-5, 0), # 첫 번째 경계값보다 작은 경우 + (0, 0), # 첫 번째 경계값과 같은 경우 + (5, 0), # 첫 번째와 두 번째 경계값 사이 + (10, 1), # 두 번째 경계값과 같은 경우 + (12, 1), # 두 번째와 세 번째 경계값 사이 + (15, 2), # 세 번째 경계값과 같은 경우 + (18, 2), # 세 번째와 네 번째 경계값 사이 + (20, 3), # 네 번째 경계값과 같은 경우 + (22, 3), # 네 번째와 다섯 번째 경계값 사이 + (25, 4), # 다섯 번째 경계값과 같은 경우 + (27, 4), # 다섯 번째와 여섯 번째 경계값 사이 + (30, 5), # 여섯 번째 경계값과 같은 경우 + (32, 5), # 여섯 번째와 일곱 번째 경계값 사이 + (35, 6), # 일곱 번째 경계값과 같은 경우 + (38, 6), # 일곱 번째와 여덟 번째 경계값 사이 + (40, 7), # 여덟 번째 경계값과 같은 경우 + (45, 7), # 여덟 번째와 아홉 번째 경계값 사이 + (50, 8), # 아홉 번째 경계값과 같은 경우 + (55, 8), # 아홉 번째와 열 번째 경계값 사이 + (60, 9), # 열 번째 경계값과 같은 경우 + (70, 9), # 열 번째와 열한 번째 경계값 사이 + (80, 10), # 열한 번째 경계값과 같은 경우 + (100, 10), # 마지막 경계값보다 큰 경우 + ] + + # "L <= n < R" 모드 테스트 + for number, expected_index in test_cases_base_0: + result = self.node.get_range_index( + number=number, + base_type="L <= n < R", + value_1=boundaries[0], + value_2=boundaries[1], + value_3=boundaries[2], + value_4=boundaries[3], + value_5=boundaries[4], + value_6=boundaries[5], + value_7=boundaries[6], + value_8=boundaries[7], + value_9=boundaries[8], + value_10=boundaries[9], + value_11=boundaries[10], + value_12=0, + value_13=0, + value_14=0, + value_15=0, + default_index=0 + ) + self.assertEqual(result[0], expected_index, f"입력값 {number}에 대해 인덱스 {expected_index}가 예상되었지만 {result[0]}가 반환됨 (L <= n < R 모드)") + + # "L < n <= R" 모드 테스트 + test_cases_base_1 = [ + (-5, 0), # 첫 번째 경계값보다 작은 경우 (범위에 속하지 않음) + (0, 0), # 첫 번째 경계값과 같은 경우 (첫 번째 범위에 속함) + (5, 1), # 첫 번째와 두 번째 경계값 사이 (두 번째 범위에 속함) + (10, 1), # 두 번째 경계값과 같은 경우 (두 번째 범위에 속함) + (12, 2), # 두 번째와 세 번째 경계값 사이 (세 번째 범위에 속함) + (15, 2), # 세 번째 경계값과 같은 경우 (세 번째 범위에 속함) + (18, 3), # 세 번째와 네 번째 경계값 사이 (네 번째 범위에 속함) + (20, 3), # 네 번째 경계값과 같은 경우 (네 번째 범위에 속함) + (22, 4), # 네 번째와 다섯 번째 경계값 사이 (다섯 번째 범위에 속함) + (25, 4), # 다섯 번째 경계값과 같은 경우 (다섯 번째 범위에 속함) + (27, 5), # 다섯 번째와 여섯 번째 경계값 사이 (여섯 번째 범위에 속함) + (30, 5), # 여섯 번째 경계값과 같은 경우 (여섯 번째 범위에 속함) + (32, 6), # 여섯 번째와 일곱 번째 경계값 사이 (일곱 번째 범위에 속함) + (35, 6), # 일곱 번째 경계값과 같은 경우 (일곱 번째 범위에 속함) + (38, 7), # 일곱 번째와 여덟 번째 경계값 사이 (여덟 번째 범위에 속함) + (40, 7), # 여덟 번째 경계값과 같은 경우 (여덟 번째 범위에 속함) + (45, 8), # 여덟 번째와 아홉 번째 경계값 사이 (아홉 번째 범위에 속함) + (50, 8), # 아홉 번째 경계값과 같은 경우 (아홉 번째 범위에 속함) + (55, 9), # 아홉 번째와 열 번째 경계값 사이 (열 번째 범위에 속함) + (60, 9), # 열 번째 경계값과 같은 경우 (열 번째 범위에 속함) + (70, 10), # 열 번째와 열한 번째 경계값 사이 (열한 번째 범위에 속함) + (80, 10), # 열한 번째 경계값과 같은 경우 (열한 번째 범위에 속함) + (100, 11), # 마지막 경계값보다 큰 경우 (마지막 범위 다음) + ] + + for number, expected_index in test_cases_base_1: + result = self.node.get_range_index( + number=number, + base_type="L < n <= R", + value_1=boundaries[0], + value_2=boundaries[1], + value_3=boundaries[2], + value_4=boundaries[3], + value_5=boundaries[4], + value_6=boundaries[5], + value_7=boundaries[6], + value_8=boundaries[7], + value_9=boundaries[8], + value_10=boundaries[9], + value_11=boundaries[10], + value_12=0, + value_13=0, + value_14=0, + value_15=0, + default_index=0 + ) + self.assertEqual(result[0], expected_index, f"입력값 {number}에 대해 인덱스 {expected_index}가 예상되었지만 {result[0]}가 반환됨 (L < n <= R 모드)") + + def test_base0_mode(self): + # 테스트 케이스 1: 숫자 11, 경계값 [0, 10, 15, 20], "L <= n < R" 모드 + # 11은 10과 15 사이에 있으므로 인덱스 1이 반환되어야 함 + result = self.node.get_range_index( + number=11, + base_type="L <= n < R", + value_1=0, value_2=10, value_3=15, value_4=20, value_5=30, + value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=0 + ) + self.assertEqual(result[0], 1, "11은 10과 15 사이에 있으므로 인덱스 1이 반환되어야 함") + + # 테스트 케이스 2: 숫자 5, 경계값 [0, 10, 15, 20], "L <= n < R" 모드 + # 5는 0과 10 사이에 있으므로 인덱스 0이 반환되어야 함 + result = self.node.get_range_index( + number=5, + base_type="L <= n < R", + value_1=0, value_2=10, value_3=15, value_4=20, value_5=30, + value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=0 + ) + self.assertEqual(result[0], 0, "5는 0과 10 사이에 있으므로 인덱스 0이 반환되어야 함") + + # 테스트 케이스 3: 숫자 25, 경계값 [0, 10, 15, 20], "L <= n < R" 모드 + # 25는 20보다 크므로 마지막 인덱스 3이 반환되어야 함 + result = self.node.get_range_index( + number=25, + base_type="L <= n < R", + value_1=0, value_2=10, value_3=15, value_4=20, value_5=30, + value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=0 + ) + self.assertEqual(result[0], 3, "25는 20보다 크므로 마지막 인덱스 3이 반환되어야 함") + + def test_base1_mode(self): + # 테스트 케이스 4: 숫자 11, 경계값 [0, 10, 15, 20], "L < n <= R" 모드 + # 11은 10과 15 사이에 있으므로 인덱스 2가 반환되어야 함 + result = self.node.get_range_index( + number=11, + base_type="L < n <= R", + value_1=0, value_2=10, value_3=15, value_4=20, value_5=30, + value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=0 + ) + self.assertEqual(result[0], 2, "11은 10과 15 사이에 있으므로 인덱스 2가 반환되어야 함") + + # 테스트 케이스 5: 숫자 -5, 경계값 [0, 10, 15, 20], "L < n <= R" 모드 + # -5는 0보다 작으므로 default_index가 반환되어야 함 + result = self.node.get_range_index( + number=-5, + base_type="L < n <= R", + value_1=0, value_2=10, value_3=15, value_4=20, + value_5=0, value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=0 + ) + self.assertEqual(result[0], 0, "-5는 0보다 작으므로 default_index가 반환되어야 함") + +if __name__ == "__main__": + unittest.main() diff --git a/test_number_range_index_boundary.py b/test_number_range_index_boundary.py new file mode 100644 index 0000000..3c2c9d5 --- /dev/null +++ b/test_number_range_index_boundary.py @@ -0,0 +1,76 @@ +import unittest +from number_range_index import NumberRangeIndex + +class TestNumberRangeIndexBoundary(unittest.TestCase): + def setUp(self): + self.node = NumberRangeIndex() + + def test_boundary_values(self): + # "L <= n < R" 모드에서 경계값 [1, 10, 15, 20]을 사용하여 다양한 입력값 테스트 + + # 테스트 케이스: (입력값, 예상 인덱스) + test_cases = [ + (0, -1), # 첫 번째 경계값보다 작은 값 -> default_index (-1) + (1, 0), # 첫 번째 경계값과 같은 값 -> 0 + (5, 0), # 첫 번째와 두 번째 경계값 사이 -> 0 + (9, 0), # 두 번째 경계값 바로 전 -> 0 + (10, 1), # 두 번째 경계값과 같은 값 -> 1 + (11, 1), # 두 번째와 세 번째 경계값 사이 -> 1 + (14, 1), # 세 번째 경계값 바로 전 -> 1 + (15, 2), # 세 번째 경계값과 같은 값 -> 2 + (16, 2), # 세 번째와 네 번째 경계값 사이 -> 2 + (19, 2), # 네 번째 경계값 바로 전 -> 2 + (20, 3), # 네 번째 경계값과 같은 값 -> 3 + (25, 3), # 마지막 경계값보다 큰 값 -> 3 + ] + + # 각 테스트 케이스 실행 + for number, expected_index in test_cases: + result = self.node.get_range_index( + number=number, + base_type="L <= n < R", + value_1=1, value_2=10, value_3=15, value_4=20, + value_5=0, value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=-1 + ) + self.assertEqual(result[0], expected_index, + f"입력값 {number}에 대해 인덱스 {expected_index}가 예상되었지만 {result[0]}가 반환됨") + print(f"입력값 {number} -> 인덱스 {result[0]} (예상: {expected_index})") + + def test_boundary_values_with_index_1(self): + # "L <= n < R" 모드에서 경계값 [1, 10, 15, 20]을 사용하고 첫 번째 값을 인덱스 1로 설정 + # 입력값 1~9는 인덱스 1이 나오고, 10부터는 인덱스 2가 나오는지 테스트 + + # 테스트 케이스: (입력값, 예상 인덱스) + test_cases = [ + (0, 1), # 첫 번째 경계값보다 작은 값 -> default_index (1) + (1, 2), # 첫 번째 경계값과 같은 값 -> 1+1=2 + (5, 2), # 첫 번째와 두 번째 경계값 사이 -> 1+1=2 + (9, 2), # 두 번째 경계값 바로 전 -> 1+1=2 + (10, 3), # 두 번째 경계값과 같은 값 -> 2+1=3 + (11, 3), # 두 번째와 세 번째 경계값 사이 -> 2+1=3 + (14, 3), # 세 번째 경계값 바로 전 -> 2+1=3 + (15, 4), # 세 번째 경계값과 같은 값 -> 3+1=4 + (16, 4), # 세 번째와 네 번째 경계값 사이 -> 3+1=4 + (19, 4), # 네 번째 경계값 바로 전 -> 3+1=4 + (20, 5), # 네 번째 경계값과 같은 값 -> 4+1=5 + (25, 5), # 마지막 경계값보다 큰 값 -> 4+1=5 + ] + + # 각 테스트 케이스 실행 + for number, expected_index in test_cases: + result = self.node.get_range_index( + number=number, + base_type="L <= n < R", + value_1=1, value_2=10, value_3=15, value_4=20, + value_5=0, value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=1 # 기본 인덱스를 1로 설정 + ) + self.assertEqual(result[0], expected_index, + f"입력값 {number}에 대해 인덱스 {expected_index}가 예상되었지만 {result[0]}가 반환됨") + print(f"입력값 {number} -> 인덱스 {result[0]} (예상: {expected_index})") + +if __name__ == "__main__": + unittest.main() diff --git a/test_number_range_index_debug.py b/test_number_range_index_debug.py new file mode 100644 index 0000000..f3c27c2 --- /dev/null +++ b/test_number_range_index_debug.py @@ -0,0 +1,46 @@ +import unittest +from number_range_index import NumberRangeIndex + +class TestNumberRangeIndexDebug(unittest.TestCase): + def setUp(self): + self.node = NumberRangeIndex() + + def test_debug_values(self): + """ + 디버깅용 테스트: 다양한 입력값에 대한 결과를 출력합니다. + """ + # 테스트 케이스: (입력값, 경계값 목록, 기본 인덱스) + test_cases = [ + # 기본 인덱스 = -1 (기본값) + (0, [1, 10, 15, 20], -1), + (1, [1, 10, 15, 20], -1), + (5, [1, 10, 15, 20], -1), + (10, [1, 10, 15, 20], -1), + (15, [1, 10, 15, 20], -1), + (20, [1, 10, 15, 20], -1), + (25, [1, 10, 15, 20], -1), + + # 기본 인덱스 = 1 + (0, [1, 10, 15, 20], 1), + (1, [1, 10, 15, 20], 1), + (5, [1, 10, 15, 20], 1), + (10, [1, 10, 15, 20], 1), + (15, [1, 10, 15, 20], 1), + (20, [1, 10, 15, 20], 1), + (25, [1, 10, 15, 20], 1), + ] + + print("\n==== L <= n < R 모드 테스트 ====") + for number, values, default_index in test_cases: + result = self.node.get_range_index( + number=number, + base_type="L <= n < R", + value_1=values[0], value_2=values[1], value_3=values[2], value_4=values[3], + value_5=0, value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=default_index + ) + print(f"입력값: {number}, 경계값: {values}, 기본 인덱스: {default_index} => 결과: {result[0]}") + +if __name__ == "__main__": + unittest.main() diff --git a/test_number_range_index_issue.py b/test_number_range_index_issue.py new file mode 100644 index 0000000..a1c3f49 --- /dev/null +++ b/test_number_range_index_issue.py @@ -0,0 +1,45 @@ +import unittest +from number_range_index import NumberRangeIndex + +class TestNumberRangeIndexIssue(unittest.TestCase): + def setUp(self): + self.node = NumberRangeIndex() + + def test_specific_case(self): + # 테스트 케이스: 숫자 11, 경계값 [1, 10, 15, 20], "L <= n < R" 모드, default_index=0 + result = self.node.get_range_index( + number=11, + base_type="L <= n < R", + value_1=1, value_2=10, value_3=15, value_4=20, + value_5=0, value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=0 + ) + print(f"결과 (default_index=0): {result[0]}") + + # 테스트 케이스: 숫자 11, 경계값 [1, 10, 15, 20], "L <= n < R" 모드, default_index=1 + result = self.node.get_range_index( + number=11, + base_type="L <= n < R", + value_1=1, value_2=10, value_3=15, value_4=20, + value_5=0, value_6=0, value_7=0, value_8=0, value_9=0, value_10=0, + value_11=0, value_12=0, value_13=0, value_14=0, value_15=0, + default_index=1 + ) + print(f"결과 (default_index=1): {result[0]}") + + # 디버깅을 위한 코드 추가 + # 경계값 목록 확인 + values = [1, 10, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + filtered_values = [v for v in values if v != 0] + filtered_values.sort() + print(f"필터링된 경계값: {filtered_values}") + + # 11이 어느 범위에 속하는지 확인 + for i in range(len(filtered_values) - 1): + if filtered_values[i] <= 11 < filtered_values[i+1]: + print(f"11은 {filtered_values[i]}와 {filtered_values[i+1]} 사이에 있음 (인덱스 {i})") + print(f"원래 값 목록에서의 인덱스: {values.index(filtered_values[i])}") + +if __name__ == "__main__": + unittest.main() diff --git a/test_string_concatenator.py b/test_string_concatenator.py new file mode 100644 index 0000000..26a073e --- /dev/null +++ b/test_string_concatenator.py @@ -0,0 +1,71 @@ +import unittest +from string_concatenator import StringConcatenator + +class TestStringConcatenator(unittest.TestCase): + def setUp(self): + self.node = StringConcatenator() + + def test_concatenate_all_strings(self): + # 모든 문자열이 있는 경우 + result = self.node.concatenate( + string1="안녕", string2="하세요", string3="반갑", string4="습니다", string5="테스트", + string6="입니다", string7="문자열", string8="연결", string9="노드", string10="테스트", + string11="중", string12="입니다", string13="잘", string14="작동", string15="하네요", + separator_type="EMPTY" + ) + self.assertEqual(result[0], "안녕하세요반갑습니다테스트입니다문자열연결노드테스트중입니다잘작동하네요", + "모든 문자열이 EMPTY 구분자로 연결되어야 함") + + def test_concatenate_with_comma_separator(self): + # 구분자가 있는 경우 + result = self.node.concatenate( + string1="안녕", string2="하세요", string3="반갑", string4="습니다", string5="테스트", + string6="입니다", string7="문자열", string8="연결", string9="노드", string10="테스트", + string11="중", string12="입니다", string13="잘", string14="작동", string15="하네요", + separator_type="COMMA" + ) + self.assertEqual(result[0], "안녕,하세요,반갑,습니다,테스트,입니다,문자열,연결,노드,테스트,중,입니다,잘,작동,하네요", + "모든 문자열이 쉼표로 구분되어 연결되어야 함") + + def test_concatenate_with_pipe_separator(self): + # 일부 문자열이 비어있는 경우 + result = self.node.concatenate( + string1="첫 번째", string2="", string3="세 번째", string4="", string5="다섯 번째", + string6="", string7="일곱 번째", string8="", string9="아홉 번째", string10="", + string11="열한 번째", string12="", string13="열셋 번째", string14="", string15="열다섯 번째", + separator_type="PIPE" + ) + self.assertEqual(result[0], "첫 번째|세 번째|다섯 번째|일곱 번째|아홉 번째|열한 번째|열셋 번째|열다섯 번째", + "비어있지 않은 문자열만 파이프(|) 구분자로 연결되어야 함") + + def test_concatenate_only_required_strings(self): + # 필수 문자열만 있는 경우 + result = self.node.concatenate( + string1="첫 번째", string2="두 번째", string3="세 번째", string4="네 번째", string5="다섯 번째", + separator_type="SPACE" + ) + self.assertEqual(result[0], "첫 번째 두 번째 세 번째 네 번째 다섯 번째", + "필수 문자열만 공백 구분자로 연결되어야 함") + + def test_concatenate_single_string(self): + # 하나의 문자열만 있는 경우 + result = self.node.concatenate( + string1="외로운 문자열", string2="", string3="", string4="", string5="", + separator_type="COMMA" + ) + self.assertEqual(result[0], "외로운 문자열", + "하나의 문자열만 있는 경우 그대로 반환되어야 함") + + def test_concatenate_all_empty_strings(self): + # 모든 문자열이 비어있는 경우 + result = self.node.concatenate( + string1="", string2="", string3="", string4="", string5="", + string6="", string7="", string8="", string9="", string10="", + string11="", string12="", string13="", string14="", string15="", + separator_type="COMMA" + ) + self.assertEqual(result[0], "", + "모든 문자열이 비어있는 경우 빈 문자열이 반환되어야 함") + +if __name__ == "__main__": + unittest.main() diff --git a/test_switch_by_number.py b/test_switch_by_number.py new file mode 100644 index 0000000..5edcdc3 --- /dev/null +++ b/test_switch_by_number.py @@ -0,0 +1,88 @@ +import unittest +from switch_by_number import StringSwitchByNumber + +class TestStringSwitchByNumber(unittest.TestCase): + def setUp(self): + self.node = StringSwitchByNumber() + + def test_valid_number_selection(self): + # 유효한 범위 내의 숫자로 문자열 선택 테스트 + result = self.node.switch( + number=3, + string1="첫 번째", string2="두 번째", string3="세 번째", string4="네 번째", string5="다섯 번째", + string6="여섯 번째", string7="일곱 번째", string8="여덟 번째", string9="아홉 번째", string10="열 번째", + string11="열한 번째", string12="열두 번째", string13="열셋 번째", string14="열넷 번째", string15="열다섯 번째" + ) + self.assertEqual(result[0], "세 번째", "숫자 3을 입력했을 때 세 번째 문자열이 반환되어야 함") + + def test_out_of_range_low(self): + # 범위보다 작은 숫자 입력 시 기본값(1) 사용 테스트 + result = self.node.switch( + number=0, # 범위 밖(1보다 작음) + string1="첫 번째", string2="두 번째", string3="세 번째", string4="네 번째", string5="다섯 번째", + string6="여섯 번째", string7="일곱 번째", string8="여덟 번째", string9="아홉 번째", string10="열 번째", + string11="열한 번째", string12="열두 번째", string13="열셋 번째", string14="열넷 번째", string15="열다섯 번째" + ) + self.assertEqual(result[0], "첫 번째", "범위 밖 숫자(0)를 입력했을 때 기본값(1)에 해당하는 첫 번째 문자열이 반환되어야 함") + + def test_out_of_range_high(self): + # 범위보다 큰 숫자 입력 시 기본값(1) 사용 테스트 + result = self.node.switch( + number=20, # 범위 밖(15보다 큼) + string1="첫 번째", string2="두 번째", string3="세 번째", string4="네 번째", string5="다섯 번째", + string6="여섯 번째", string7="일곱 번째", string8="여덟 번째", string9="아홉 번째", string10="열 번째", + string11="열한 번째", string12="열두 번째", string13="열셋 번째", string14="열넷 번째", string15="열다섯 번째" + ) + self.assertEqual(result[0], "첫 번째", "범위 밖 숫자(20)를 입력했을 때 기본값(1)에 해당하는 첫 번째 문자열이 반환되어야 함") + + def test_first_string(self): + # 첫 번째 문자열 선택 테스트 + result = self.node.switch( + number=1, + string1="첫 번째", string2="두 번째", string3="세 번째", string4="네 번째", string5="다섯 번째", + string6="여섯 번째", string7="일곱 번째", string8="여덟 번째", string9="아홉 번째", string10="열 번째", + string11="열한 번째", string12="열두 번째", string13="열셋 번째", string14="열넷 번째", string15="열다섯 번째" + ) + self.assertEqual(result[0], "첫 번째", "숫자 1을 입력했을 때 첫 번째 문자열이 반환되어야 함") + + def test_last_string(self): + # 마지막(15번째) 문자열 선택 테스트 + result = self.node.switch( + number=15, + string1="첫 번째", string2="두 번째", string3="세 번째", string4="네 번째", string5="다섯 번째", + string6="여섯 번째", string7="일곱 번째", string8="여덟 번째", string9="아홉 번째", string10="열 번째", + string11="열한 번째", string12="열두 번째", string13="열셋 번째", string14="열넷 번째", string15="열다섯 번째" + ) + self.assertEqual(result[0], "열다섯 번째", "숫자 15를 입력했을 때 열다섯 번째 문자열이 반환되어야 함") + + def test_added_strings(self): + # 추가된 문자열(11-15) 선택 테스트 + test_cases = [ + (11, "열한 번째"), + (12, "열두 번째"), + (13, "열셋 번째"), + (14, "열넷 번째"), + (15, "열다섯 번째") + ] + + for number, expected in test_cases: + result = self.node.switch( + number=number, + string1="첫 번째", string2="두 번째", string3="세 번째", string4="네 번째", string5="다섯 번째", + string6="여섯 번째", string7="일곱 번째", string8="여덟 번째", string9="아홉 번째", string10="열 번째", + string11="열한 번째", string12="열두 번째", string13="열셋 번째", string14="열넷 번째", string15="열다섯 번째" + ) + self.assertEqual(result[0], expected, f"숫자 {number}를 입력했을 때 {expected}가 반환되어야 함") + + def test_empty_strings(self): + # 빈 문자열 처리 테스트 + result = self.node.switch( + number=5, + string1="", string2="", string3="", string4="", string5="비어있지 않은 문자열", + string6="", string7="", string8="", string9="", string10="", + string11="", string12="", string13="", string14="", string15="" + ) + self.assertEqual(result[0], "비어있지 않은 문자열", "숫자 5를 입력했을 때 다섯 번째 문자열이 반환되어야 함") + +if __name__ == "__main__": + unittest.main()