Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: remove broken marker, better define notimpl and notyet #9688

Merged
merged 1 commit into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/contribute/02_workflow.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,12 @@ all ordinary and edge cases.
Pytest markers can be used to assert that a test should fail or raise a specific error.
We use a number of pytest markers in ibis:

- `pytest.mark.notimpl`: the backend can do a thing, we haven't mapped the op
- `pytest.mark.notyet`: the backend cannot do a thing, but might in the future
- `pytest.mark.never`: the backend will never support this / pass this test (common example
here is a test running on sqlite that relies on strong typing)
- `pytest.mark.broken`: this test broke and it's demonstrably unrelated to the PR I'm working
on and fixing it shouldn't block this PR from going in (but we should fix it up pronto)
- `pytest.mark.notimpl`: We can implement/fix/workaround this on the ibis side, but haven't yet.
- `pytest.mark.notyet`: This requires the backend to implement/fix something.
We can't/won't do it on the ibis side.
- `pytest.mark.never`: The backend will never support this / pass this test.
We shouldn't have any hope of trying to fix this.
A common example here is a test running on sqlite that relies on strong typing.

Refrain from using a generic marker like `pytest.mark.xfail`.

Expand Down
16 changes: 0 additions & 16 deletions ibis/backends/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,22 +370,6 @@ def _filter_none_from_raises(kwargs):
kwargs = _filter_none_from_raises(kwargs)
item.add_marker(pytest.mark.xfail(**kwargs))

# Something has been exposed as broken by a new test and it shouldn't be
# imperative for a contributor to fix it just because they happened to
# bring it to attention -- USE SPARINGLY
for marker in item.iter_markers(name="broken"):
if backend in marker.args[0]:
if (
item.location[0] in FIlES_WITH_STRICT_EXCEPTION_CHECK
and "raises" not in marker.kwargs.keys()
):
raise ValueError("broken requires a raises")

kwargs = marker.kwargs.copy()
kwargs.setdefault("reason", f"Feature is failing on {backend}")
kwargs = _filter_none_from_raises(kwargs)
item.add_marker(pytest.mark.xfail(**kwargs))

for marker in item.iter_markers(name="xfail_version"):
kwargs = marker.kwargs.copy()
kwargs = _filter_none_from_raises(kwargs)
Expand Down
26 changes: 13 additions & 13 deletions ibis/backends/tests/test_aggregation.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def mean_and_std(v):
lambda t, where: t.bool_col[where].any(),
id="any",
marks=[
pytest.mark.broken(
pytest.mark.notimpl(
["druid"],
raises=AttributeError,
reason="'IntegerColumn' object has no attribute 'any'",
Expand All @@ -292,7 +292,7 @@ def mean_and_std(v):
lambda t, where: ~t.bool_col[where].any(),
id="notany",
marks=[
pytest.mark.broken(
pytest.mark.notimpl(
["druid"],
raises=AttributeError,
reason="'IntegerColumn' object has no attribute 'notany'",
Expand All @@ -305,7 +305,7 @@ def mean_and_std(v):
lambda t, where: ~t.bool_col[where].any(),
id="any_negate",
marks=[
pytest.mark.broken(
pytest.mark.notimpl(
["druid"],
raises=AttributeError,
reason="'IntegerColumn' object has no attribute 'any'",
Expand All @@ -318,7 +318,7 @@ def mean_and_std(v):
lambda t, where: t.bool_col[where].all(),
id="all",
marks=[
pytest.mark.broken(
pytest.mark.notimpl(
["druid"],
raises=AttributeError,
reason="'IntegerColumn' object has no attribute 'all'",
Expand All @@ -330,7 +330,7 @@ def mean_and_std(v):
lambda t, where: ~t.bool_col[where].all(),
id="notall",
marks=[
pytest.mark.broken(
pytest.mark.notimpl(
["druid"],
raises=AttributeError,
reason="'IntegerColumn' object has no attribute 'notall'",
Expand All @@ -343,7 +343,7 @@ def mean_and_std(v):
lambda t, where: ~t.bool_col[where].all(),
id="all_negate",
marks=[
pytest.mark.broken(
pytest.mark.notimpl(
["druid"],
raises=AttributeError,
reason="'IntegerColumn' object has no attribute 'all'",
Expand All @@ -361,7 +361,7 @@ def mean_and_std(v):
lambda t, where: (t.int_col > 0)[where].sum(),
id="bool_sum",
marks=[
pytest.mark.broken(
pytest.mark.notimpl(
["oracle"],
raises=OracleDatabaseError,
reason="ORA-02000: missing AS keyword",
Expand Down Expand Up @@ -538,7 +538,7 @@ def mean_and_std(v):
["impala", "mysql", "sqlite", "mssql", "druid", "oracle", "exasol"],
raises=com.OperationNotDefinedError,
),
pytest.mark.broken(
pytest.mark.notimpl(
["dask"],
raises=(AttributeError, TypeError),
reason=(
Expand Down Expand Up @@ -809,12 +809,12 @@ def test_count_distinct_star(alltypes, df, ibis_cond, pandas_cond):
reason="backend implements approximate quantiles",
raises=com.OperationNotDefinedError,
),
pytest.mark.broken(
pytest.mark.never(
["pyspark"],
reason="backend implements approximate quantiles",
raises=AssertionError,
),
pytest.mark.broken(
pytest.mark.never(
["dask"],
reason="backend implements approximate quantiles",
raises=AssertionError,
Expand All @@ -824,7 +824,7 @@ def test_count_distinct_star(alltypes, df, ibis_cond, pandas_cond):
reason="backend doesn't implement approximate quantiles yet",
raises=com.OperationNotDefinedError,
),
pytest.mark.broken(
pytest.mark.notimpl(
["risingwave"],
reason="Invalid input syntax: direct arg in `percentile_cont` must be castable to float64",
raises=PsycoPg2InternalError,
Expand Down Expand Up @@ -1132,7 +1132,7 @@ def test_median(alltypes, df):
raises=ClickHouseDatabaseError,
reason="doesn't support median of strings",
)
@pytest.mark.broken(
@pytest.mark.notyet(
["pyspark"], raises=AssertionError, reason="pyspark returns null for string median"
)
@pytest.mark.notimpl(["dask"], raises=(AssertionError, NotImplementedError, TypeError))
Expand Down Expand Up @@ -1301,7 +1301,7 @@ def test_topk_op(alltypes, df):
)
],
)
@pytest.mark.broken(
@pytest.mark.notyet(
["druid"], raises=PyDruidProgrammingError, reason="Java NullPointerException"
)
@pytest.mark.notimpl(
Expand Down
38 changes: 21 additions & 17 deletions ibis/backends/tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ def test_array_slice(backend, start, stop):
@pytest.mark.notimpl(
["datafusion", "flink", "polars", "sqlite"], raises=com.OperationNotDefinedError
)
@pytest.mark.broken(
@pytest.mark.notimpl(
["risingwave"],
raises=PsycoPg2InternalError,
reason="TODO(Kexiang): seems a bug",
Expand Down Expand Up @@ -460,7 +460,7 @@ def test_array_slice(backend, start, stop):
],
ids=["lambda", "partial", "deferred"],
)
@pytest.mark.broken(
@pytest.mark.notimpl(
["risingwave"],
raises=PsycoPg2InternalError,
reason="TODO(Kexiang): seems a bug",
Expand Down Expand Up @@ -542,7 +542,7 @@ def test_array_filter(con, input, output, predicate):
"x",
1,
marks=[
pytest.mark.broken(
pytest.mark.notimpl(
["flink"],
raises=Py4JJavaError,
reason="unknown; NPE during execution",
Expand Down Expand Up @@ -574,7 +574,7 @@ def test_array_contains(backend, con, col, value):
raises=Py4JJavaError,
reason="SQL validation failed; Flink does not support ARRAY[]", # https://issues.apache.org/jira/browse/FLINK-20578
),
pytest.mark.broken(
pytest.mark.notyet(
["datafusion"],
raises=Exception,
reason="Internal error: start_from index out of bounds",
Expand Down Expand Up @@ -647,7 +647,7 @@ def test_array_remove(con, a):
raises=(AssertionError, GoogleBadRequest),
reason="bigquery doesn't support null elements in arrays",
)
@pytest.mark.broken(
@pytest.mark.notimpl(
["risingwave"], raises=AssertionError, reason="TODO(Kexiang): seems a bug"
)
@pytest.mark.notyet(
Expand Down Expand Up @@ -681,7 +681,7 @@ def test_array_unique(con, input, expected):
["flink", "polars"],
raises=com.OperationNotDefinedError,
)
@pytest.mark.broken(
@pytest.mark.notyet(
["risingwave"],
raises=AssertionError,
reason="Refer to https://github.com/risingwavelabs/risingwave/issues/14735",
Expand Down Expand Up @@ -752,7 +752,7 @@ def test_array_union(con, a, b, expected_array):
@pytest.mark.notimpl(
["sqlite"], raises=com.UnsupportedBackendType, reason="Unsupported type: Array..."
)
@pytest.mark.broken(
@pytest.mark.notimpl(
["risingwave"],
raises=AssertionError,
reason="TODO(Kexiang): seems a bug",
Expand Down Expand Up @@ -793,7 +793,7 @@ def test_array_intersect(con, data):
@pytest.mark.notimpl(["postgres"], raises=PsycoPg2SyntaxError)
@pytest.mark.notimpl(["risingwave"], raises=PsycoPg2InternalError)
@pytest.mark.notimpl(["datafusion"], raises=com.OperationNotDefinedError)
@pytest.mark.broken(
@pytest.mark.notimpl(
["trino"], reason="inserting maps into structs doesn't work", raises=TrinoUserError
)
def test_unnest_struct(con):
Expand All @@ -813,10 +813,10 @@ def test_unnest_struct(con):
@pytest.mark.notimpl(["postgres"], raises=PsycoPg2SyntaxError)
@pytest.mark.notimpl(["risingwave"], raises=PsycoPg2InternalError)
@pytest.mark.notimpl(["datafusion"], raises=com.OperationNotDefinedError)
@pytest.mark.broken(
@pytest.mark.notimpl(
["trino"], reason="inserting maps into structs doesn't work", raises=TrinoUserError
)
@pytest.mark.broken(
@pytest.mark.notimpl(
["flink"], reason="flink unnests a and b as separate columns", raises=Py4JJavaError
)
def test_unnest_struct_with_multiple_fields(con):
Expand Down Expand Up @@ -914,7 +914,7 @@ def test_zip_null(con, fn):
reason="pyspark doesn't seem to support field selection on explode",
raises=PySparkAnalysisException,
)
@pytest.mark.broken(
@pytest.mark.notimpl(
["trino"], reason="inserting maps into structs doesn't work", raises=TrinoUserError
)
@pytest.mark.notyet(
Expand Down Expand Up @@ -1002,7 +1002,7 @@ def flatten_data():
reason="Arrays are never nullable",
raises=AssertionError,
),
pytest.mark.broken(
pytest.mark.notimpl(
["polars"],
raises=TypeError,
reason="comparison of nested arrays doesn't work in pandas testing module",
Expand Down Expand Up @@ -1220,8 +1220,10 @@ def swap(token):
"-1h",
id="neg_inner",
marks=[
pytest.mark.broken(
["polars"], raises=AssertionError, reason="returns an empty array"
pytest.mark.notimpl(
["polars"],
raises=(AssertionError, TypeError),
reason="returns an empty array",
),
pytest.mark.notimpl(
["risingwave"],
Expand Down Expand Up @@ -1329,8 +1331,10 @@ def test_repr_timestamp_array(con, monkeypatch):
["datafusion", "flink", "polars"],
raises=com.OperationNotDefinedError,
)
@pytest.mark.broken(["pandas"], raises=ValueError, reason="reindex on duplicate values")
@pytest.mark.broken(
@pytest.mark.notimpl(
["pandas"], raises=ValueError, reason="reindex on duplicate values"
)
@pytest.mark.notimpl(
["dask"], raises=AssertionError, reason="DataFrame.index are different"
)
def test_unnest_range(con):
Expand Down Expand Up @@ -1367,7 +1371,7 @@ def test_array_literal_with_exprs(con, input, expected):
["datafusion", "postgres", "pandas", "polars", "risingwave", "dask", "flink"],
raises=com.OperationNotDefinedError,
)
@pytest.mark.broken(
@pytest.mark.notimpl(
["trino"],
raises=TrinoUserError,
reason="sqlglot generates code that assumes there's only at most two fields to unpack from a struct",
Expand Down
2 changes: 1 addition & 1 deletion ibis/backends/tests/test_asof_join.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def test_asof_join(con, time_left, time_right, time_df1, time_df2, direction, op
@pytest.mark.parametrize(
("direction", "op"), [("backward", operator.ge), ("forward", operator.le)]
)
@pytest.mark.broken(
@pytest.mark.notimpl(
["clickhouse"], raises=AssertionError, reason="`time` is truncated to seconds"
)
@pytest.mark.notyet(
Expand Down
14 changes: 7 additions & 7 deletions ibis/backends/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def test_create_table(backend, con, temp_table, func, sch):
reason="Can't rename temp tables",
raises=ValueError,
),
pytest.mark.broken(
pytest.mark.notimpl(
["bigquery"],
reason="tables created with temp=True cause a 404 on retrieval",
),
Expand All @@ -155,8 +155,8 @@ def test_create_table(backend, con, temp_table, func, sch):
["pyspark", "trino", "exasol", "risingwave"],
reason="No support for temp tables",
),
pytest.mark.broken(["mssql"], reason="Incorrect temp table syntax"),
pytest.mark.broken(
pytest.mark.notimpl(["mssql"], reason="Incorrect temp table syntax"),
pytest.mark.notimpl(
["bigquery"],
reason="tables created with temp=True cause a 404 on retrieval",
),
Expand Down Expand Up @@ -293,7 +293,7 @@ def test_create_table_from_schema(con, new_schema, temp_table):
assert result == new_table.schema()


@mark.broken(
@mark.notimpl(
["oracle"],
reason="oracle temp tables aren't cleaned up on reconnect -- they need to "
"be switched from using atexit to weakref.finalize",
Expand Down Expand Up @@ -1315,10 +1315,10 @@ def test_set_backend_url(url, monkeypatch):
@pytest.mark.notimpl(
["snowflake"], reason="scale not implemented in ibis's snowflake backend"
)
@pytest.mark.broken(
@pytest.mark.never(
["oracle"], reason="oracle doesn't allow DESCRIBE outside of its CLI"
)
@pytest.mark.broken(["druid"], reason="dialect is broken")
@pytest.mark.notimpl(["druid"], reason="dialect is broken")
@pytest.mark.notimpl(
["flink"],
raises=com.IbisError,
Expand All @@ -1345,7 +1345,7 @@ def gen_test_name(con: BaseBackend):
con.drop_table(name, force=True)


@mark.broken(
@mark.notimpl(
["druid"], raises=NotImplementedError, reason="generated SQL fails to parse"
)
@mark.notimpl(["impala"], reason="impala doesn't support memtable")
Expand Down
6 changes: 3 additions & 3 deletions ibis/backends/tests/test_conditionals.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def test_value_cases_scalar(con, inp, exp):
assert result == exp


@pytest.mark.broken(
@pytest.mark.notimpl(
"exasol",
reason="the int64 RBI column is .to_pandas()ed to an object column, which is incomparable to ints",
raises=AssertionError,
Expand Down Expand Up @@ -117,7 +117,7 @@ def test_ibis_cases_scalar():
assert result == "five"


@pytest.mark.broken(
@pytest.mark.notimpl(
["sqlite", "exasol"],
reason="the int64 RBI column is .to_pandas()ed to an object column, which is incomparable to 5",
raises=TypeError,
Expand All @@ -142,7 +142,7 @@ def test_ibis_cases_column(batting):
assert Counter(result) == Counter(expected)


@pytest.mark.broken("clickhouse", reason="special case this and returns 'oops'")
@pytest.mark.notimpl("clickhouse", reason="special case this and returns 'oops'")
def test_value_cases_null(con):
"""CASE x WHEN NULL never gets hit"""
e = ibis.literal(5).nullif(5).case().when(None, "oops").else_("expected").end()
Expand Down
2 changes: 1 addition & 1 deletion ibis/backends/tests/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def test_column_pyarrow_batch_chunk_size(awards_players):


@pytest.mark.notimpl(["pandas", "dask"])
@pytest.mark.broken(
@pytest.mark.notimpl(
["sqlite"],
raises=pa.ArrowException,
reason="Test data has empty strings in columns typed as int64",
Expand Down
Loading