def test_const_scalar_cond(self, cond_val, is_cond_scalar, need_broadcast, is_block_output):
        """
        Input graph:

            const(cond) -|
                         |
            a -----------|-> select -> (add 1.0 if not is_block_output) -> output
                         |
            b -----------|

        If a and b need broadcast, then nothing is changed; else output graph becomes:

            if cond:
                if is_block_output:
                    a -> identity -> output
                else:
                    a -> add 1.0 -> output
            else:
                if is_block_output:
                    b -> identity -> output
                else:
                    b -> add 1.0 -> output
        """
        SHAPE = (5, 2, 3)

        if need_broadcast:
            a_shape = (5, 2, 1)
            b_shape = (5, 1, 3)
        else:
            a_shape = SHAPE
            b_shape = SHAPE

        if is_cond_scalar:
            cond = cond_val
        else:
            cond_shape = (5, 1, 1)
            cond = np.full(cond_shape, cond_val)

        @mb.program(
            input_specs=[
                mb.TensorSpec(shape=a_shape),
                mb.TensorSpec(shape=b_shape),
            ]
        )
        def prog(a, b):
            c = mb.select(cond=cond, a=a, b=b)
            if not is_block_output:
                c = mb.add(x=c, y=1.0)
            return c

        prev_prog, _, _ = apply_pass_and_basic_check(prog, "common::select_optimization")
        apply_pass_and_basic_check(prog, "common::noop_elimination")
        _, _, block = apply_pass_and_basic_check(prog, "common::dead_code_elimination")
        # check previous program
        if is_block_output:
            assert get_op_types_in_program(prev_prog) == ["select"]
        else:
            assert get_op_types_in_program(prev_prog) == ["select", "add"]
        # check passed program
        if is_block_output:
            if need_broadcast:
                assert get_op_types_in_program(prog) == ["select"]
            else:
                assert get_op_types_in_program(prog) == ["identity"]
        else:
            if need_broadcast:
                assert get_op_types_in_program(prog) == ["select", "add"]
            else:
                assert get_op_types_in_program(prog) == ["add"]

        output_name = block.outputs[0].name
        assert_model_is_valid(
            prog,
            {"a": a_shape, "b": b_shape},
            expected_output_shapes={output_name: SHAPE},
        )

        prev_model = ct.convert(
            prev_prog,
            pass_pipeline=ct.PassPipeline.EMPTY,
            convert_to="mlprogram",
            compute_units=ct.ComputeUnit.CPU_ONLY,
        )
        model = ct.convert(
            prog,
            pass_pipeline=ct.PassPipeline.EMPTY,
            convert_to="mlprogram",
            compute_units=ct.ComputeUnit.CPU_ONLY,
        )

        a = np.random.rand(*a_shape)
        b = np.random.rand(*b_shape)
        input_dict = {"a": a, "b": b}
        prev_output = prev_model.predict(input_dict)[output_name]
        output = model.predict(input_dict)[output_name]
        np.testing.assert_allclose(prev_output, output, rtol=0.0, atol=0.0)

        Input graph:

            const(cond) -|
                         |
            a -----------|-> select -> (add 1.0 if not is_block_output) -> output
                         |
            b -----------|

        If a and b need broadcast, then nothing is changed; else output graph becomes:

            if cond:
                if is_block_output:
                    a -> identity -> output
                else:
                    a -> add 1.0 -> output
            else:
                if is_block_output:
                    b -> identity -> output
                else:
                    b -> add 1.0 -> output
        
coremltools/converters/mil/mil/passes/tests/test_passes.py