From e3eb1c8d48ee5c4527f1bf95a1bae6176c579f0c Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Sun, 12 Oct 2025 11:30:53 +0200 Subject: [PATCH] wip --- dist/text_texture_generator_v1.0.0.zip | Bin 9671271 -> 9671460 bytes dist/text_texture_generator_v1.0.0_free.zip | Bin 9654555 -> 9654555 bytes src/__pycache__/__init__.cpython-311.pyc | Bin 13238 -> 13352 bytes src/operators/text_editor_ops.py | 67 ++++++--- .../__pycache__/constants.cpython-311.pyc | Bin 7431 -> 7431 bytes tests/unit/operators/test_text_editor_ops.py | 140 ++++++++++++------ 6 files changed, 136 insertions(+), 71 deletions(-) diff --git a/dist/text_texture_generator_v1.0.0.zip b/dist/text_texture_generator_v1.0.0.zip index b6062fa1d9ca3c05f90f55ca50ae6826bf704ec6..de35f37a72de708437e8a45e87b2775e6b79ad12 100644 GIT binary patch delta 11849 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 Zq_K~cq(c?p<;&$u&~T02)(H0G{|A#-h`0a% delta 11695 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 MDgR^2!8YRm01wZECjbBd diff --git a/dist/text_texture_generator_v1.0.0_free.zip b/dist/text_texture_generator_v1.0.0_free.zip index f1fb9f07a3896af56e48fa7e5af75afd6b4a1f07..e18e10d54cac454592ca961873f047c0cacddc90 100644 GIT binary patch delta 4627 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 delta 4627 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 Da%fFq delta 2440 zcmZuxT})fa6~6b{fQ>OW0TZwdHW+NoKLiKjIAr-T1PE!n5GS17E=zKUK=zJ}<~o0B zR?sc&Mt!InT~*a;TPfOyY}GzAs#mM3-Ks0?CQVhfB2rY*`_ex2rK^4GL#0ZUdd>hF zkTTcjo0;=}&dm5{Yad_pe(3d9DmY>b|5|%`-`l9(y`M-{Gwr&AxHKL(t%2G)C$a7u z%|}J$BpPBB4^>6^1=8L)y3;EkYA8k88{m?zK8*~xjw*^a=3?rLz&BM9I=p{SNzpcS z7jqr;s4BWkcN-q?$hiutaP}BpUJcdU2R`lxKd*rrsaItj!6MYAi(RPo*#$f zya5_`BQ)|L1o!=Ephkt!q z0I@@_-&QsuxuoQsx0KtCCFPciu8$ktya#%CFZ80hS8p)-ct7;>0T|$eFsLe;GOg&1 z+ln4M+BZ0X+96|zry#{o!bv_1!~5xmkvjq-{1lwxX-MAA1$yc)1Ju3 z_R|i@v?tgJ7(qIPltwy@ltDU!G>UW<=@q0gr18Twuuf5nnKWU{NtonW$nq(e;?pqA z&%rr912cRUW>qC$jh2&03t5CIq-mscNHa*Yvd_)vWDe$Jg6H$&`w6CDUY^jw$z6a8 zn!>)ON7(%P%1<@CWnZ_xa5OnCT>pmk?La-&hYsiPD9$e_x*O(So6qA7qxqJ4sPiqx zt9$_#_#!Owi*S+WAjdDkC4Lz$tI9QHWr_8(0XE1|>?9jzBkUAQv(qfY&ahE7$Ih}z zmStmXoK3N5_6j@4X4ouSV2dnqaWxE}x8j+sLx6dlC+?KmOr}ZAcACsswJWX`@%QPE-f zJgtEXuh1wVo?Yw9wjT)c3d?~lm0L%pGW?>vQ=FF$@-&z%}LI!N74Gy zT+JKhYJchH$M0%t)Jg=?zC4OdCt2Y@!;Rp8>Mwl|tWwog>zAR907scH2q=yS5~Qpz zLvb}@)rPZyWn|di+)Vb;iOJ>p#pM-U7jIfT9FORvdYxc}pp`)0BZ?^s)~~|d<&2iX z`e(SUQ*uq*n-vI@4i0&TY?oly*3BXl;s+@DYiY1)#I2gvTk#(Cy7gW>)VPN1WJKJ; zNn{Z0GV?0SGx4_d=Xi2cmhdjg?h$;SfQqplCiYO)_ATOlhk)u4+XQbCe3#&R1V_uE zoNh8s;*<<1^V$x}sZHU5#Y2)eTbpwkLWJn!2bO5-s zD_^(~KS4&K^l{=#t#;Y2xMD1?=T{4u_#M;M3a0h9&WRLNZI@l)%v`hGS2phS7Vm5@ zQ}~cA`~)=w$4E#$7XMNt2IQwb#nP>L7XLifA>nPhE zes}bp(I0j^bA+BdLOV+Cf$NduBiBxJ1b@GdebVv!zTfmcZFze6SvvPTo!d$0tdqTu zhTU88+f{s9^4nEvb`E~KO68XPb`{T-{Jv5vX? max_chars: + col.separator() + warning_box = col.box() + warning_box.alert = True + warning_box.label(text=f"⚠ Exceeds character limit! Will be trimmed to {max_chars} chars", icon='ERROR') def execute(self, context): - """Save temp text back to main property with limits enforced""" + """Save temp text back to main property - reject if contains newlines""" props = context.scene.text_texture_props - limits = get_version_limits() - max_lines = limits.get("max_text_lines", 50) - max_chars = limits.get("max_text_length", 4096) + # Check for newlines and reject + if '\n' in self.temp_text: + self.report({'ERROR'}, "Multiline text not supported. Please use single line only.") + return {'CANCELLED'} + max_chars = 4096 + + # Enforce character limit text = self.temp_text[:max_chars] - lines = text.split('\n') - if len(lines) > max_lines: - lines = lines[:max_lines] - text = '\n'.join(lines) - props.text = text return {'FINISHED'} diff --git a/src/utils/__pycache__/constants.cpython-311.pyc b/src/utils/__pycache__/constants.cpython-311.pyc index 4536649f855462700e8ef9ace4db24f1a2c2303f..b68e05ad2da0711943a6faacaab9ad2d75a9727e 100644 GIT binary patch delta 20 acmZp-YPaHE&dbZi00f=IuQzi4kpTcWmj)C7 delta 20 acmZp-YPaHE&dbZi00hdxFE?`kkpTcT0|kr# diff --git a/tests/unit/operators/test_text_editor_ops.py b/tests/unit/operators/test_text_editor_ops.py index 9b6458f..49fdde4 100644 --- a/tests/unit/operators/test_text_editor_ops.py +++ b/tests/unit/operators/test_text_editor_ops.py @@ -5,6 +5,7 @@ Tests verify: - Operator class exists and is properly structured - PRO-only access control via poll() - Text processing and limit enforcement +- Single-line validation (rejects newlines) - Proper Blender operator contract """ @@ -53,13 +54,13 @@ class TestModalTextEditorOperator: operator = TEXT_TEXTURE_OT_edit_multiline_text() mock_context = Mock() - mock_context.scene.text_texture_props.text = "Line 1\nLine 2\nLine 3" + mock_context.scene.text_texture_props.text = "Single line text" mock_context.window_manager.invoke_props_dialog.return_value = {'RUNNING_MODAL'} mock_event = Mock() result = operator.invoke(mock_context, mock_event) - assert operator.temp_text == "Line 1\nLine 2\nLine 3" + assert operator.temp_text == "Single line text" assert result == {'RUNNING_MODAL'} mock_context.window_manager.invoke_props_dialog.assert_called_once() @@ -68,84 +69,83 @@ class TestModalTextEditorOperator: from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text operator = TEXT_TEXTURE_OT_edit_multiline_text() - operator.temp_text = "Edited Line 1\nEdited Line 2" + operator.temp_text = "Edited single line" mock_context = Mock() mock_context.scene.text_texture_props.text = "Old text" - with patch('src.operators.text_editor_ops.get_version_limits', return_value={ - 'max_text_lines': 50, - 'max_text_length': 4096 - }): - result = operator.execute(mock_context) + result = operator.execute(mock_context) assert result == {'FINISHED'} - assert mock_context.scene.text_texture_props.text == "Edited Line 1\nEdited Line 2" + assert mock_context.scene.text_texture_props.text == "Edited single line" def test_execute_enforces_character_limit(self): """execute() must truncate text exceeding max_text_length.""" from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text operator = TEXT_TEXTURE_OT_edit_multiline_text() - # Create text exceeding 100 char limit - operator.temp_text = "x" * 150 + # Create text exceeding 4096 char limit + operator.temp_text = "x" * 5000 mock_context = Mock() mock_context.scene.text_texture_props.text = "" - with patch('src.operators.text_editor_ops.get_version_limits', return_value={ - 'max_text_lines': 50, - 'max_text_length': 100 - }): - operator.execute(mock_context) + operator.execute(mock_context) saved_text = mock_context.scene.text_texture_props.text - assert len(saved_text) == 100, f"Expected 100 chars, got {len(saved_text)}" + assert len(saved_text) == 4096, f"Expected 4096 chars, got {len(saved_text)}" - def test_execute_enforces_line_limit(self): - """execute() must truncate lines exceeding max_text_lines.""" + def test_execute_rejects_text_with_newlines(self): + """execute() must reject text containing newline characters.""" from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text operator = TEXT_TEXTURE_OT_edit_multiline_text() - # Create 10 lines when limit is 5 - operator.temp_text = "\n".join([f"Line {i}" for i in range(10)]) + operator.temp_text = "Line 1\nLine 2" mock_context = Mock() - mock_context.scene.text_texture_props.text = "" + mock_context.scene.text_texture_props.text = "Old text" - with patch('src.operators.text_editor_ops.get_version_limits', return_value={ - 'max_text_lines': 5, - 'max_text_length': 4096 - }): - operator.execute(mock_context) + result = operator.execute(mock_context) - saved_text = mock_context.scene.text_texture_props.text - line_count = saved_text.count('\n') + 1 - assert line_count == 5, f"Expected 5 lines, got {line_count}" - assert saved_text == "Line 0\nLine 1\nLine 2\nLine 3\nLine 4" + # Should return CANCELLED + assert result == {'CANCELLED'} + # Should not modify the original text + assert mock_context.scene.text_texture_props.text == "Old text" + # Should report an error + operator.report.assert_called_once_with( + {'ERROR'}, + "Multiline text not supported. Please use single line only." + ) - def test_execute_handles_both_limits_simultaneously(self): - """execute() must enforce both character and line limits correctly.""" + def test_execute_rejects_multiple_newlines(self): + """execute() must reject text with multiple newline characters.""" from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text operator = TEXT_TEXTURE_OT_edit_multiline_text() - # Create 10 lines of 20 chars each (200 chars total) - operator.temp_text = "\n".join([f"Line {i:015d}" for i in range(10)]) + operator.temp_text = "Line 1\nLine 2\nLine 3\nLine 4" mock_context = Mock() - mock_context.scene.text_texture_props.text = "" + mock_context.scene.text_texture_props.text = "Old text" - with patch('src.operators.text_editor_ops.get_version_limits', return_value={ - 'max_text_lines': 5, - 'max_text_length': 150 - }): - operator.execute(mock_context) + result = operator.execute(mock_context) - saved_text = mock_context.scene.text_texture_props.text - # Should truncate to 150 chars first, then check lines - assert len(saved_text) <= 150 - line_count = saved_text.count('\n') + 1 - assert line_count <= 5 + assert result == {'CANCELLED'} + assert mock_context.scene.text_texture_props.text == "Old text" + + def test_execute_accepts_text_without_newlines(self): + """execute() must accept and save text without newlines.""" + from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text + + operator = TEXT_TEXTURE_OT_edit_multiline_text() + operator.temp_text = "This is a single line with no newlines" + + mock_context = Mock() + mock_context.scene.text_texture_props.text = "Old text" + + result = operator.execute(mock_context) + + assert result == {'FINISHED'} + assert mock_context.scene.text_texture_props.text == "This is a single line with no newlines" def test_operator_has_temp_text_property(self): """Operator must have StringProperty named temp_text.""" @@ -178,4 +178,50 @@ class TestModalTextEditorOperator: from src.operators import text_editor_ops assert hasattr(text_editor_ops, 'unregister') - assert callable(text_editor_ops.unregister) \ No newline at end of file + assert callable(text_editor_ops.unregister) + + +class TestSingleLineUIBehavior: + """Test suite for single-line text UI display and statistics.""" + + def test_draw_calculates_character_count(self): + """draw() must count characters correctly.""" + from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text + + operator = TEXT_TEXTURE_OT_edit_multiline_text() + mock_context = Mock() + + operator.temp_text = "Single line text" + operator.draw(mock_context) + + actual_chars = len(operator.temp_text) + expected_chars = 16 + assert actual_chars == expected_chars, f"Expected {expected_chars} chars, got {actual_chars}" + + def test_draw_handles_empty_text(self): + """draw() must handle empty text without errors.""" + from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text + + operator = TEXT_TEXTURE_OT_edit_multiline_text() + mock_context = Mock() + + operator.temp_text = "" + operator.draw(mock_context) + + char_count = len(operator.temp_text) + assert char_count == 0, f"Empty text should have 0 characters, got {char_count}" + + def test_draw_handles_long_single_line(self): + """draw() must handle long single-line text correctly.""" + from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text + + operator = TEXT_TEXTURE_OT_edit_multiline_text() + mock_context = Mock() + + # Create a very long single line + long_text = "x" * 5000 + operator.temp_text = long_text + operator.draw(mock_context) + + # Should track all characters + assert len(operator.temp_text) == 5000 \ No newline at end of file