From 1c2d0074db64c892944d46d7f06a00dac5e9ceb0 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 5 Dec 2024 11:16:19 +0100 Subject: [PATCH 01/25] Currently a DataflowError may get lost in a middle of block statements --- .../DataflowErrorPropagationTest.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/semantic/DataflowErrorPropagationTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/semantic/DataflowErrorPropagationTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/semantic/DataflowErrorPropagationTest.java new file mode 100644 index 000000000000..46f466abdb8e --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/semantic/DataflowErrorPropagationTest.java @@ -0,0 +1,81 @@ +package org.enso.interpreter.test.semantic; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.enso.common.MethodNames; +import org.enso.test.utils.ContextUtils; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Value; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class DataflowErrorPropagationTest { + private static Context ctx; + private static Value suppressError; + private static Value suppressErrorWithAssign; + + @BeforeClass + public static void prepareCtx() { + ctx = ContextUtils.createDefaultContext(); + var code = + """ + from Standard.Base import all + + private yield_error yes:Boolean -> Text = + if yes then Error.throw "Yielding an error" else + "OK" + + suppress_error yes:Boolean value = + yield_error yes + value + + suppress_error_with_assign yes:Boolean value = + _ = yield_error yes + value + """; + suppressError = + ctx.eval("enso", code).invokeMember(MethodNames.Module.EVAL_EXPRESSION, "suppress_error"); + suppressErrorWithAssign = + ctx.eval("enso", code) + .invokeMember(MethodNames.Module.EVAL_EXPRESSION, "suppress_error_with_assign"); + } + + @AfterClass + public static void disposeCtx() { + ctx.close(); + ctx = null; + } + + @Test + public void noErrorReturnValue() { + var value = suppressError.execute(false, 42); + assertTrue("It is a number", value.isNumber()); + assertEquals(42, value.asInt()); + } + + @Test + public void propagateErrorImmediatelly() { + var value = suppressError.execute(true, 42); + assertTrue("It is a number", value.isNumber()); + assertFalse("Not an error", value.isException()); + assertEquals(42, value.asInt()); + } + + @Test + public void noErrorReturnValueWithAssign() { + var value = suppressErrorWithAssign.execute(false, 42); + assertTrue("It is a number", value.isNumber()); + assertEquals(42, value.asInt()); + } + + @Test + public void errorIsAssignedAndThatIsEnoughReturnValue() { + var value = suppressErrorWithAssign.execute(true, 42); + assertTrue("It is a number", value.isNumber()); + assertFalse("Not an error", value.isException()); + assertEquals(42, value.asInt()); + } +} From 49c628ef1f06e4b4659512964e8c806fe9d9cf12 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 5 Dec 2024 11:24:49 +0100 Subject: [PATCH 02/25] Propagate Error ASAP instead of ignoring it --- CHANGELOG.md | 8 ++++++++ .../test/semantic/DataflowErrorPropagationTest.java | 11 ++++++++--- .../interpreter/node/callable/function/BlockNode.java | 11 ++++++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9010c33189b1..4dcdceac1a51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# Next Next Release + +#### Enso Language & Runtime + +- [Propagate Error ASAP instead of ignoring it][11777]. + +[11777]: https://github.com/enso-org/enso/pull/11777 + # Next Release #### Enso IDE diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/semantic/DataflowErrorPropagationTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/semantic/DataflowErrorPropagationTest.java index 46f466abdb8e..f5facc9d72d1 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/semantic/DataflowErrorPropagationTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/semantic/DataflowErrorPropagationTest.java @@ -7,6 +7,7 @@ import org.enso.common.MethodNames; import org.enso.test.utils.ContextUtils; import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.Value; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -59,9 +60,13 @@ public void noErrorReturnValue() { @Test public void propagateErrorImmediatelly() { var value = suppressError.execute(true, 42); - assertTrue("It is a number", value.isNumber()); - assertFalse("Not an error", value.isException()); - assertEquals(42, value.asInt()); + assertFalse("It is not a number", value.isNumber()); + assertTrue("It is an error", value.isException()); + try { + throw value.throwException(); + } catch (PolyglotException ex) { + assertEquals("Yielding an error", ex.getMessage()); + } } @Test diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java index 994b68b34149..a50ccedbbdbb 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java @@ -9,6 +9,8 @@ import com.oracle.truffle.api.source.SourceSection; import java.util.Set; import org.enso.interpreter.node.ExpressionNode; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; /** * This node defines the body of a function for execution, as well as the protocol for executing the @@ -55,8 +57,15 @@ public static BlockNode buildSilent(ExpressionNode[] expressions, ExpressionNode @Override @ExplodeLoop public Object executeGeneric(VirtualFrame frame) { + var ctx = EnsoContext.get(this); + var nothing = ctx.getBuiltins().nothing(); for (ExpressionNode statement : statements) { - statement.executeGeneric(frame); + var result = statement.executeGeneric(frame); + if (result != nothing) { + if (result instanceof DataflowError err) { + return err; + } + } } return returnExpr.executeGeneric(frame); } From 91f74978b79deac11265c655c5b7518929c72796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Thu, 5 Dec 2024 14:04:32 +0100 Subject: [PATCH 03/25] check for dataflow error in should_equal rhs and report it - this is wrongly written test --- .../Test/0.0.0-dev/src/Extensions.enso | 41 ++++++++++--------- .../Standard/Test/0.0.0-dev/src/Helpers.enso | 10 +++++ 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso index bafe3431a303..c5908ab60d7e 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso @@ -4,6 +4,7 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import project.Spec_Result.Spec_Result import project.Test.Test +from project.Helpers import should_equal_error_check ## Expect a function to fail with the provided dataflow error. @@ -70,23 +71,24 @@ Error.should_fail_with self matcher frames_to_skip=0 unwrap_errors=True = example_should_equal = Examples.add_1_to 1 . should_equal 2 Any.should_equal : Any -> Integer -> Spec_Result -Any.should_equal self that frames_to_skip=0 = case self == that of - True -> Spec_Result.Success - False -> - loc = Meta.get_source_location 2+frames_to_skip - additional_comment = case self of - _ : Vector -> case that of - _ : Vector -> - case self.length == that.length of - True -> - diff = self.zip that . index_of p-> - p.first != p.second - "; first difference at index " + diff.to_text + " " - False -> "; lengths differ (" + self.length.to_text + " != " + that.length.to_text + ") " +Any.should_equal self that frames_to_skip=0 = should_equal_error_check that <| + case self == that of + True -> Spec_Result.Success + False -> + loc = Meta.get_source_location 2+frames_to_skip + additional_comment = case self of + _ : Vector -> case that of + _ : Vector -> + case self.length == that.length of + True -> + diff = self.zip that . index_of p-> + p.first != p.second + "; first difference at index " + diff.to_text + " " + False -> "; lengths differ (" + self.length.to_text + " != " + that.length.to_text + ") " + _ -> "" _ -> "" - _ -> "" - msg = self.pretty + " did not equal " + that.pretty + additional_comment + " (at " + loc + ")." - Test.fail msg + msg = self.pretty + " did not equal " + that.pretty + additional_comment + " (at " + loc + ")." + Test.fail msg ## Asserts that `self` value is equal to the expected type value. @@ -266,8 +268,7 @@ Error.should_end_with self that frames_to_skip=0 = example_should_equal = Examples.add_1_to 1 . should_equal 2 Error.should_equal : Any -> Integer -> Spec_Result -Error.should_equal self that frames_to_skip=0 = - _ = [that] +Error.should_equal self that frames_to_skip=0 = should_equal_error_check that <| Test.fail_match_on_unexpected_error self 1+frames_to_skip ## Asserts that `self` is within `epsilon` from `that`. @@ -293,7 +294,7 @@ Error.should_equal self that frames_to_skip=0 = example_should_equal = 1.00000001 . should_equal 1.00000002 epsilon=0.0001 Number.should_equal : Float -> Float -> Integer -> Spec_Result -Number.should_equal self that epsilon=0 frames_to_skip=0 = +Number.should_equal self that epsilon=0 frames_to_skip=0 = should_equal_error_check that <| matches = case that of _ : Number -> self.equals that epsilon _ -> self==that @@ -312,7 +313,7 @@ Number.should_equal self that epsilon=0 frames_to_skip=0 = - frames_to_skip (optional, advanced): used to alter the location which is displayed as the source of this error. Decimal.should_equal : Number -> Float-> Float -> Integer -> Spec_Result -Decimal.should_equal self that epsilon=0 frames_to_skip=0 = +Decimal.should_equal self that epsilon=0 frames_to_skip=0 = should_equal_error_check that <| self.to_float . should_equal that.to_float epsilon frames_to_skip+1 ## Asserts that `self` value is not an error. diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso index 2f4a2bef3735..bcef333339f6 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso @@ -3,6 +3,7 @@ private from Standard.Base import all from Standard.Base.Runtime import assert from Standard.Base.Runtime import State +import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import project.Clue.Clue import project.Group.Group @@ -82,3 +83,12 @@ type Finished_With - stack_trace_text: A textual representation of the stack trace for the error. Error err stack_trace_text + + +## PRIVATE +should_equal_error_check that ~action = + case that.is_error of + False -> action + True -> + msg = "Dataflow error provided as expected value for `should_equal`. Use `should_fail_with` instead."+ ' Error stack trace was:\n'+that.get_stack_trace_text + Panic.throw (Illegal_Argument.Error msg) From 1ed8505c1327826dbf062aae45dbe6043b680aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Thu, 5 Dec 2024 14:22:07 +0100 Subject: [PATCH 04/25] add error check in remaining should_equal overrides --- test/Table_Tests/src/Util.enso | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/Table_Tests/src/Util.enso b/test/Table_Tests/src/Util.enso index b956fe84dd66..31476bfe3d99 100644 --- a/test/Table_Tests/src/Util.enso +++ b/test/Table_Tests/src/Util.enso @@ -5,29 +5,30 @@ import Standard.Database.DB_Column.DB_Column from Standard.Table import Column, Table from Standard.Test import all +from Standard.Test.Helpers import should_equal_error_check polyglot java import org.enso.base_test_helpers.FileSystemHelper Table.should_equal : Any -> Integer -> Any -Table.should_equal self expected frames_to_skip=0 = +Table.should_equal self expected frames_to_skip=0 = should_equal_error_check that <| loc = Meta.get_source_location 1+frames_to_skip Panic.catch Test_Failure_Error (table_should_equal_impl self expected loc) error-> Test.fail error.payload.message Column.should_equal : Any -> Integer -> Any -Column.should_equal self expected frames_to_skip=0 = +Column.should_equal self expected frames_to_skip=0 = should_equal_error_check that <| loc = Meta.get_source_location 1+frames_to_skip Panic.catch Test_Failure_Error (column_should_equal_impl self expected loc) error-> Test.fail error.payload.message DB_Table.should_equal : DB_Table -> Integer -> Any -DB_Table.should_equal self expected frames_to_skip=0 = +DB_Table.should_equal self expected frames_to_skip=0 = should_equal_error_check that <| t0 = self.read t1 = expected.read t0 . should_equal t1 frames_to_skip+1 DB_Column.should_equal : DB_Column -> Integer -> Any -DB_Column.should_equal self expected frames_to_skip=0 = +DB_Column.should_equal self expected frames_to_skip=0 = should_equal_error_check that <| t0 = self.read t1 = expected.read t0 . should_equal t1 frames_to_skip+1 From 737330455a2f4b5a127d9ba0ded1a586668bc2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Fri, 6 Dec 2024 14:27:59 +0100 Subject: [PATCH 05/25] align behaviour of find across range/vector --- .../lib/Standard/Base/0.0.0-dev/src/Data/Range.enso | 5 +++-- .../Standard/Base/0.0.0-dev/src/Data/Time/Date_Range.enso | 5 +++-- test/Base_Tests/src/Data/Range_Spec.enso | 3 ++- test/Base_Tests/src/Data/Time/Date_Range_Spec.enso | 8 ++++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Range.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Range.enso index 8c588cb434cd..2064d7927fdb 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Range.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Range.enso @@ -7,6 +7,7 @@ import project.Data.Text.Text import project.Data.Vector.Vector import project.Error.Error import project.Errors.Common.Index_Out_Of_Bounds +import project.Errors.Common.Not_Found import project.Errors.Empty_Error.Empty_Error import project.Errors.Illegal_Argument.Illegal_Argument import project.Errors.Illegal_State.Illegal_State @@ -397,7 +398,7 @@ type Range @condition range_default_filter_condition_widget any : (Filter_Condition | (Integer -> Boolean)) -> Boolean any self (condition : Filter_Condition | (Integer -> Boolean)) = - self.find condition . is_nothing . not + self.find condition if_missing=Nothing . is_nothing . not ## GROUP Selections ICON find @@ -422,7 +423,7 @@ type Range 1.up_to 100 . find (..Greater than=10) @condition range_default_filter_condition_widget find : (Filter_Condition | (Integer -> Boolean)) -> Integer -> Any -> Any - find self (condition : Filter_Condition | (Integer -> Boolean)) (start : Integer = 0) ~if_missing=Nothing = + find self (condition : Filter_Condition | (Integer -> Boolean)) (start : Integer = 0) ~if_missing=(Error.throw Not_Found) = predicate = unify_condition_or_predicate condition check_start_valid start self used_start-> result = find_internal self used_start predicate diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Range.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Range.enso index e51d9f12062e..3f0d9077dfba 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Range.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Range.enso @@ -11,6 +11,7 @@ import project.Data.Time.Period.Period import project.Data.Vector.Vector import project.Error.Error import project.Errors.Common.Index_Out_Of_Bounds +import project.Errors.Common.Not_Found import project.Errors.Empty_Error.Empty_Error import project.Errors.Illegal_Argument.Illegal_Argument import project.Function.Function @@ -418,7 +419,7 @@ type Date_Range @condition date_range_default_filter_condition_widget any : (Filter_Condition | (Date -> Boolean)) -> Boolean any self (condition : Filter_Condition | (Date -> Boolean)) = - self.find condition . is_nothing . not + self.find condition if_missing=Nothing . is_nothing . not ## GROUP Selections ICON find @@ -438,7 +439,7 @@ type Date_Range (Date.new 2020 10 01).up_to (Date.new 2020 10 31) . find (d-> d.day_of_week == Day_Of_Week.Monday) @condition date_range_default_filter_condition_widget find : (Filter_Condition | (Date -> Boolean)) -> Integer -> Any -> Any - find self (condition : Filter_Condition | (Date -> Boolean)) (start : Integer = 0) ~if_missing=Nothing = + find self (condition : Filter_Condition | (Date -> Boolean)) (start : Integer = 0) ~if_missing=(Error.throw Not_Found) = predicate = unify_condition_or_predicate condition index = self.index_of predicate start case index of diff --git a/test/Base_Tests/src/Data/Range_Spec.enso b/test/Base_Tests/src/Data/Range_Spec.enso index a80a5cd52a0f..f18cbaad222e 100644 --- a/test/Base_Tests/src/Data/Range_Spec.enso +++ b/test/Base_Tests/src/Data/Range_Spec.enso @@ -3,6 +3,7 @@ import Standard.Base.Data.Vector.Builder import Standard.Base.Errors.Empty_Error.Empty_Error import Standard.Base.Errors.Common.Index_Out_Of_Bounds import Standard.Base.Errors.Common.No_Such_Method +import Standard.Base.Errors.Common.Not_Found import Standard.Base.Errors.Common.Type_Error import Standard.Base.Errors.Common.Unsupported_Argument_Types import Standard.Base.Errors.Illegal_Argument.Illegal_Argument @@ -250,7 +251,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> group_builder.specify "should find elements" <| 1.up_to 10 . find (> 5) . should_equal 6 1.up_to 10 . find (..Greater 5) . should_equal 6 - 1.up_to 10 . find (> 10) . should_be_a Nothing + 1.up_to 10 . find (> 10) . should_fail_with Not_Found 1.up_to 10 . find (v-> v%4 == 0) start=6 . should_equal 8 1.up_to 10 . find (< 5) start=6 . should_be_a Nothing 1.up_to 10 . find (< 5) start=10 . should_fail_with Index_Out_Of_Bounds diff --git a/test/Base_Tests/src/Data/Time/Date_Range_Spec.enso b/test/Base_Tests/src/Data/Time/Date_Range_Spec.enso index b2b469ae1c61..9d74f3604c7b 100644 --- a/test/Base_Tests/src/Data/Time/Date_Range_Spec.enso +++ b/test/Base_Tests/src/Data/Time/Date_Range_Spec.enso @@ -1,4 +1,5 @@ from Standard.Base import all +import Standard.Base.Errors.Common.Not_Found import Standard.Base.Errors.Common.Type_Error import Standard.Base.Errors.Empty_Error.Empty_Error import Standard.Base.Errors.Illegal_Argument.Illegal_Argument @@ -157,7 +158,7 @@ add_specs suite_builder = r.partition p . should_equal (r.to_vector.partition p) r.all p . should_equal (r.to_vector.all p) r.any p . should_equal (r.to_vector.any p) - r.find p . should_equal (r.to_vector.find p) + r.find p if_missing="not found" . should_equal (r.to_vector.find p if_missing="not found") r.index_of p . should_equal (r.to_vector.index_of p) r.last_index_of p . should_equal (r.to_vector.last_index_of p) count_mondays acc date = @@ -170,7 +171,7 @@ add_specs suite_builder = r.partition fc . should_equal (r.to_vector.partition fc) r.all fc . should_equal (r.to_vector.all fc) r.any fc . should_equal (r.to_vector.any fc) - r.find fc . should_equal (r.to_vector.find fc) + r.find fc if_missing="not found" . should_equal (r.to_vector.find fc if_missing="not found") r.index_of fc . should_equal (r.to_vector.index_of fc) r.last_index_of fc . should_equal (r.to_vector.last_index_of fc) @@ -182,6 +183,9 @@ add_specs suite_builder = Test.expect_panic_with (r.index_of invalid_arg) Type_Error Test.expect_panic_with (r.last_index_of invalid_arg) Type_Error + # If `if_missing` is not provided, it defaults to `Not_Found` dataflow error + r.find (== 123) . should_fail_with Not_Found + reducer x y = if x > y then x else y case r.length of From b0f71f8d58f59def16962720616374f0e4bbe327 Mon Sep 17 00:00:00 2001 From: Gregory Michael Travis Date: Tue, 17 Dec 2024 03:12:40 -0500 Subject: [PATCH 06/25] Fix Decimal test and `Array_Like_Helpers.find` (#11883) --- .../Base/0.0.0-dev/src/Data/Index_Sub_Range.enso | 9 +++++---- .../0.0.0-dev/src/Internal/Array_Like_Helpers.enso | 7 +++++-- test/Base_Tests/src/Data/Decimal_Spec.enso | 4 ++-- test/Base_Tests/src/Data/Range_Spec.enso | 12 +++++++----- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso index 95dc0af8ac17..2befdfcc054d 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso @@ -9,6 +9,7 @@ import project.Errors.Common.Missing_Argument import project.Errors.Common.Type_Error import project.Errors.Illegal_Argument.Illegal_Argument import project.Function.Function +import project.Internal.Array_Like_Helpers.Not_Found import project.Math import project.Meta import project.Metadata.Display @@ -204,8 +205,8 @@ take_helper length at single_slice slice_ranges range:(Index_Sub_Range | Range | Index_Sub_Range.First count -> single_slice 0 (length.min count) Index_Sub_Range.Last count -> single_slice length-count length Index_Sub_Range.While predicate -> - end = 0.up_to length . find i-> (predicate (at i)).not - true_end = if end.is_nothing then length else end + end = 0.up_to length . find (i-> (predicate (at i)).not) if_missing=Not_Found.Value + true_end = if end == Not_Found.Value then length else end single_slice 0 true_end Index_Sub_Range.By_Index one_or_many_descriptors -> Panic.recover [Index_Out_Of_Bounds, Illegal_Argument] <| indices = case one_or_many_descriptors of @@ -255,8 +256,8 @@ drop_helper length at single_slice slice_ranges range:(Index_Sub_Range | Range | Index_Sub_Range.First count -> single_slice count length Index_Sub_Range.Last count -> single_slice 0 length-count Index_Sub_Range.While predicate -> - end = 0.up_to length . find i-> (predicate (at i)).not - true_end = if end.is_nothing then length else end + end = 0.up_to length . find (i-> (predicate (at i)).not) if_missing=Not_Found.Value + true_end = if end == Not_Found.Value then length else end single_slice true_end length Index_Sub_Range.By_Index one_or_many_descriptors -> Panic.recover [Index_Out_Of_Bounds, Illegal_Argument] <| indices = case one_or_many_descriptors of diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso index 131d1de9fd9f..a0a53669230a 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso @@ -30,6 +30,9 @@ from project.Data.Index_Sub_Range import drop_helper, take_helper from project.Data.Ordering import Comparable from project.Data.Range.Extensions import all +type Not_Found + Value + new_array_proxy_builtin : Integer -> (Integer -> Any) -> Array new_array_proxy_builtin length at = @Builtin_Method "Array_Like_Helpers.new_array_proxy_builtin" @@ -216,8 +219,8 @@ find vector condition start ~if_missing = predicate = unify_condition_or_predicate condition self_len = vector.length check_start_valid start self_len used_start-> - found = used_start.up_to self_len . find (idx -> (predicate (vector.at idx))) - if found.is_nothing then if_missing else vector.at found + found = used_start.up_to self_len . find (idx -> (predicate (vector.at idx))) if_missing=Not_Found.Value + if found == Not_Found.Value then if_missing else vector.at found transpose vec_of_vecs = if vec_of_vecs.is_empty then [] else diff --git a/test/Base_Tests/src/Data/Decimal_Spec.enso b/test/Base_Tests/src/Data/Decimal_Spec.enso index 3465ce9cd85a..98a9d096b215 100644 --- a/test/Base_Tests/src/Data/Decimal_Spec.enso +++ b/test/Base_Tests/src/Data/Decimal_Spec.enso @@ -484,10 +484,10 @@ add_specs suite_builder = (Decimal.from_integer -29388920982834 . subtract (Decimal.from_integer 842820) (Math_Context.new 7)) . should_equal (Decimal.from_integer -29388920000000) (Decimal.new "-8273762787.3535345" . subtract (Decimal.new "76287273.23434535") (Math_Context.new 10)) . should_equal (Decimal.new "-8350050061") - (Decimal.from_integer 7297927982888383 . multiply (Decimal.from_integer 828737) (Math_Context.new 6)) . should_equal (Decimal.from_integer 6048060000000000000000 ) + (Decimal.from_integer 7297927982888383 . multiply (Decimal.from_integer 828737) (Math_Context.new 6)) . should_equal (Decimal.from_integer 6048060000000000000000) (Decimal.new "893872388.3535345" . multiply (Decimal.new "72374727737.23434535") (Math_Context.new 14)) . should_equal (Decimal.new "64693770738918000000") - (Decimal.new "909678645268840" . divide (Decimal.new "28029830") (Math_Context.new 6)) . should_equal (Decimal.new "32453900 ") + (Decimal.new "909678645268840" . divide (Decimal.new "28029830") (Math_Context.new 6)) . should_equal (Decimal.new "32453900") (Decimal.new "384456406.7860325392609633764" . divide (Decimal.new "24556.125563546") (Math_Context.new 7)) . should_equal (Decimal.new "15656.23") (Decimal.from_integer 3948539458034580838458034803485 . add (Decimal.from_integer 237957498573948579387495837459837) (Math_Context.new 20)) . should_equal (Decimal.from_integer 241906038031983160230000000000000) diff --git a/test/Base_Tests/src/Data/Range_Spec.enso b/test/Base_Tests/src/Data/Range_Spec.enso index f18cbaad222e..dd21731e9ac7 100644 --- a/test/Base_Tests/src/Data/Range_Spec.enso +++ b/test/Base_Tests/src/Data/Range_Spec.enso @@ -253,7 +253,8 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> 1.up_to 10 . find (..Greater 5) . should_equal 6 1.up_to 10 . find (> 10) . should_fail_with Not_Found 1.up_to 10 . find (v-> v%4 == 0) start=6 . should_equal 8 - 1.up_to 10 . find (< 5) start=6 . should_be_a Nothing + 1.up_to 10 . find (< 5) start=6 if_missing=Nothing. should_be_a Nothing + 1.up_to 10 . find (< 5) start=6 . should_fail_with Not_Found 1.up_to 10 . find (< 5) start=10 . should_fail_with Index_Out_Of_Bounds 1.up_to 10 . find (< 5) start=10 . catch . should_equal (Index_Out_Of_Bounds.Error 10 10) Test.expect_panic_with (1.up_to 10 . find "invalid arg") Type_Error @@ -344,7 +345,8 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r.fold 0 (+) . should_equal 0 r.any _->True . should_equal False r.all _->False . should_equal True - r.find _->True . should_equal Nothing + r.find _->True if_missing=Nothing . should_equal Nothing + r.find _->True . should_fail_with Not_Found verify_contains r [] [-1, 0, 1, 2, 10] check_empty_range (0.up_to 0) @@ -371,7 +373,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r1.all (_ == 10) . should_equal True r1.all (_ == 11) . should_equal False r1.find (x-> x*x == 100) . should_equal 10 - r1.find (x-> x*x == 25) . should_equal Nothing + r1.find (x-> x*x == 25) if_missing=Nothing . should_equal Nothing verify_contains r1 [10] [-1, 0, 1, 2, 9, 11, 12] group_builder.specify "should behave correctly with step greater than 1" <| @@ -388,7 +390,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r1.all (x-> x % 2 == 0) . should_equal True r1.all (_ == 2) . should_equal False r1.find (x-> x*x == 16) . should_equal 4 - r1.find (x-> x*x == 25) . should_equal Nothing + r1.find (x-> x*x == 25) if_missing=Nothing . should_equal Nothing verify_contains r1 [0, 2, 4, 6, 8] [-3, -2, -1, 1, 3, 5, 7, 11, 12, 13, 14] r2 = Range.Between 0 3 2 @@ -486,7 +488,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r1.all (x-> x % 2 == 0) . should_equal False r1.all (_ > 0) . should_equal True r1.find (x-> x*x == 16) . should_equal 4 - r1.find (x-> x*x == 0) . should_equal Nothing + r1.find (x-> x*x == 0) if_missing=Nothing . should_equal Nothing verify_contains r1 [4, 3, 2, 1] [-2, -1, 0, 5, 6, 7, 10] r2 = Range.Between 4 0 -2 From d703496aa43e99d1f8116767cde816022a82b463 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Tue, 17 Dec 2024 09:20:28 +0100 Subject: [PATCH 07/25] Space before dot --- test/Base_Tests/src/Data/Range_Spec.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Base_Tests/src/Data/Range_Spec.enso b/test/Base_Tests/src/Data/Range_Spec.enso index dd21731e9ac7..bea200f8f01a 100644 --- a/test/Base_Tests/src/Data/Range_Spec.enso +++ b/test/Base_Tests/src/Data/Range_Spec.enso @@ -253,7 +253,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> 1.up_to 10 . find (..Greater 5) . should_equal 6 1.up_to 10 . find (> 10) . should_fail_with Not_Found 1.up_to 10 . find (v-> v%4 == 0) start=6 . should_equal 8 - 1.up_to 10 . find (< 5) start=6 if_missing=Nothing. should_be_a Nothing + 1.up_to 10 . find (< 5) start=6 if_missing=Nothing . should_be_a Nothing 1.up_to 10 . find (< 5) start=6 . should_fail_with Not_Found 1.up_to 10 . find (< 5) start=10 . should_fail_with Index_Out_Of_Bounds 1.up_to 10 . find (< 5) start=10 . catch . should_equal (Index_Out_Of_Bounds.Error 10 10) From 7901a2519f8e55637b0d0a02056bcb387857a7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 11:52:43 +0100 Subject: [PATCH 08/25] fix mistake in should_equal check --- .../lib/Standard/Test/0.0.0-dev/src/Extensions.enso | 2 +- .../Test/0.0.0-dev/src/Extensions_Helpers.enso | 11 +++++++++++ .../lib/Standard/Test/0.0.0-dev/src/Helpers.enso | 10 ---------- test/Table_Tests/src/Util.enso | 10 +++++----- 4 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso index 834ec003b7dd..df705e8cece7 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso @@ -4,7 +4,7 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import project.Spec_Result.Spec_Result import project.Test.Test -from project.Helpers import should_equal_error_check +from project.Extensions_Helpers import should_equal_error_check ## Expect a function to fail with the provided dataflow error. diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso new file mode 100644 index 000000000000..59390252d457 --- /dev/null +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso @@ -0,0 +1,11 @@ +from Standard.Base import all +import Standard.Base.Errors.Illegal_Argument.Illegal_Argument + +## PRIVATE + A helper that ensures that the value in `that` is not an error. +should_equal_error_check that ~action = + case that.is_error of + False -> action + True -> + msg = "Dataflow error provided as expected value for `should_equal`. Use `should_fail_with` instead."+ ' Error stack trace was:\n'+that.get_stack_trace_text + Panic.throw (Illegal_Argument.Error msg) diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso index bcef333339f6..2f4a2bef3735 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Helpers.enso @@ -3,7 +3,6 @@ private from Standard.Base import all from Standard.Base.Runtime import assert from Standard.Base.Runtime import State -import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import project.Clue.Clue import project.Group.Group @@ -83,12 +82,3 @@ type Finished_With - stack_trace_text: A textual representation of the stack trace for the error. Error err stack_trace_text - - -## PRIVATE -should_equal_error_check that ~action = - case that.is_error of - False -> action - True -> - msg = "Dataflow error provided as expected value for `should_equal`. Use `should_fail_with` instead."+ ' Error stack trace was:\n'+that.get_stack_trace_text - Panic.throw (Illegal_Argument.Error msg) diff --git a/test/Table_Tests/src/Util.enso b/test/Table_Tests/src/Util.enso index 31476bfe3d99..669bd6e72fb7 100644 --- a/test/Table_Tests/src/Util.enso +++ b/test/Table_Tests/src/Util.enso @@ -5,30 +5,30 @@ import Standard.Database.DB_Column.DB_Column from Standard.Table import Column, Table from Standard.Test import all -from Standard.Test.Helpers import should_equal_error_check +from Standard.Test.Extensions_Helpers import should_equal_error_check polyglot java import org.enso.base_test_helpers.FileSystemHelper Table.should_equal : Any -> Integer -> Any -Table.should_equal self expected frames_to_skip=0 = should_equal_error_check that <| +Table.should_equal self expected frames_to_skip=0 = should_equal_error_check expected <| loc = Meta.get_source_location 1+frames_to_skip Panic.catch Test_Failure_Error (table_should_equal_impl self expected loc) error-> Test.fail error.payload.message Column.should_equal : Any -> Integer -> Any -Column.should_equal self expected frames_to_skip=0 = should_equal_error_check that <| +Column.should_equal self expected frames_to_skip=0 = should_equal_error_check expected <| loc = Meta.get_source_location 1+frames_to_skip Panic.catch Test_Failure_Error (column_should_equal_impl self expected loc) error-> Test.fail error.payload.message DB_Table.should_equal : DB_Table -> Integer -> Any -DB_Table.should_equal self expected frames_to_skip=0 = should_equal_error_check that <| +DB_Table.should_equal self expected frames_to_skip=0 = should_equal_error_check expected <| t0 = self.read t1 = expected.read t0 . should_equal t1 frames_to_skip+1 DB_Column.should_equal : DB_Column -> Integer -> Any -DB_Column.should_equal self expected frames_to_skip=0 = should_equal_error_check that <| +DB_Column.should_equal self expected frames_to_skip=0 = should_equal_error_check expected <| t0 = self.read t1 = expected.read t0 . should_equal t1 frames_to_skip+1 From ba16c47cb43e3ede3a7cb42553a8958d23d72be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 12:34:35 +0100 Subject: [PATCH 09/25] fix frame offsets --- .../Test/0.0.0-dev/src/Extensions.enso | 116 ++++++++++-------- .../0.0.0-dev/src/Extensions_Helpers.enso | 9 +- test/Base_Tests/src/Data/Function_Spec.enso | 1 - test/Table_Tests/src/Util.enso | 18 +-- 4 files changed, 77 insertions(+), 67 deletions(-) diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso index df705e8cece7..a87a9ed86119 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso @@ -4,7 +4,7 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import project.Spec_Result.Spec_Result import project.Test.Test -from project.Extensions_Helpers import should_equal_error_check +from project.Extensions_Helpers import rhs_error_check ## Expect a function to fail with the provided dataflow error. @@ -71,11 +71,11 @@ Error.should_fail_with self matcher frames_to_skip=0 unwrap_errors=True = example_should_equal = Examples.add_1_to 1 . should_equal 2 Any.should_equal : Any -> Integer -> Spec_Result -Any.should_equal self that frames_to_skip=0 = should_equal_error_check that <| +Any.should_equal self that frames_to_skip=0 = rhs_error_check that <| + loc = Meta.get_source_location 4+frames_to_skip case self == that of True -> Spec_Result.Success False -> - loc = Meta.get_source_location 2+frames_to_skip additional_comment = case self of _ : Vector -> case that of _ : Vector -> @@ -132,12 +132,13 @@ Error.should_equal_type self that frames_to_skip=0 = example_should_not_equal = Examples.add_1_to 1 . should_not_equal 2 Any.should_not_equal : Any -> Integer -> Spec_Result -Any.should_not_equal self that frames_to_skip=0 = case self != that of - True -> Spec_Result.Success - False -> - loc = Meta.get_source_location 2+frames_to_skip - msg = self.to_text + " did equal " + that.to_text + " (at " + loc + ")." - Test.fail msg +Any.should_not_equal self that frames_to_skip=0 = if that.is_error then (Panic.throw (Illegal_Argument.Error "Expected value provided as `that` for `should_not_equal` cannot be an error, but got: "+that.to_display_text)) else + loc = Meta.get_source_location 2+frames_to_skip + case self != that of + True -> Spec_Result.Success + False -> + msg = self.to_text + " did equal " + that.to_text + " (at " + loc + ")." + Test.fail msg ## Added so that dataflow errors are not silently lost. Error.should_not_equal self that frames_to_skip=0 = @@ -185,15 +186,15 @@ Error.should_not_equal_type self that frames_to_skip=0 = example_should_start_with = "Hello World!" . should_start_with "Hello" Any.should_start_with : Text -> Integer -> Spec_Result -Any.should_start_with self that frames_to_skip=0 = case self of - _ : Text -> if self.starts_with that then Spec_Result.Success else - loc = Meta.get_source_location 3+frames_to_skip - msg = self.to_text + " does not start with " + that.to_text + " (at " + loc + ")." - Test.fail msg - _ -> - loc = Meta.get_source_location 2+frames_to_skip - msg = self.to_text + " is not a `Text` value (at " + loc + ")." - Test.fail msg +Any.should_start_with self that frames_to_skip=0 = + loc = Meta.get_source_location 1+frames_to_skip + rhs_error_check that <| case self of + _ : Text -> if self.starts_with that then Spec_Result.Success else + msg = self.to_text + " does not start with " + that.to_text + " (at " + loc + ")." + Test.fail msg + _ -> + msg = self.to_text + " is not a `Text` value (at " + loc + ")." + Test.fail msg ## Asserts that `self` value is a Text value and ends with `that`. @@ -209,15 +210,15 @@ Any.should_start_with self that frames_to_skip=0 = case self of example_should_end_with = "Hello World!" . should_end_with "ld!" Any.should_end_with : Text -> Integer -> Spec_Result -Any.should_end_with self that frames_to_skip=0 = case self of - _ : Text -> if self.ends_with that then Spec_Result.Success else - loc = Meta.get_source_location 3+frames_to_skip - msg = self.to_text + " does not end with " + that.to_text + " (at " + loc + ")." - Test.fail msg - _ -> - loc = Meta.get_source_location 2+frames_to_skip - msg = self.to_text + " is not a `Text` value (at " + loc + ")." - Test.fail msg +Any.should_end_with self that frames_to_skip=0 = + loc = Meta.get_source_location 1+frames_to_skip + rhs_error_check that <| case self of + _ : Text -> if self.ends_with that then Spec_Result.Success else + msg = self.to_text + " does not end with " + that.to_text + " (at " + loc + ")." + Test.fail msg + _ -> + msg = self.to_text + " is not a `Text` value (at " + loc + ")." + Test.fail msg ## Asserts that `self` value is a Text value and starts with `that`. @@ -268,8 +269,8 @@ Error.should_end_with self that frames_to_skip=0 = example_should_equal = Examples.add_1_to 1 . should_equal 2 Error.should_equal : Any -> Integer -> Spec_Result -Error.should_equal self that frames_to_skip=0 = should_equal_error_check that <| - Test.fail_match_on_unexpected_error self 1+frames_to_skip +Error.should_equal self that frames_to_skip=0 = rhs_error_check that <| + Test.fail_match_on_unexpected_error self 4+frames_to_skip ## Asserts that `self` is within `epsilon` from `that`. @@ -294,16 +295,17 @@ Error.should_equal self that frames_to_skip=0 = should_equal_error_check that <| example_should_equal = 1.00000001 . should_equal 1.00000002 epsilon=0.0001 Number.should_equal : Float -> Float -> Integer -> Spec_Result -Number.should_equal self that epsilon=0 frames_to_skip=0 = should_equal_error_check that <| - matches = case that of - n : Number -> self.equals n epsilon - _ -> self==that - case matches of - True -> Spec_Result.Success - False -> - loc = Meta.get_source_location 2+frames_to_skip - msg = self.to_text + " did not equal " + that.to_text + " (at " + loc + ")." - Test.fail msg +Number.should_equal self that epsilon=0 frames_to_skip=0 = + loc = Meta.get_source_location 1+frames_to_skip + rhs_error_check that <| + matches = case that of + n : Number -> self.equals n epsilon + _ -> self==that + case matches of + True -> Spec_Result.Success + False -> + msg = self.to_text + " did not equal " + that.to_text + " (at " + loc + ")." + Test.fail msg ## Asserts that `self` is within `epsilon` from `that`. @@ -313,8 +315,8 @@ Number.should_equal self that epsilon=0 frames_to_skip=0 = should_equal_error_ch - frames_to_skip (optional, advanced): used to alter the location which is displayed as the source of this error. Decimal.should_equal : Number -> Float-> Float -> Integer -> Spec_Result -Decimal.should_equal self that epsilon=0 frames_to_skip=0 = should_equal_error_check that <| - self.to_float . should_equal that.to_float epsilon frames_to_skip+1 +Decimal.should_equal self that epsilon=0 frames_to_skip=0 = rhs_error_check that <| + self.to_float . should_equal that.to_float epsilon frames_to_skip+4 ## Asserts that `self` value is not an error. @@ -428,7 +430,7 @@ Any.should_be_a self typ = fail_on_wrong_arg_type = Panic.throw <| Illegal_Argument.Error "typ ("+typ.to_display_text+") must either be a type or a constructor. Use `should_equal` for value equality test instead." - case Meta.meta typ of + rhs_error_check typ <| case Meta.meta typ of c : Meta.Constructor -> case Meta.meta self of a : Meta.Atom -> if a.constructor == c then Spec_Result.Success else @@ -491,6 +493,8 @@ Any.should_be_a self typ = Any.should_equal_ignoring_order : Any -> Integer -> Spec_Result Any.should_equal_ignoring_order self that frames_to_skip=0 = loc = Meta.get_source_location 1+frames_to_skip + if that.is_a Vector . not then + Panic.throw (Illegal_Argument.Error "Expected a Vector, but got a "+that.to_display_text+" (at "+loc+").") that.each element-> if self.contains element . not then msg = "The collection (" + self.to_text + ") did not contain " + element.to_text + " (at " + loc + ")." @@ -558,6 +562,8 @@ Error.should_equal_ignoring_order self that frames_to_skip=0 = Any.should_only_contain_elements_in : Any -> Integer -> Spec_Result Any.should_only_contain_elements_in self that frames_to_skip=0 = loc = Meta.get_source_location 1+frames_to_skip + if that.is_a Vector . not then + Panic.throw (Illegal_Argument.Error "Expected a Vector, but got a "+that.to_display_text+" (at "+loc+").") self.each element-> if that.contains element . not then msg = "The collection contained an element ("+element.to_text+") which was not expected (at " + loc + ")." @@ -611,13 +617,14 @@ Error.should_only_contain_elements_in self that frames_to_skip=0 = Any.should_contain : Any -> Integer -> Spec_Result Any.should_contain self element frames_to_skip=0 = loc = Meta.get_source_location 1+frames_to_skip - contains_result = Panic.catch No_Such_Method (self.contains element) caught_panic-> - if caught_panic.payload.method_name != "contains" then Panic.throw caught_panic else - msg = "The value (" + self.to_text + ") does not support the method `contains` (at " + loc + ")." + rhs_error_check element <| + contains_result = Panic.catch No_Such_Method (self.contains element) caught_panic-> + if caught_panic.payload.method_name != "contains" then Panic.throw caught_panic else + msg = "The value (" + self.to_text + ") does not support the method `contains` (at " + loc + ")." + Test.fail msg + if contains_result then Spec_Result.Success else + msg = "The value (" + self.to_text + ") did not contain the element (" + element.to_text + ") (at " + loc + ")." Test.fail msg - if contains_result then Spec_Result.Success else - msg = "The value (" + self.to_text + ") did not contain the element (" + element.to_text + ") (at " + loc + ")." - Test.fail msg ## Asserts that `self` value contains an element. @@ -654,13 +661,14 @@ Error.should_contain self element frames_to_skip=0 = Any.should_not_contain : Any -> Integer -> Spec_Result Any.should_not_contain self element frames_to_skip=0 = loc = Meta.get_source_location 1+frames_to_skip - contains_result = Panic.catch No_Such_Method (self.contains element) caught_panic-> - if caught_panic.payload.method_name != "contains" then Panic.throw caught_panic else - msg = "The value (" + self.to_text + ") does not support the method `contains` (at " + loc + ")." + rhs_error_check element <| + contains_result = Panic.catch No_Such_Method (self.contains element) caught_panic-> + if caught_panic.payload.method_name != "contains" then Panic.throw caught_panic else + msg = "The value (" + self.to_text + ") does not support the method `contains` (at " + loc + ")." + Test.fail msg + if contains_result.not then Spec_Result.Success else + msg = "The value (" + self.to_text + ") contained the element (" + element.to_text + "), but it was expected to not contain it (at " + loc + ")." Test.fail msg - if contains_result.not then Spec_Result.Success else - msg = "The value (" + self.to_text + ") contained the element (" + element.to_text + "), but it was expected to not contain it (at " + loc + ")." - Test.fail msg ## Asserts that `self` value does not contain an element. diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso index 59390252d457..bf5e8d55f795 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso @@ -2,10 +2,13 @@ from Standard.Base import all import Standard.Base.Errors.Illegal_Argument.Illegal_Argument ## PRIVATE - A helper that ensures that the value in `that` is not an error. -should_equal_error_check that ~action = + A helper that ensures that the expected value provided in some of the Test + operations is not an error. + The left-hand side may be an error and that will cause a test failure. + But the right-hand side being an error is bad test design and should be fixed. +rhs_error_check that ~action = case that.is_error of False -> action True -> - msg = "Dataflow error provided as expected value for `should_equal`. Use `should_fail_with` instead."+ ' Error stack trace was:\n'+that.get_stack_trace_text + msg = "Dataflow error ("+that.to_display_text+") provided as expected value. Use `should_fail_with` or change the test."+ ' Error stack trace was:\n'+that.get_stack_trace_text Panic.throw (Illegal_Argument.Error msg) diff --git a/test/Base_Tests/src/Data/Function_Spec.enso b/test/Base_Tests/src/Data/Function_Spec.enso index b0cd9099e11a..c808ce77d9c8 100644 --- a/test/Base_Tests/src/Data/Function_Spec.enso +++ b/test/Base_Tests/src/Data/Function_Spec.enso @@ -90,4 +90,3 @@ main filter=Nothing = suite = Test.build suite_builder-> add_specs suite_builder suite.run_with_filter filter - diff --git a/test/Table_Tests/src/Util.enso b/test/Table_Tests/src/Util.enso index 669bd6e72fb7..9f4e119bd58e 100644 --- a/test/Table_Tests/src/Util.enso +++ b/test/Table_Tests/src/Util.enso @@ -5,33 +5,33 @@ import Standard.Database.DB_Column.DB_Column from Standard.Table import Column, Table from Standard.Test import all -from Standard.Test.Extensions_Helpers import should_equal_error_check +from Standard.Test.Extensions_Helpers import rhs_error_check polyglot java import org.enso.base_test_helpers.FileSystemHelper Table.should_equal : Any -> Integer -> Any -Table.should_equal self expected frames_to_skip=0 = should_equal_error_check expected <| - loc = Meta.get_source_location 1+frames_to_skip +Table.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| + loc = Meta.get_source_location 2+frames_to_skip Panic.catch Test_Failure_Error (table_should_equal_impl self expected loc) error-> Test.fail error.payload.message Column.should_equal : Any -> Integer -> Any -Column.should_equal self expected frames_to_skip=0 = should_equal_error_check expected <| - loc = Meta.get_source_location 1+frames_to_skip +Column.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| + loc = Meta.get_source_location 2+frames_to_skip Panic.catch Test_Failure_Error (column_should_equal_impl self expected loc) error-> Test.fail error.payload.message DB_Table.should_equal : DB_Table -> Integer -> Any -DB_Table.should_equal self expected frames_to_skip=0 = should_equal_error_check expected <| +DB_Table.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| t0 = self.read t1 = expected.read - t0 . should_equal t1 frames_to_skip+1 + t0 . should_equal t1 frames_to_skip+2 DB_Column.should_equal : DB_Column -> Integer -> Any -DB_Column.should_equal self expected frames_to_skip=0 = should_equal_error_check expected <| +DB_Column.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| t0 = self.read t1 = expected.read - t0 . should_equal t1 frames_to_skip+1 + t0 . should_equal t1 frames_to_skip+2 type Test_Failure_Error ## PRIVATE From 6e0928a7a857578dcce9ec80038ea1c85d591e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 12:44:13 +0100 Subject: [PATCH 10/25] fixing more offsets --- test/Table_Tests/src/Util.enso | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/test/Table_Tests/src/Util.enso b/test/Table_Tests/src/Util.enso index 9f4e119bd58e..15837711f463 100644 --- a/test/Table_Tests/src/Util.enso +++ b/test/Table_Tests/src/Util.enso @@ -10,28 +10,30 @@ from Standard.Test.Extensions_Helpers import rhs_error_check polyglot java import org.enso.base_test_helpers.FileSystemHelper Table.should_equal : Any -> Integer -> Any -Table.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| - loc = Meta.get_source_location 2+frames_to_skip - Panic.catch Test_Failure_Error (table_should_equal_impl self expected loc) error-> - Test.fail error.payload.message +Table.should_equal self expected frames_to_skip=0 = + loc = Meta.get_source_location 1+frames_to_skip + rhs_error_check expected <| + Panic.catch Test_Failure_Error (table_should_equal_impl self expected loc) error-> + Test.fail error.payload.message Column.should_equal : Any -> Integer -> Any -Column.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| - loc = Meta.get_source_location 2+frames_to_skip - Panic.catch Test_Failure_Error (column_should_equal_impl self expected loc) error-> - Test.fail error.payload.message +Column.should_equal self expected frames_to_skip=0 = + loc = Meta.get_source_location 1+frames_to_skip + rhs_error_check expected <| + Panic.catch Test_Failure_Error (column_should_equal_impl self expected loc) error-> + Test.fail error.payload.message DB_Table.should_equal : DB_Table -> Integer -> Any DB_Table.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| t0 = self.read t1 = expected.read - t0 . should_equal t1 frames_to_skip+2 + t0 . should_equal t1 frames_to_skip+4 DB_Column.should_equal : DB_Column -> Integer -> Any DB_Column.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| t0 = self.read t1 = expected.read - t0 . should_equal t1 frames_to_skip+2 + t0 . should_equal t1 frames_to_skip+4 type Test_Failure_Error ## PRIVATE From bed6609e97953b792d31e56daef6ceac8c32b062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 12:52:25 +0100 Subject: [PATCH 11/25] fix invalid tests --- test/Base_Tests/src/Data/Range_Spec.enso | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/Base_Tests/src/Data/Range_Spec.enso b/test/Base_Tests/src/Data/Range_Spec.enso index bea200f8f01a..5bb3ecc8446f 100644 --- a/test/Base_Tests/src/Data/Range_Spec.enso +++ b/test/Base_Tests/src/Data/Range_Spec.enso @@ -405,7 +405,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r2.any (_ == 3) . should_equal False r2.all (x-> x % 2 == 0) . should_equal True r2.all (_ == 2) . should_equal False - r2.find (x-> x*x == 16) . should_equal Nothing + r2.find (x-> x*x == 16) . should_fail_with Not_Found r2.find (x-> x*x == 4) . should_equal 2 verify_contains r2 [0, 2] [-3, -2, -1, 1, 3, 4, 5] @@ -421,7 +421,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r3.any (_ == 3) . should_equal False r3.all (_ == 5) . should_equal True r3.all (_ == 3) . should_equal False - r3.find (x-> x*x == 16) . should_equal Nothing + r3.find (x-> x*x == 16) . should_fail_with Not_Found r3.find (x-> x*x == 25) . should_equal 5 verify_contains r3 [5] [0, 1, 4, 6, 7, 10] @@ -438,7 +438,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r4.all (x-> x % 2 == 1) . should_equal True r4.all (_ == 5) . should_equal False r4.find (x-> x*x == 25) . should_equal 5 - r4.find (x-> x*x == 4) . should_equal Nothing + r4.find (x-> x*x == 4) if_missing=Nothing . should_equal Nothing verify_contains r4 [5, 7] [0, 1, 4, 6, 8, 10] r5 = Range.Between 5 7 2 @@ -454,7 +454,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r5.all (x-> x % 2 == 1) . should_equal True r5.all (_ == 5) . should_equal True r5.find (x-> x*x == 25) . should_equal 5 - r5.find (x-> x*x == 4) . should_equal Nothing + r5.find (x-> x*x == 4) if_missing=Nothing . should_equal Nothing verify_contains r5 [5] [0, 1, 4, 6, 7, 10] r6 = Range.Between 0 10 3 @@ -470,7 +470,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r6.all (x-> x % 2 == 0) . should_equal False r6.all (x-> x % 3 == 0) . should_equal True r6.find (x-> x*x == 9) . should_equal 3 - r6.find (x-> x*x == 25) . should_equal Nothing + r6.find (x-> x*x == 25) if_missing=Nothing . should_equal Nothing r6.filter (_ < 4) . should_equal [0, 3] verify_contains r6 [0, 3, 6, 9] [-3, -2, -1, 1, 2, 4, 5, 7, 8, 10, 11] @@ -504,7 +504,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r2.all (x-> x % 2 == 0) . should_equal True r2.all (_ > 2) . should_equal False r2.find (x-> x*x == 16) . should_equal 4 - r2.find (x-> x*x == 0) . should_equal Nothing + r2.find (x-> x*x == 0) . should_fail_with Not_Found verify_contains r2 [4, 2] [-2, -1, 0, 1, 3, 5, 6, 7, 10] r3 = Range.Between 4 0 -10 @@ -520,7 +520,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r3.all (x-> x % 2 == 0) . should_equal True r3.all (_ > 4) . should_equal False r3.find (x-> x*x == 16) . should_equal 4 - r3.find (x-> x*x == 0) . should_equal Nothing + r3.find (x-> x*x == 0) . should_fail_with Not_Found verify_contains r3 [4] [-2, -1, 0, 1, 2, 3, 5, 6, 7, 10] r4 = Range.Between 3 0 -3 @@ -536,7 +536,7 @@ add_specs suite_builder = suite_builder.group "Range" group_builder-> r4.all (x-> x % 2 == 0) . should_equal False r4.all (_ > 0) . should_equal True r4.find (x-> x*x == 9) . should_equal 3 - r4.find (x-> x*x == 0) . should_equal Nothing + r4.find (x-> x*x == 0) . should_fail_with Not_Found verify_contains r4 [3] [-3, -2, -1, 0, 1, 2, 4, 5, 6, 7, 10] group_builder.specify "should report errors if trying to set step to 0" <| @@ -585,4 +585,3 @@ main filter=Nothing = suite = Test.build suite_builder-> add_specs suite_builder suite.run_with_filter filter - From 1647c53fcca1374fdbb72c7c8e12810ba1ed2405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 12:54:25 +0100 Subject: [PATCH 12/25] remove unnecessary Not_Found type (clashing with another existing one) --- .../Base/0.0.0-dev/src/Data/Index_Sub_Range.enso | 11 ++++------- .../0.0.0-dev/src/Internal/Array_Like_Helpers.enso | 7 ++----- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso index 2befdfcc054d..360497b52657 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso @@ -9,7 +9,6 @@ import project.Errors.Common.Missing_Argument import project.Errors.Common.Type_Error import project.Errors.Illegal_Argument.Illegal_Argument import project.Function.Function -import project.Internal.Array_Like_Helpers.Not_Found import project.Math import project.Meta import project.Metadata.Display @@ -205,9 +204,8 @@ take_helper length at single_slice slice_ranges range:(Index_Sub_Range | Range | Index_Sub_Range.First count -> single_slice 0 (length.min count) Index_Sub_Range.Last count -> single_slice length-count length Index_Sub_Range.While predicate -> - end = 0.up_to length . find (i-> (predicate (at i)).not) if_missing=Not_Found.Value - true_end = if end == Not_Found.Value then length else end - single_slice 0 true_end + end = 0.up_to length . find (i-> (predicate (at i)).not) if_missing=length + single_slice 0 end Index_Sub_Range.By_Index one_or_many_descriptors -> Panic.recover [Index_Out_Of_Bounds, Illegal_Argument] <| indices = case one_or_many_descriptors of _ : Vector -> one_or_many_descriptors @@ -256,9 +254,8 @@ drop_helper length at single_slice slice_ranges range:(Index_Sub_Range | Range | Index_Sub_Range.First count -> single_slice count length Index_Sub_Range.Last count -> single_slice 0 length-count Index_Sub_Range.While predicate -> - end = 0.up_to length . find (i-> (predicate (at i)).not) if_missing=Not_Found.Value - true_end = if end == Not_Found.Value then length else end - single_slice true_end length + end = 0.up_to length . find (i-> (predicate (at i)).not) if_missing=length + single_slice end length Index_Sub_Range.By_Index one_or_many_descriptors -> Panic.recover [Index_Out_Of_Bounds, Illegal_Argument] <| indices = case one_or_many_descriptors of _ : Vector -> one_or_many_descriptors diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso index a0a53669230a..b2351bcde3b7 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso @@ -30,9 +30,6 @@ from project.Data.Index_Sub_Range import drop_helper, take_helper from project.Data.Ordering import Comparable from project.Data.Range.Extensions import all -type Not_Found - Value - new_array_proxy_builtin : Integer -> (Integer -> Any) -> Array new_array_proxy_builtin length at = @Builtin_Method "Array_Like_Helpers.new_array_proxy_builtin" @@ -219,8 +216,8 @@ find vector condition start ~if_missing = predicate = unify_condition_or_predicate condition self_len = vector.length check_start_valid start self_len used_start-> - found = used_start.up_to self_len . find (idx -> (predicate (vector.at idx))) if_missing=Not_Found.Value - if found == Not_Found.Value then if_missing else vector.at found + found = used_start.up_to self_len . find (idx -> (predicate (vector.at idx))) if_missing=Nothing + if found.is_nothing then if_missing else vector.at found transpose vec_of_vecs = if vec_of_vecs.is_empty then [] else From ef803390638b97cd17ad2f9299505cae37ecd813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 13:00:13 +0100 Subject: [PATCH 13/25] remove check that was actually too strict --- distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso | 4 +--- test/Base_Tests/src/Random_Spec.enso | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso index a87a9ed86119..d45fd2958ee7 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso @@ -562,9 +562,7 @@ Error.should_equal_ignoring_order self that frames_to_skip=0 = Any.should_only_contain_elements_in : Any -> Integer -> Spec_Result Any.should_only_contain_elements_in self that frames_to_skip=0 = loc = Meta.get_source_location 1+frames_to_skip - if that.is_a Vector . not then - Panic.throw (Illegal_Argument.Error "Expected a Vector, but got a "+that.to_display_text+" (at "+loc+").") - self.each element-> + rhs_error_check that <| self.each element-> if that.contains element . not then msg = "The collection contained an element ("+element.to_text+") which was not expected (at " + loc + ")." Test.fail msg diff --git a/test/Base_Tests/src/Random_Spec.enso b/test/Base_Tests/src/Random_Spec.enso index ecb2b6ac07cb..980dc33707a2 100644 --- a/test/Base_Tests/src/Random_Spec.enso +++ b/test/Base_Tests/src/Random_Spec.enso @@ -157,4 +157,3 @@ main filter=Nothing = suite = Test.build suite_builder-> add_specs suite_builder suite.run_with_filter filter - From a8571c4bc77f6048e5b3fe90a9c733b3f9afadc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 13:08:44 +0100 Subject: [PATCH 14/25] fix cloud test cleanup --- test/Base_Tests/src/System/File_Spec.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Base_Tests/src/System/File_Spec.enso b/test/Base_Tests/src/System/File_Spec.enso index f6d6ff9d7452..22972f80d965 100644 --- a/test/Base_Tests/src/System/File_Spec.enso +++ b/test/Base_Tests/src/System/File_Spec.enso @@ -391,7 +391,7 @@ add_specs suite_builder = subdir.should_succeed cleanup = Enso_User.flush_caches - subdir.delete + subdir.delete recursive=True Panic.with_finalizer cleanup <| Test_Environment.unsafe_with_environment_override "ENSO_CLOUD_PROJECT_DIRECTORY_PATH" subdir.path <| # Flush caches to ensure fresh dir is used From 6551756287e17a2b94a00e5e8d4da73acc93139b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 13:33:37 +0100 Subject: [PATCH 15/25] fix a test which was propagating error and crashing suite builder --- test/Table_Tests/src/Common_Table_Operations/Nothing_Spec.enso | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Table_Tests/src/Common_Table_Operations/Nothing_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Nothing_Spec.enso index 43596f79bbd8..e929623dc265 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Nothing_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Nothing_Spec.enso @@ -1,4 +1,5 @@ from Standard.Base import all +import Standard.Base.Errors.Common.Incomparable_Values from Standard.Table import all @@ -257,7 +258,7 @@ add_nothing_specs suite_builder setup = other_value = triple.at 1 value_type = triple.at 2 - is_comparable = case Ordering.compare value other_value of + is_comparable = case Ordering.compare value other_value . catch Incomparable_Values of _:Ordering -> True _ -> False From 09fb17f47d3de3c04bf1731c94acae7518ed9b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 13:50:54 +0100 Subject: [PATCH 16/25] fixing other tests that break Table Tests progress --- test/Table_Tests/src/Database/SQLite_Spec.enso | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 1c10b7d3b1ea..97f56f00c2c7 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -82,7 +82,7 @@ type Tables_And_Table_Types_Data teardown self = self.connection.drop_table self.tinfo - self.connection.drop_table self.vinfo + self.connection.execute_update "DROP VIEW '"+self.vinfo+"'" self.connection.drop_table self.temporary_table self.connection.close @@ -388,7 +388,7 @@ create_inmem_connection = create_file_connection file = connection = Database.connect (SQLite.From_File file) - connection.execute_update 'CREATE TABLE "Dummy" ("strs" VARCHAR, "ints" INTEGER, "bools" BOOLEAN, "reals" REAL)' + connection.execute_update 'CREATE TABLE IF NOT EXISTS "Dummy" ("strs" VARCHAR, "ints" INTEGER, "bools" BOOLEAN, "reals" REAL)' connection type File_Connection From 3b8c08914988626f2508d1ee192f69440c5e54f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 18:34:22 +0100 Subject: [PATCH 17/25] fixing more issues in Table tests --- .../Test/0.0.0-dev/src/Extensions.enso | 7 ++++++- .../Cross_Tab_Spec.enso | 1 - .../Table_Tests/src/Database/SQLite_Spec.enso | 21 ++++++------------- .../Table_Tests/src/Database/Upload_Spec.enso | 2 +- test/Table_Tests/src/IO/Fetch_Spec.enso | 8 ++++--- .../src/In_Memory/Split_Tokenize_Spec.enso | 11 +++++----- 6 files changed, 23 insertions(+), 27 deletions(-) diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso index d45fd2958ee7..234b57e74420 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso @@ -1,4 +1,5 @@ from Standard.Base import all +import Standard.Base.Errors.Common.Incomparable_Values import Standard.Base.Errors.Common.No_Such_Method import Standard.Base.Errors.Illegal_Argument.Illegal_Argument @@ -299,7 +300,11 @@ Number.should_equal self that epsilon=0 frames_to_skip=0 = loc = Meta.get_source_location 1+frames_to_skip rhs_error_check that <| matches = case that of - n : Number -> self.equals n epsilon + n : Number -> self.equals n epsilon . catch Incomparable_Values _-> + ## Incomparable_Values is thrown if one of the values is NaN. + We fallback to is_same_object_as, + because in tests we actually NaN.should_equal NaN to succeed. + self.is_same_object_as n _ -> self==that case matches of True -> Spec_Result.Success diff --git a/test/Table_Tests/src/Common_Table_Operations/Cross_Tab_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Cross_Tab_Spec.enso index c0d17b70a245..ad79702d5c8b 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Cross_Tab_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Cross_Tab_Spec.enso @@ -252,7 +252,6 @@ add_specs suite_builder setup = r1.catch.to_display_text . should_contain "cannot contain the NUL character" r2 = data.table2.cross_tab [] "Key" values=[Aggregate_Column.Average "Value" as='x\0'] - r2.print r2.should_fail_with Invalid_Column_Names r2.catch.to_display_text . should_contain "cannot contain the NUL character" diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 97f56f00c2c7..8817aaa11c56 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -54,8 +54,8 @@ type Metadata_Data [connection, tinfo, t] teardown self = - self.connection.drop_table self.t.name - self.connection.drop_table self.tinfo + self.connection.drop_table self.t.name if_exists=True + self.connection.drop_table self.tinfo if_exists=True self.connection.close @@ -81,9 +81,9 @@ type Tables_And_Table_Types_Data [connection, tinfo, vinfo, temporary_table] teardown self = - self.connection.drop_table self.tinfo - self.connection.execute_update "DROP VIEW '"+self.vinfo+"'" - self.connection.drop_table self.temporary_table + self.connection.drop_table self.tinfo if_exists=True + self.connection.execute_update "DROP VIEW IF EXISTS '"+self.vinfo+"'" + self.connection.drop_table self.temporary_table if_exists=True self.connection.close @@ -377,9 +377,7 @@ type Database_File Value ~file create = Database_File.Value <| - transient_dir = enso_project.data / "transient" - assert transient_dir.exists ("The directory " + transient_dir.path + " should exist (ensured by containing a `.gitignore` file).") - f = transient_dir / "sqlite_test.db" + f = File.create_temporary_file "sqlite-test" ".db" f.delete_if_exists f @@ -401,10 +399,6 @@ type File_Connection assert tmp_file.exists tmp_file - teardown self = - assert self.file.exists - self.file.delete - add_specs suite_builder = in_file_prefix = "[SQLite File] " @@ -425,9 +419,6 @@ add_specs suite_builder = suite_builder.group "SQLite_Format should allow connecting to SQLite files" group_builder-> data = File_Connection.setup database_file - group_builder.teardown <| - data.teardown - group_builder.specify "should recognise a SQLite database file" <| Auto_Detect.get_reading_format data.file . should_be_a SQLite_Format diff --git a/test/Table_Tests/src/Database/Upload_Spec.enso b/test/Table_Tests/src/Database/Upload_Spec.enso index ffaa385e9e3c..dce15a1d1fbb 100644 --- a/test/Table_Tests/src/Database/Upload_Spec.enso +++ b/test/Table_Tests/src/Database/Upload_Spec.enso @@ -498,7 +498,7 @@ add_specs suite_builder setup make_new_connection persistent_connector=True = copied_table = db_table.select_into_database_table tmp_connection (Name_Generator.random_name "copied-table") temporary=False copied_table.is_trivial_query . should_be_true name = copied_table.name - Panic.with_finalizer (data.connection.drop_table name) <| + Panic.with_finalizer (data.connection.drop_table name if_exists=True) <| setup.expect_integer_type <| copied_table.at "X" copied_table.at "Y" . value_type . is_text . should_be_true copied_table.at "Z" . value_type . is_floating_point . should_be_true diff --git a/test/Table_Tests/src/IO/Fetch_Spec.enso b/test/Table_Tests/src/IO/Fetch_Spec.enso index b1c33445065f..000ea57c8808 100644 --- a/test/Table_Tests/src/IO/Fetch_Spec.enso +++ b/test/Table_Tests/src/IO/Fetch_Spec.enso @@ -4,8 +4,10 @@ import Standard.Base.Errors.Common.Response_Too_Large import Standard.Base.Errors.File_Error.File_Error import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Base.Network.HTTP.Cache_Policy.Cache_Policy +import Standard.Base.Network.HTTP.HTTP_Error.HTTP_Error import Standard.Base.Network.HTTP.Request.Request import Standard.Base.Network.HTTP.Request_Body.Request_Body +import Standard.Base.Network.HTTP.Request_Error import Standard.Base.Network.HTTP.Response.Response import Standard.Base.Runtime.Context import Standard.Base.Runtime.Ref.Ref @@ -412,11 +414,11 @@ add_specs suite_builder = group_builder.specify "Should not cache if the request fails" pending=pending_has_url <| Test.with_retries <| with_default_cache <| - HTTP.fetch url0 + HTTP.fetch url0 . should_succeed get_num_response_cache_entries . should_equal 1 - HTTP.fetch base_url_with_slash+'crash' + HTTP.fetch base_url_with_slash+'crash' . should_fail_with Request_Error get_num_response_cache_entries . should_equal 1 - HTTP.fetch base_url_with_slash+'nonexistent_endpoint' + HTTP.fetch base_url_with_slash+'nonexistent_endpoint' . should_fail_with HTTP_Error get_num_response_cache_entries . should_equal 1 cloud_setup = Cloud_Tests_Setup.prepare diff --git a/test/Table_Tests/src/In_Memory/Split_Tokenize_Spec.enso b/test/Table_Tests/src/In_Memory/Split_Tokenize_Spec.enso index 2f365d7ce65c..bfdb332f3199 100644 --- a/test/Table_Tests/src/In_Memory/Split_Tokenize_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Split_Tokenize_Spec.enso @@ -2,7 +2,7 @@ from Standard.Base import all import Standard.Test.Extensions -from Standard.Table import Table +from Standard.Table import Table, Value_Type from Standard.Table.Columns_To_Add import Columns_To_Add from Standard.Table.Errors import Invalid_Value_Type, Column_Count_Exceeded, Duplicate_Output_Column_Names, No_Such_Column from Standard.Test import all @@ -204,7 +204,7 @@ add_specs suite_builder = cols = [["foo", [0, 1, 2]], ["bar", ["abc", "cbdbef", "ghbijbu"]]] t = Table.new cols expected_rows = [[0, "a", "c", Nothing, Nothing], [1, "c", "d", "ef", Nothing], [2, "gh", "ij", "u", Nothing]] - expected = Table.from_rows ["foo", "bar 1", "bar 2", "bar 3", "bar 3"] expected_rows + expected = Table.from_rows ["foo", "bar 1", "bar 2", "bar 3", "bar 4"] expected_rows . cast "bar 4" Value_Type.Char t2 = t.split_to_columns "bar" "b" column_count=(Columns_To_Add.First 4) t2.should_equal expected t2.at "bar 3" . value_type . is_text . should_be_true @@ -213,7 +213,7 @@ add_specs suite_builder = cols = [["foo", [0, 1, 2]], ["bar", ["abc", "cbdbef", "ghbijbu"]]] t = Table.new cols expected_rows = [[0, "a", "c", Nothing, Nothing], [1, "c", "d", "ef", Nothing], [2, "gh", "ij", "u", Nothing]] - expected = Table.from_rows ["foo", "bar 1", "bar 2", "bar 3", "bar 3"] expected_rows + expected = Table.from_rows ["foo", "bar 1", "bar 2", "bar 3", "bar 4"] expected_rows . cast "bar 4" Value_Type.Char t2 = t.split_to_columns "bar" "b" column_count=4 t2.should_equal expected t2.at "bar 3" . value_type . is_text . should_be_true @@ -262,7 +262,7 @@ add_specs suite_builder = cols = [["foo", [0, 1, 2]], ["bar", ["ghbijbu", "cbdbef", "abc"]]] t = Table.new cols expected_rows = [[0, "gh", "ij", "u", Nothing], [1, "c", "d", "ef", Nothing], [2, "a", "c", Nothing, Nothing]] - expected = Table.from_rows ["foo", "bar 1", "bar 2", "bar 3", "bar 3"] expected_rows + expected = Table.from_rows ["foo", "bar 1", "bar 2", "bar 3", "bar 4"] expected_rows . cast "bar 4" Value_Type.Char t2 = t.split_to_columns "bar" "b" column_count=(Columns_To_Add.First 4) t2.should_equal expected t2.at "bar 3" . value_type . is_text . should_be_true @@ -271,7 +271,7 @@ add_specs suite_builder = cols = [["foo", [0, 1, 2]], ["bar", ["ghbijbu", "cbdbef", "abc"]]] t = Table.new cols expected_rows = [[0, "gh", "ij", "u", Nothing], [1, "c", "d", "ef", Nothing], [2, "a", "c", Nothing, Nothing]] - expected = Table.from_rows ["foo", "bar 1", "bar 2", "bar 3", "bar 3"] expected_rows + expected = Table.from_rows ["foo", "bar 1", "bar 2", "bar 3", "bar 4"] expected_rows . cast "bar 4" Value_Type.Char t2 = t.split_to_columns "bar" "b" column_count=4 t2.should_equal expected t2.at "bar 3" . value_type . is_text . should_be_true @@ -433,4 +433,3 @@ main filter=Nothing = suite = Test.build suite_builder-> add_specs suite_builder suite.run_with_filter filter - From 434ba8a21ff3875774fdc8574d18b90353cb87a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Tue, 17 Dec 2024 19:12:46 +0100 Subject: [PATCH 18/25] enable Context to fix lazy init issue --- test/Table_Tests/src/Database/SQLite_Spec.enso | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/Table_Tests/src/Database/SQLite_Spec.enso b/test/Table_Tests/src/Database/SQLite_Spec.enso index 8817aaa11c56..5ea4e8ce960b 100644 --- a/test/Table_Tests/src/Database/SQLite_Spec.enso +++ b/test/Table_Tests/src/Database/SQLite_Spec.enso @@ -3,6 +3,7 @@ import Standard.Base.Runtime.Ref.Ref from Standard.Base.Runtime import assert import Standard.Base.Errors.File_Error.File_Error import Standard.Base.Errors.Illegal_Argument.Illegal_Argument +import Standard.Base.Runtime.Context from Standard.Table import Table, Value_Type, Bits from Standard.Table.Errors import Invalid_Column_Names, Duplicate_Output_Column_Names @@ -386,7 +387,10 @@ create_inmem_connection = create_file_connection file = connection = Database.connect (SQLite.From_File file) - connection.execute_update 'CREATE TABLE IF NOT EXISTS "Dummy" ("strs" VARCHAR, "ints" INTEGER, "bools" BOOLEAN, "reals" REAL)' + ## We need to re-enable the context because this initializer may be executed + lazily in a context where Output was disabled (e.g. Upload_Spec). + Context.Output.with_enabled <| + connection.execute_update 'CREATE TABLE IF NOT EXISTS "Dummy" ("strs" VARCHAR, "ints" INTEGER, "bools" BOOLEAN, "reals" REAL)' connection type File_Connection From 87483493faad099a59b6bd8301237114dc7b2f51 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Dec 2024 07:42:42 +0100 Subject: [PATCH 19/25] Speculate on statements returning Nothing --- .../interpreter/node/callable/function/BlockNode.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java index a50ccedbbdbb..41d51a54a9ee 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java @@ -6,6 +6,7 @@ import com.oracle.truffle.api.instrumentation.Tag; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.source.SourceSection; import java.util.Set; import org.enso.interpreter.node.ExpressionNode; @@ -18,11 +19,17 @@ */ @NodeInfo(shortName = "Block") public class BlockNode extends ExpressionNode { + private final BranchProfile unexpectedReturnValue; @Children private final ExpressionNode[] statements; @Child private ExpressionNode returnExpr; private BlockNode(ExpressionNode[] expressions, ExpressionNode returnExpr) { this.statements = expressions; + if (expressions.length > 0) { + this.unexpectedReturnValue = BranchProfile.create(); + } else { + this.unexpectedReturnValue = BranchProfile.getUncached(); + } this.returnExpr = returnExpr; } @@ -62,6 +69,7 @@ public Object executeGeneric(VirtualFrame frame) { for (ExpressionNode statement : statements) { var result = statement.executeGeneric(frame); if (result != nothing) { + unexpectedReturnValue.enter(); if (result instanceof DataflowError err) { return err; } From b7430cb8cbda0536bc5db283550c0f88637ae3e6 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Dec 2024 07:58:13 +0100 Subject: [PATCH 20/25] Code style issues found in CHANGELOG.md file --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d24a03f14e..b5b0634ba4f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - [Propagate Error ASAP instead of ignoring it][11777]. [11777]: https://github.com/enso-org/enso/pull/11777 + - [Intersection types & type checks][11600] - A constructor or type definition with a single inline argument definition was previously allowed to use spaces in the argument definition without From fe5b9e1c3736877dc522d804fb2f026a7425606e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Wed, 18 Dec 2024 11:54:37 +0100 Subject: [PATCH 21/25] duplicate rhs_error_check and make it imperative to avoid nesting --- .../Test/0.0.0-dev/src/Extensions.enso | 80 ++++++++++--------- .../0.0.0-dev/src/Extensions_Helpers.enso | 12 +-- test/Table_Tests/src/Util.enso | 34 +++++--- 3 files changed, 72 insertions(+), 54 deletions(-) diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso index 234b57e74420..3c107631bc1a 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso @@ -5,7 +5,6 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import project.Spec_Result.Spec_Result import project.Test.Test -from project.Extensions_Helpers import rhs_error_check ## Expect a function to fail with the provided dataflow error. @@ -72,7 +71,8 @@ Error.should_fail_with self matcher frames_to_skip=0 unwrap_errors=True = example_should_equal = Examples.add_1_to 1 . should_equal 2 Any.should_equal : Any -> Integer -> Spec_Result -Any.should_equal self that frames_to_skip=0 = rhs_error_check that <| +Any.should_equal self that frames_to_skip=0 = + rhs_error_check that loc = Meta.get_source_location 4+frames_to_skip case self == that of True -> Spec_Result.Success @@ -188,8 +188,9 @@ Error.should_not_equal_type self that frames_to_skip=0 = example_should_start_with = "Hello World!" . should_start_with "Hello" Any.should_start_with : Text -> Integer -> Spec_Result Any.should_start_with self that frames_to_skip=0 = + rhs_error_check that loc = Meta.get_source_location 1+frames_to_skip - rhs_error_check that <| case self of + case self of _ : Text -> if self.starts_with that then Spec_Result.Success else msg = self.to_text + " does not start with " + that.to_text + " (at " + loc + ")." Test.fail msg @@ -212,8 +213,9 @@ Any.should_start_with self that frames_to_skip=0 = example_should_end_with = "Hello World!" . should_end_with "ld!" Any.should_end_with : Text -> Integer -> Spec_Result Any.should_end_with self that frames_to_skip=0 = + rhs_error_check that loc = Meta.get_source_location 1+frames_to_skip - rhs_error_check that <| case self of + case self of _ : Text -> if self.ends_with that then Spec_Result.Success else msg = self.to_text + " does not end with " + that.to_text + " (at " + loc + ")." Test.fail msg @@ -270,8 +272,9 @@ Error.should_end_with self that frames_to_skip=0 = example_should_equal = Examples.add_1_to 1 . should_equal 2 Error.should_equal : Any -> Integer -> Spec_Result -Error.should_equal self that frames_to_skip=0 = rhs_error_check that <| - Test.fail_match_on_unexpected_error self 4+frames_to_skip +Error.should_equal self that frames_to_skip=0 = + rhs_error_check that + Test.fail_match_on_unexpected_error self 2+frames_to_skip ## Asserts that `self` is within `epsilon` from `that`. @@ -297,20 +300,20 @@ Error.should_equal self that frames_to_skip=0 = rhs_error_check that <| 1.00000001 . should_equal 1.00000002 epsilon=0.0001 Number.should_equal : Float -> Float -> Integer -> Spec_Result Number.should_equal self that epsilon=0 frames_to_skip=0 = + rhs_error_check that loc = Meta.get_source_location 1+frames_to_skip - rhs_error_check that <| - matches = case that of - n : Number -> self.equals n epsilon . catch Incomparable_Values _-> - ## Incomparable_Values is thrown if one of the values is NaN. - We fallback to is_same_object_as, - because in tests we actually NaN.should_equal NaN to succeed. - self.is_same_object_as n - _ -> self==that - case matches of - True -> Spec_Result.Success - False -> - msg = self.to_text + " did not equal " + that.to_text + " (at " + loc + ")." - Test.fail msg + matches = case that of + n : Number -> self.equals n epsilon . catch Incomparable_Values _-> + ## Incomparable_Values is thrown if one of the values is NaN. + We fallback to is_same_object_as, + because in tests we actually NaN.should_equal NaN to succeed. + self.is_same_object_as n + _ -> self==that + case matches of + True -> Spec_Result.Success + False -> + msg = self.to_text + " did not equal " + that.to_text + " (at " + loc + ")." + Test.fail msg ## Asserts that `self` is within `epsilon` from `that`. @@ -320,8 +323,9 @@ Number.should_equal self that epsilon=0 frames_to_skip=0 = - frames_to_skip (optional, advanced): used to alter the location which is displayed as the source of this error. Decimal.should_equal : Number -> Float-> Float -> Integer -> Spec_Result -Decimal.should_equal self that epsilon=0 frames_to_skip=0 = rhs_error_check that <| - self.to_float . should_equal that.to_float epsilon frames_to_skip+4 +Decimal.should_equal self that epsilon=0 frames_to_skip=0 = + rhs_error_check that + self.to_float . should_equal that.to_float epsilon frames_to_skip+2 ## Asserts that `self` value is not an error. @@ -431,11 +435,12 @@ Error.should_be_false self = Test.fail_match_on_unexpected_error self 1 example_should_be_a = 1.should_be_a Boolean Any.should_be_a : Any -> Spec_Result Any.should_be_a self typ = + rhs_error_check typ loc = Meta.get_source_location 1 fail_on_wrong_arg_type = Panic.throw <| Illegal_Argument.Error "typ ("+typ.to_display_text+") must either be a type or a constructor. Use `should_equal` for value equality test instead." - rhs_error_check typ <| case Meta.meta typ of + case Meta.meta typ of c : Meta.Constructor -> case Meta.meta self of a : Meta.Atom -> if a.constructor == c then Spec_Result.Success else @@ -566,8 +571,9 @@ Error.should_equal_ignoring_order self that frames_to_skip=0 = example_should_equal = [1, 2] . should_only_contain_elements_in [1, 2, 3, 4] Any.should_only_contain_elements_in : Any -> Integer -> Spec_Result Any.should_only_contain_elements_in self that frames_to_skip=0 = + rhs_error_check that loc = Meta.get_source_location 1+frames_to_skip - rhs_error_check that <| self.each element-> + self.each element-> if that.contains element . not then msg = "The collection contained an element ("+element.to_text+") which was not expected (at " + loc + ")." Test.fail msg @@ -619,15 +625,15 @@ Error.should_only_contain_elements_in self that frames_to_skip=0 = example_should_equal = "foobar".should_contain "foo" Any.should_contain : Any -> Integer -> Spec_Result Any.should_contain self element frames_to_skip=0 = + rhs_error_check element loc = Meta.get_source_location 1+frames_to_skip - rhs_error_check element <| - contains_result = Panic.catch No_Such_Method (self.contains element) caught_panic-> - if caught_panic.payload.method_name != "contains" then Panic.throw caught_panic else - msg = "The value (" + self.to_text + ") does not support the method `contains` (at " + loc + ")." - Test.fail msg - if contains_result then Spec_Result.Success else - msg = "The value (" + self.to_text + ") did not contain the element (" + element.to_text + ") (at " + loc + ")." + contains_result = Panic.catch No_Such_Method (self.contains element) caught_panic-> + if caught_panic.payload.method_name != "contains" then Panic.throw caught_panic else + msg = "The value (" + self.to_text + ") does not support the method `contains` (at " + loc + ")." Test.fail msg + if contains_result then Spec_Result.Success else + msg = "The value (" + self.to_text + ") did not contain the element (" + element.to_text + ") (at " + loc + ")." + Test.fail msg ## Asserts that `self` value contains an element. @@ -663,15 +669,15 @@ Error.should_contain self element frames_to_skip=0 = implementing a method `contains : a -> Boolean`. Any.should_not_contain : Any -> Integer -> Spec_Result Any.should_not_contain self element frames_to_skip=0 = + rhs_error_check element loc = Meta.get_source_location 1+frames_to_skip - rhs_error_check element <| - contains_result = Panic.catch No_Such_Method (self.contains element) caught_panic-> - if caught_panic.payload.method_name != "contains" then Panic.throw caught_panic else - msg = "The value (" + self.to_text + ") does not support the method `contains` (at " + loc + ")." - Test.fail msg - if contains_result.not then Spec_Result.Success else - msg = "The value (" + self.to_text + ") contained the element (" + element.to_text + "), but it was expected to not contain it (at " + loc + ")." + contains_result = Panic.catch No_Such_Method (self.contains element) caught_panic-> + if caught_panic.payload.method_name != "contains" then Panic.throw caught_panic else + msg = "The value (" + self.to_text + ") does not support the method `contains` (at " + loc + ")." Test.fail msg + if contains_result.not then Spec_Result.Success else + msg = "The value (" + self.to_text + ") contained the element (" + element.to_text + "), but it was expected to not contain it (at " + loc + ")." + Test.fail msg ## Asserts that `self` value does not contain an element. diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso index bf5e8d55f795..73f4437ef8bf 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions_Helpers.enso @@ -1,3 +1,5 @@ +private + from Standard.Base import all import Standard.Base.Errors.Illegal_Argument.Illegal_Argument @@ -6,9 +8,7 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument operations is not an error. The left-hand side may be an error and that will cause a test failure. But the right-hand side being an error is bad test design and should be fixed. -rhs_error_check that ~action = - case that.is_error of - False -> action - True -> - msg = "Dataflow error ("+that.to_display_text+") provided as expected value. Use `should_fail_with` or change the test."+ ' Error stack trace was:\n'+that.get_stack_trace_text - Panic.throw (Illegal_Argument.Error msg) +rhs_error_check that = + if that.is_error then + msg = "Dataflow error ("+that.to_display_text+") provided as expected value. Use `should_fail_with` or change the test."+ ' Error stack trace was:\n'+that.get_stack_trace_text + Panic.throw (Illegal_Argument.Error msg) diff --git a/test/Table_Tests/src/Util.enso b/test/Table_Tests/src/Util.enso index 15837711f463..c6cecb4d8a51 100644 --- a/test/Table_Tests/src/Util.enso +++ b/test/Table_Tests/src/Util.enso @@ -5,35 +5,36 @@ import Standard.Database.DB_Column.DB_Column from Standard.Table import Column, Table from Standard.Test import all -from Standard.Test.Extensions_Helpers import rhs_error_check polyglot java import org.enso.base_test_helpers.FileSystemHelper Table.should_equal : Any -> Integer -> Any Table.should_equal self expected frames_to_skip=0 = loc = Meta.get_source_location 1+frames_to_skip - rhs_error_check expected <| - Panic.catch Test_Failure_Error (table_should_equal_impl self expected loc) error-> - Test.fail error.payload.message + rhs_error_check expected + Panic.catch Test_Failure_Error (table_should_equal_impl self expected loc) error-> + Test.fail error.payload.message Column.should_equal : Any -> Integer -> Any Column.should_equal self expected frames_to_skip=0 = loc = Meta.get_source_location 1+frames_to_skip - rhs_error_check expected <| - Panic.catch Test_Failure_Error (column_should_equal_impl self expected loc) error-> - Test.fail error.payload.message + rhs_error_check expected + Panic.catch Test_Failure_Error (column_should_equal_impl self expected loc) error-> + Test.fail error.payload.message DB_Table.should_equal : DB_Table -> Integer -> Any -DB_Table.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| +DB_Table.should_equal self expected frames_to_skip=0 = + rhs_error_check expected t0 = self.read t1 = expected.read - t0 . should_equal t1 frames_to_skip+4 + t0 . should_equal t1 frames_to_skip+2 DB_Column.should_equal : DB_Column -> Integer -> Any -DB_Column.should_equal self expected frames_to_skip=0 = rhs_error_check expected <| +DB_Column.should_equal self expected frames_to_skip=0 = + rhs_error_check expected t0 = self.read t1 = expected.read - t0 . should_equal t1 frames_to_skip+4 + t0 . should_equal t1 frames_to_skip+2 type Test_Failure_Error ## PRIVATE @@ -145,3 +146,14 @@ Error.should_have_relative_ordering self example = loc = Meta.get_source_location 1 _ = example Test.fail "Expected a vector but got a dataflow error "+self.catch.to_display_text+" (at "+loc+")." + + +## PRIVATE + A helper that ensures that the expected value provided in some of the Test + operations is not an error. + The left-hand side may be an error and that will cause a test failure. + But the right-hand side being an error is bad test design and should be fixed. +rhs_error_check that = + if that.is_error then + msg = "Dataflow error ("+that.to_display_text+") provided as expected value. Use `should_fail_with` or change the test."+ ' Error stack trace was:\n'+that.get_stack_trace_text + Panic.throw (Illegal_Argument.Error msg) From 3ce19c7d984793542481db468505e8137296d960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Wed, 18 Dec 2024 13:59:36 +0100 Subject: [PATCH 22/25] fix offsets and finally add tests for this --- .../Test/0.0.0-dev/src/Extensions.enso | 7 +- test/Table_Tests/src/Util.enso | 5 +- test/Table_Tests/src/Util_Spec.enso | 66 +++++++++++++- test/Test_Tests/package.yaml | 7 ++ test/Test_Tests/src/Extensions_Spec.enso | 86 +++++++++++++++++++ test/Test_Tests/src/Helpers.enso | 17 ++++ test/Test_Tests/src/Main.enso | 13 +++ 7 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 test/Test_Tests/package.yaml create mode 100644 test/Test_Tests/src/Extensions_Spec.enso create mode 100644 test/Test_Tests/src/Helpers.enso create mode 100644 test/Test_Tests/src/Main.enso diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso index 3c107631bc1a..ff852368a507 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso @@ -5,6 +5,7 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import project.Spec_Result.Spec_Result import project.Test.Test +from project.Extensions_Helpers import rhs_error_check ## Expect a function to fail with the provided dataflow error. @@ -73,7 +74,7 @@ Error.should_fail_with self matcher frames_to_skip=0 unwrap_errors=True = Any.should_equal : Any -> Integer -> Spec_Result Any.should_equal self that frames_to_skip=0 = rhs_error_check that - loc = Meta.get_source_location 4+frames_to_skip + loc = Meta.get_source_location 1+frames_to_skip case self == that of True -> Spec_Result.Success False -> @@ -274,7 +275,7 @@ Error.should_end_with self that frames_to_skip=0 = Error.should_equal : Any -> Integer -> Spec_Result Error.should_equal self that frames_to_skip=0 = rhs_error_check that - Test.fail_match_on_unexpected_error self 2+frames_to_skip + Test.fail_match_on_unexpected_error self 1+frames_to_skip ## Asserts that `self` is within `epsilon` from `that`. @@ -325,7 +326,7 @@ Number.should_equal self that epsilon=0 frames_to_skip=0 = Decimal.should_equal : Number -> Float-> Float -> Integer -> Spec_Result Decimal.should_equal self that epsilon=0 frames_to_skip=0 = rhs_error_check that - self.to_float . should_equal that.to_float epsilon frames_to_skip+2 + self.to_float . should_equal that.to_float epsilon frames_to_skip+1 ## Asserts that `self` value is not an error. diff --git a/test/Table_Tests/src/Util.enso b/test/Table_Tests/src/Util.enso index c6cecb4d8a51..2976f150d6f9 100644 --- a/test/Table_Tests/src/Util.enso +++ b/test/Table_Tests/src/Util.enso @@ -1,4 +1,5 @@ from Standard.Base import all +import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Database.DB_Table.DB_Table import Standard.Database.DB_Column.DB_Column @@ -27,14 +28,14 @@ DB_Table.should_equal self expected frames_to_skip=0 = rhs_error_check expected t0 = self.read t1 = expected.read - t0 . should_equal t1 frames_to_skip+2 + t0 . should_equal t1 frames_to_skip+1 DB_Column.should_equal : DB_Column -> Integer -> Any DB_Column.should_equal self expected frames_to_skip=0 = rhs_error_check expected t0 = self.read t1 = expected.read - t0 . should_equal t1 frames_to_skip+2 + t0 . should_equal t1 frames_to_skip+1 type Test_Failure_Error ## PRIVATE diff --git a/test/Table_Tests/src/Util_Spec.enso b/test/Table_Tests/src/Util_Spec.enso index f8f02507d2a0..d51f3a6c8d9c 100644 --- a/test/Table_Tests/src/Util_Spec.enso +++ b/test/Table_Tests/src/Util_Spec.enso @@ -1,10 +1,59 @@ from Standard.Base import all + from Standard.Table import Column, Table -from project.Util import all + +from Standard.Database import all + from Standard.Test import all +from enso_dev.Test_Tests.Helpers import expect_test_failure + +from project.Util import all + add_specs suite_builder = - suite_builder.group "Column should_equal" group_builder-> + suite_builder.group "Table/Column.should_equal helpers" group_builder-> + group_builder.specify "should report correct location for Table" <| + r1 = expect_test_failure <| + (Table.new [["X", [1]]]) . should_equal (Table.new [["X", [2]]]) + r1.message.should_contain "Util_Spec.enso:17" + + r2 = expect_test_failure <| + (Table.new [["X", [1]]]) . should_equal (Table.new [["A", [1]]]) + r2.message.should_contain "Util_Spec.enso:21" + + group_builder.specify "should report correct location for Column" <| + r1 = expect_test_failure <| + Column.from_vector "X" [1] . should_equal (Column.from_vector "X" [2]) + r1.message.should_contain "Util_Spec.enso:26" + + r2 = expect_test_failure <| + Column.from_vector "X" [1] . should_equal (Column.from_vector "A" [1]) + r2.message.should_contain "Util_Spec.enso:30" + + group_builder.specify "should report correct location for DB_Table" <| + tables = DB_Tables.make + r1 = expect_test_failure <| + tables.t1 . should_equal tables.t2 + r1.message.should_contain "Util_Spec.enso:36" + + r2 = expect_test_failure <| + tables.t1 . should_equal tables.tA + r2.message.should_contain "Util_Spec.enso:40" + + group_builder.specify "should report correct location for DB_Column" <| + tables = DB_Tables.make + c1 = tables.t1.at "X" + c2 = tables.t2.at "X" + cA = tables.tA.at "A" + + r1 = expect_test_failure <| + c1 . should_equal c2 + r1.message.should_contain "Util_Spec.enso:50" + + r2 = expect_test_failure <| + c1 . should_equal cA + r2.message.should_contain "Util_Spec.enso:54" + group_builder.specify "Two Columns Are Equal" <| expected_column = Column.from_vector "Col" ["Quis", "custodiet", "ipsos", "custodes?"] actual_column = Column.from_vector "Col" ["Quis", "custodiet", "ipsos", "custodes?"] @@ -44,7 +93,7 @@ add_specs suite_builder = expected_column = Column.from_vector "Col" [1.0, 2.0, Number.nan] actual_column = Column.from_vector "Col" [1.0, 2.0, Number.nan] actual_column.should_equal expected_column - suite_builder.group "Table should_equal" group_builder-> + group_builder.specify "Two Tables Are Equal" <| expected_table = Table.new [Column.from_vector "Col1" ["Quis", "custodiet", "ipsos", "custodes?"], Column.from_vector "Col2" ["Who", "guards", "the", "guards?"]] actual_table = Table.new [Column.from_vector "Col1" ["Quis", "custodiet", "ipsos", "custodes?"], Column.from_vector "Col2" ["Who", "guards", "the", "guards?"]] @@ -75,6 +124,17 @@ add_specs suite_builder = res = Panic.recover Test_Failure_Error (table_should_equal_impl actual_table expected_table "LOCATION_PATH") res.catch.message.should_equal "Got a Table, but expected a 42 (at LOCATION_PATH)." +type DB_Tables + Value t1 t2 tA + + make = + connection = Database.connect ..In_Memory + t1 = (Table.new [["X", [1]]]).select_into_database_table connection "t1" + t2 = (Table.new [["X", [2]]]).select_into_database_table connection "t2" + tA = (Table.new [["A", [1]]]).select_into_database_table connection "tA" + DB_Tables.Value t1 t2 tA + + main filter=Nothing = suite = Test.build suite_builder-> add_specs suite_builder diff --git a/test/Test_Tests/package.yaml b/test/Test_Tests/package.yaml new file mode 100644 index 000000000000..2d35ea3d05a9 --- /dev/null +++ b/test/Test_Tests/package.yaml @@ -0,0 +1,7 @@ +name: Test_Tests +namespace: enso_dev +version: 0.0.1 +license: MIT +author: enso-dev@enso.org +maintainer: enso-dev@enso.org +prefer-local-libraries: true diff --git a/test/Test_Tests/src/Extensions_Spec.enso b/test/Test_Tests/src/Extensions_Spec.enso new file mode 100644 index 000000000000..afee91d94597 --- /dev/null +++ b/test/Test_Tests/src/Extensions_Spec.enso @@ -0,0 +1,86 @@ +from Standard.Base import all +import Standard.Base.Errors.Illegal_Argument.Illegal_Argument + +from Standard.Test import all + +from project.Helpers import expect_test_failure + +main filter=Nothing = + suite = Test.build suite_builder-> + add_specs suite_builder + suite.run_with_filter filter + +add_specs suite_builder = + suite_builder.group "should_equal extension method" group_builder-> + group_builder.specify "should report correct location for Text" <| + r1 = expect_test_failure <| + "a".should_equal "b" + r1.message.should_contain "Extensions_Spec.enso:17" + + group_builder.specify "should report correct location for numbers" <| + r1 = expect_test_failure <| + 1.should_equal 2 + r1.message.should_contain "Extensions_Spec.enso:22" + + r2 = expect_test_failure <| + 1.0 . should_equal 2 + r2.message.should_contain "Extensions_Spec.enso:26" + + r3 = expect_test_failure <| + 1.to_decimal . should_equal 2 + r3.message.should_contain "Extensions_Spec.enso:30" + + r4 = expect_test_failure <| + Number.nan.should_equal 2 + r4.message.should_contain "Extensions_Spec.enso:34" + + group_builder.specify "should report correct location for errors" <| + error = Error.throw (Illegal_Argument.Error "foo") + r1 = expect_test_failure <| + error.should_equal 10 + r1.message.should_contain "Extensions_Spec.enso:40" + + group_builder.specify "should panic if error is expected" <| + error = Error.throw (Illegal_Argument.Error "foo") + Test.expect_panic Illegal_Argument <| + 10.should_equal error + + suite_builder.group "should_not_equal extension method" group_builder-> + group_builder.specify "should report correct location" <| + r1 = expect_test_failure <| + 1.should_not_equal 1 + r1.message.should_contain "Extensions_Spec.enso:51" + + group_builder.specify "should report correct location for errors" <| + error = Error.throw (Illegal_Argument.Error "foo") + r1 = expect_test_failure <| + error.should_not_equal 1 + r1.message.should_contain "Extensions_Spec.enso:57" + + suite_builder.group "should_contain extension method" group_builder-> + group_builder.specify "should report correct location" <| + r1 = expect_test_failure <| + [1, 2].should_contain 3 + r1.message.should_contain "Extensions_Spec.enso:63" + + r2 = expect_test_failure <| + "abc".should_contain "d" + r2.message.should_contain "Extensions_Spec.enso:67" + + suite_builder.group "should_not_contain extension method" group_builder-> + group_builder.specify "should report correct location" <| + r1 = expect_test_failure <| + [1, 2].should_not_contain 2 + r1.message.should_contain "Extensions_Spec.enso:73" + + suite_builder.group "should_start_with extension method" group_builder-> + group_builder.specify "should report correct location" <| + r1 = expect_test_failure <| + "abc".should_start_with "d" + r1.message.should_contain "Extensions_Spec.enso:79" + + suite_builder.group "should_end_with extension method" group_builder-> + group_builder.specify "should report correct location" <| + r1 = expect_test_failure <| + "abc".should_end_with "d" + r1.message.should_contain "Extensions_Spec.enso:85" diff --git a/test/Test_Tests/src/Helpers.enso b/test/Test_Tests/src/Helpers.enso new file mode 100644 index 000000000000..beaa75f34fcb --- /dev/null +++ b/test/Test_Tests/src/Helpers.enso @@ -0,0 +1,17 @@ +from Standard.Base import all + +import Standard.Test.Spec_Result.Spec_Result +from Standard.Test import Test + +## Expects the inner action to report a test failure exception and returns its payload. +expect_test_failure ~action -> Spec_Result = + loc = Meta.get_source_location 1 + handle_panic caught_panic = + result = caught_panic.payload + case result of + Spec_Result.Failure _ _ -> result + _ -> Test.fail "Expected test failure, but "+result.to_text+" was raised as error." + + Panic.catch Spec_Result handler=handle_panic <| + action + Test.fail "Expected the inner action to fail, but there was no failure (at "+loc+")." diff --git a/test/Test_Tests/src/Main.enso b/test/Test_Tests/src/Main.enso new file mode 100644 index 000000000000..a4542f7d522b --- /dev/null +++ b/test/Test_Tests/src/Main.enso @@ -0,0 +1,13 @@ +from Standard.Base import all + +from Standard.Test import Test + +import project.Extensions_Spec + +add_specs suite_builder = + Extensions_Spec.add_specs suite_builder + +main filter=Nothing = + suite = Test.build suite_builder-> + add_specs suite_builder + suite.run_with_filter filter From afedc71733f881bbd4a29d50ea6f480eadf0be8b Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 19 Dec 2024 07:40:42 +0100 Subject: [PATCH 23/25] Documenting propagation of _broken values_ --- docs/types/errors.md | 118 ++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/docs/types/errors.md b/docs/types/errors.md index 653645734301..52047ef1772e 100644 --- a/docs/types/errors.md +++ b/docs/types/errors.md @@ -9,7 +9,7 @@ order: 12 # Errors Enso supports two notions of errors. One is the standard exceptions model, while -the other is a theory of 'broken values' that propagate through computations. +the other is a theory of _broken values_ that propagate through computations. > [!WARNING] The actionables for this section are: > @@ -34,50 +34,96 @@ the other is a theory of 'broken values' that propagate through computations. ## Broken Values -In Enso we have the notion of a 'broken' value: one which is in an invalid state -but not an error. While these may initially seem a touch useless, they are -actually key for the display of errors in the GUI. +In Enso we have the notion of a _broken value_: one which is in an invalid +state. Such values are very useful for displaying errors in the GUI. -Broken values can be thought of like checked monadic exceptions in Haskell, but -with an automatic propagation mechanism: +Broken values are fast to allocate and pass around the program. They record line +of their own creation - e.g. where `Error.throw` has happened. Shall that not be +enough, one can run with `-ea` flag, like: -- Broken values that aren't handled explicitly are automatically promoted - through the parent scope. This is trivial inference as no evidence discharge - will have occurred on the value. +```bash +enso$ JAVA_OPTS=-ea ./built-distribution/enso-engine-*/enso-*/bin/enso --run x.enso +``` - ```ruby - open : String -> String in IO ! IO.Exception - open = ... +to get full stack where the _broken value_ has been created. Collecting such +full stack trace however prevents the execution to run at _full speed_. - test = - print 'Opening the gates!' - txt = open 'gates.txt' - print 'Gates were opened!' - 7 - ``` +### Promotion of Broken Values - In the above example, the type of test is inferred to - `test : Int in IO ! IO.Exception`, because no evidence discharge has taken - place as the potential broken value hasn't been handled. +Broken values that aren't handled explicitly are automatically promoted through +the parent scope. Let's assume an `open` function that can yield a `Text` or +_broken value_ representing a `File_Error`: -- This allows for very natural error handling in the GUI. +```ruby +open file_name:Text -> Text ! File_Error = ... +``` -> [!WARNING] The actionables for this section are: -> -> - Determine what kinds of APIs we want to use async exceptions for, and which -> broken values are more suited for. -> - Ensure that we are okay with initially designing everything around async -> exceptions as broken values are very hard to support without a type checker. -> - Initially not supported for APIs. +Then imagine following `test` function trying to open a non-existing file +`gates.txt` + +```ruby +test = + IO.println 'Opening the gates!' + open 'gates.txt' + IO.println 'Gates were opened!' +``` + +Execution of such function will: + +- print `Opening the gates!` text +- finish with `File_Error` _broken value_ +- **not print** `Gates were opened!` + +E.g. the execution of a function body ends after first _uhandled broken value_. + +### Propagation of Broken Values -Broken values (implemented as `DataflowError` class in the interpreter) are fast -to allocate and pass around the program. They record line of their own -creation - e.g. where `Error.throw` has happened. Shall that not be enough, one -can run with `-ea` flag, like: +Let's modify the previous example a bit. Let's assign the read text (or _broken +value_) to a variable and return it from the `test` function: + +```ruby +test = + IO.println 'Opening the gates!' + content = open 'gates.txt' + IO.println 'Gates were opened!' + content +``` + +If the `gates.txt` file exists, its content is returned from the `test` +function. If a `File_Error` _broken value_ is returned from the `open` function, +then the variable `content` will contain such a _broken value_ and as `content` +is the return value from the `test` function, the `File_Error` will be returned +from the `test` function and propagated further as a _broken value_. + +In both situations (if the file exists or not) both `IO.println` statements are +executed and the execution of `test` function thus prints both +`Opening the gates!` as well as `Gates were opened!`. + +### Unused Broken Values + +Should the last statement (e.g. `content`) of the `test` function defined in +previous section be missing, then the _broken value_ assigned to `content` +variable might _"disappear"_ unnoticed. However in such a situation the Enso +compiler emits a _compile time warning_: ```bash -enso$ JAVA_OPTS=-ea ./built-distribution/enso-engine-*/enso-*/bin/enso --run x.enso +test.enso:3:3: warning: Unused variable content. + 3 | content = open 'gates.txt' + | ^~~~~~~ ``` -to get full stack where the _broken value_ has been created. Collecting such -full stack trace however prevents the execution to run at _full speed_. +The combination of _compiler warning_, _propagation_ and _promotion_ of _broken +values_ ensures `File_Error` and other _broken values_ are **never lost** +(unintentionally). Should _loosing a broken value_ be a goal, one can change the +line in question to: + +```ruby + _ = open 'gates.txt' +``` + +e.g. assign it to anonymous variable. That signals to the system one doesn't +care about the result of the `open` function call. No _compiler warning_ is thus +reported and the _broken value_ gets lost during execution. + +To handle _broken values_ properly and recover from such an errorneous state, +use methods offered by `Standard.Base.Error` type like `catch`. From bda76cf97feb5fa6e22884b8a29b4263811218ce Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 19 Dec 2024 07:46:25 +0100 Subject: [PATCH 24/25] Renaming to Errors & Panics --- docs/types/errors.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/types/errors.md b/docs/types/errors.md index 52047ef1772e..f48e2f2eab97 100644 --- a/docs/types/errors.md +++ b/docs/types/errors.md @@ -1,15 +1,17 @@ --- layout: developer-doc -title: Errors +title: Errors & Panics category: types tags: [types, errors] order: 12 --- -# Errors +# Errors & Panics -Enso supports two notions of errors. One is the standard exceptions model, while -the other is a theory of _broken values_ that propagate through computations. +Enso supports two notions of errors. One is the standard exceptions model (built +around `Panic.throw` and related methods), while the other is a theory of +_broken values_ that propagate through computations (represented by `Error` and +created by `Error.throw` method). > [!WARNING] The actionables for this section are: > @@ -19,18 +21,16 @@ the other is a theory of _broken values_ that propagate through computations. -- [Async Exceptions](#async-exceptions) +- [Exceptions/Panics](#errors--panics) - [Broken Values](#broken-values) -## Async Exceptions +## Exceptions/Panics > [!WARNING] The actionables for this section are: > -> - why is this called _"asynchronous"_ when the `Panic` is raised -> synchronously? -> - Formalise the model of async exceptions as implemented. +> - Formalise the model of `Panic.throw` as implemented. ## Broken Values From 591620498f78a7c86c22c455685138665a3cb0bd Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 19 Dec 2024 07:52:56 +0100 Subject: [PATCH 25/25] Reorganizing changelog --- CHANGELOG.md | 6 ++---- docs/types/errors.md | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1255f4141a11..1d4db090dd76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,15 +12,13 @@ #### Enso Language & Runtime -- [Propagate Error ASAP instead of ignoring it][11777]. - -[11777]: https://github.com/enso-org/enso/pull/11777 - +- [Promote broken values instead of ignoring them][11777]. - [Intersection types & type checks][11600] - A constructor or type definition with a single inline argument definition was previously allowed to use spaces in the argument definition without parentheses. [This is now a syntax error.][11856] +[11777]: https://github.com/enso-org/enso/pull/11777 [11600]: https://github.com/enso-org/enso/pull/11600 [11856]: https://github.com/enso-org/enso/pull/11856 diff --git a/docs/types/errors.md b/docs/types/errors.md index f48e2f2eab97..8d0aeb4f3187 100644 --- a/docs/types/errors.md +++ b/docs/types/errors.md @@ -99,7 +99,7 @@ In both situations (if the file exists or not) both `IO.println` statements are executed and the execution of `test` function thus prints both `Opening the gates!` as well as `Gates were opened!`. -### Unused Broken Values +### Detection of Unused Broken Values Should the last statement (e.g. `content`) of the `test` function defined in previous section be missing, then the _broken value_ assigned to `content` @@ -112,8 +112,8 @@ test.enso:3:3: warning: Unused variable content. | ^~~~~~~ ``` -The combination of _compiler warning_, _propagation_ and _promotion_ of _broken -values_ ensures `File_Error` and other _broken values_ are **never lost** +The combination of _detection_, _propagation_ and _promotion_ of _broken values_ +ensures `File_Error` and other _broken values_ are **never lost** (unintentionally). Should _loosing a broken value_ be a goal, one can change the line in question to: