code: def test_ufunc_series(request, ufunc, has_nulls, indexed):
      # Note: This test assumes that all ufuncs are unary or binary.
      fname = ufunc.__name__
      request.applymarker(
          pytest.mark.xfail(
              condition=(
                  indexed
                  and fname
                  in {
                      "greater",
                      "greater_equal",
                      "less",
                      "less_equal",
                      "not_equal",
                      "equal",
                  }
              ),
              reason="Comparison operators do not support misaligned indexes.",
          )
      )
      request.applymarker(
          pytest.mark.xfail(
              condition=ufunc == np.matmul and has_nulls,
              reason="Can't call cupy on column with nulls",
          )
      )
      # If we don't have explicit dispatch and cupy doesn't support the operator,
      # we expect a failure
      request.applymarker(
          pytest.mark.xfail(
              condition=not hasattr(cp, fname),
              reason=f"cupy has no support for '{fname}'",
          )
      )
  
      N = 100
      # Avoid zeros in either array to skip division by 0 errors. Also limit the
      # scale to avoid issues with overflow, etc. We use ints because some
      # operations (like bitwise ops) are not defined for floats.
      pandas_args = args = [
          cudf.Series(
              cp.random.randint(low=1, high=10, size=N),
              index=cp.random.choice(range(N), N, False)
              if indexed
              else None,
          )
          for _ in range(ufunc.nin)
      ]
  
      if has_nulls:
          # Converting nullable integer cudf.Series to pandas will produce a
          # float pd.Series, so instead we replace nulls with an arbitrary
          # integer value, precompute the mask, and then reapply it afterwards.
          for arg in args:
              set_random_null_mask_inplace(arg)
          pandas_args = [arg.fillna(0) for arg in args]
  
          # Note: Different indexes must be aligned before the mask is computed.
          # This requires using an internal function (_align_indices), and that
          # is unlikely to change for the foreseeable future.
          aligned = (
              cudf.core.series._align_indices(
                  args, allow_non_unique=True
              )
              if indexed and ufunc.nin == 2
              else args
          )
          mask = reduce(
              operator.or_, (a.isna() for a in aligned)
          ).to_pandas()
  
      got = ufunc(*args)
  
      with _hide_ufunc_warnings(ufunc):
          expect = ufunc(*(arg.to_pandas() for arg in pandas_args))
  
      if ufunc.nout > 1:
          for g, e in zip(got, expect):
              if has_nulls:
                  e[mask] = np.nan
              assert_eq(g, e, check_exact=False)
      else:
          if has_nulls:
              expect[mask] = np.nan
              assert_eq(got, expect, check_exact=False)
  
file_path: cudf/python/cudf/cudf/tests/test_array_ufunc.py
imports: import operator
  import warnings
  from contextlib import contextmanager
  from functools import reduce
  import cupy
  import numpy
  import pytest
  import cudf
  from cudf.core._compat import PANDAS_GE_150
  from cudf.testing._utils import assert_eq, set_random_null_mask_inplace
label_desc: Use image node_id 4 for calling a global function with example usage: test_ufunc_series(request, ufunc, has_nulls, indexed) without return types