def test_single_pass(self):
        """
        Input:

            x
            -> relu(torch_scope="module_1")
            -> transpose_1(torch_scope="module_1")
            -> transpose_2(torch_scope="module_2")
            -> output

        Output:

            x
            -> relu(torch_scope="module_1")
            -> transpose_3(
                    torch_scope="module_2",
                    pass_scope="merge_consecutive_transposes"
                )
            -> output

        In the above case, the relu op preserves its original scope information.
        Since transpose_3 is created by the "merge_consecutive_transposes" pass, the COREMLTOOLS_GRAPH_PASS scope
        information will be saved in the op.
        Also, the TORCHSCRIPT_MODULE_TYPE scope info of transpose_2 is back propagated to transpose_3,
        when the use of output of transpose_2 is replaced by the output of transpose_3.
        """

        @mb.program(input_specs=[mb.TensorSpec(shape=(1, 2, 3, 4))])
        def prog(x):
            with mb.scope(
                ScopeInfo(source=ScopeSource.TORCHSCRIPT_MODULE_TYPE, data="module_1"),
            ):
                x = mb.relu(x=x)
                x = mb.transpose(x=x, perm=[0, 2, 1, 3])

            with mb.scope(
                ScopeInfo(source=ScopeSource.TORCHSCRIPT_MODULE_TYPE, data="module_2"),
            ):
                return mb.transpose(x=x, perm=[3, 2, 0, 1])

        prog._add_essential_scope_source(ScopeSource.TORCHSCRIPT_MODULE_TYPE)

        apply_pass_and_basic_check(prog, "common::merge_consecutive_transposes")
        assert get_op_types_in_program(prog) == ["relu", "transpose"]

        # the scope info in the relu op is not affected
        relu_op = prog.find_ops(op_type="relu")[0]
        assert len(relu_op.scopes) == 1
        assert relu_op.scopes[ScopeSource.TORCHSCRIPT_MODULE_TYPE] == ["module_1"]

        # the new transpose op has the scope information from the graph pass
        transpose_op = prog.find_ops(op_type="transpose")[0]
        assert len(transpose_op.scopes) == 2
        assert transpose_op.scopes[ScopeSource.COREMLTOOLS_GRAPH_PASS] == [
            "merge_consecutive_transposes"
        ]
        assert transpose_op.scopes[ScopeSource.TORCHSCRIPT_MODULE_TYPE] == ["module_2"]

        Input:

            x
            -> relu(torch_scope="module_1")
            -> transpose_1(torch_scope="module_1")
            -> transpose_2(torch_scope="module_2")
            -> output

        Output:

            x
            -> relu(torch_scope="module_1")
            -> transpose_3(
                    torch_scope="module_2",
                    pass_scope="merge_consecutive_transposes"
                )
            -> output

        In the above case, the relu op preserves its original scope information.
        Since transpose_3 is created by the "merge_consecutive_transposes" pass, the COREMLTOOLS_GRAPH_PASS scope
        information will be saved in the op.
        Also, the TORCHSCRIPT_MODULE_TYPE scope info of transpose_2 is back propagated to transpose_3,
        when the use of output of transpose_2 is replaced by the output of transpose_3.
        
coremltools/converters/mil/mil/passes/tests/test_passes.py