code: def test_unary_copy_e(idtype):
      def _test(mfunc, rfunc):
          g = create_test_heterograph_large(idtype)
          g0 = create_test_heterograph_2(idtype)
          g1 = create_test_heterograph(idtype)
          cross_reducer = rfunc.__name__
          x1 = F.randn((g.num_edges("plays"), feat_size))
          x2 = F.randn((g.num_edges("follows"), feat_size))
          x3 = F.randn((g.num_edges("develops"), feat_size))
          x4 = F.randn((g.num_edges("wishes"), feat_size))
          F.attach_grad(x1)
          F.attach_grad(x2)
          F.attach_grad(x3)
          F.attach_grad(x4)
          g["plays"].edata["eid"] = x1
          g["follows"].edata["eid"] = x2
          g["develops"].edata["eid"] = x3
          g["wishes"].edata["eid"] = x4
  
          #################################################################
          #  multi_update_all(): call msg_passing separately for each etype
          #################################################################
  
          with F.record_grad():
              g.multi_update_all(
                  {
                      "plays": (mfunc("eid", "m"), rfunc("m", "y")),
                      "follows": (mfunc("eid", "m"), rfunc("m", "y")),
                      "develops": (mfunc("eid", "m"), rfunc("m", "y")),
                      "wishes": (mfunc("eid", "m"), rfunc("m", "y")),
                  },
                  cross_reducer,
              )
              r1 = g.nodes["game"].data["y"].clone()
              r2 = g.nodes["user"].data["y"].clone()
              loss = r1.sum() + r2.sum()
              F.backward(loss)
              e_grad1 = F.grad(g["develops"].edata["eid"]).clone()
              e_grad2 = F.grad(g["plays"].edata["eid"]).clone()
              e_grad3 = F.grad(g["wishes"].edata["eid"]).clone()
              e_grad4 = F.grad(g["follows"].edata["eid"]).clone()
          {
              etype: (g[etype].edata.clear())
              for _, etype, _ in g.canonical_etypes
          },
  
          #################################################################
          #  update_all(): call msg_passing for all etypes
          #################################################################
  
          # TODO(Israt): output type can be None in multi_update and empty
          F.attach_grad(x1)
          F.attach_grad(x2)
          F.attach_grad(x3)
          F.attach_grad(x4)
  
          g["plays"].edata["eid"] = x1
          g["follows"].edata["eid"] = x2
          g["develops"].edata["eid"] = x3
          g["wishes"].edata["eid"] = x4
  
          with F.record_grad():
              g.update_all(mfunc("eid", "m"), rfunc("m", "y"))
              r3 = g.nodes["game"].data["y"]
              r4 = g.nodes["user"].data["y"]
              loss = r3.sum() + r4.sum()
              F.backward(loss)
              e_grad5 = F.grad(g["develops"].edata["eid"])
              e_grad6 = F.grad(g["plays"].edata["eid"])
              e_grad7 = F.grad(g["wishes"].edata["eid"])
              e_grad8 = F.grad(g["follows"].edata["eid"])
  
          # # correctness check
          def _print_error(a, b):
              for i, (x, y) in enumerate(
                  zip(F.asnumpy(a).flatten(), F.asnumpy(b).flatten())
              ):
                  if not np.allclose(x, y):
                      print("@{} {} v.s. {}".format(i, x, y))
  
          assert F.allclose(r1, r3)
          assert F.allclose(r2, r4)
          assert F.allclose(e_grad1, e_grad5)
          assert F.allclose(e_grad2, e_grad6)
          assert F.allclose(e_grad3, e_grad7)
          assert F.allclose(e_grad4, e_grad8)
  
      _test(fn.copy_e, fn.sum)
      _test(fn.copy_e, fn.max)
      _test(fn.copy_e, fn.min)
  
imports: import itertools
  import unittest
  from collections import Counter
  from itertools import product
  import backend
  import dgl
  import dgl.function
  import networkx
  import numpy
  import pytest
  import scipy.sparse
  from dgl import DGLError
  from scipy.sparse import rand
  from utils import get_cases, parametrize_idtype
label_desc: Use image node_id 5 for calling a global function with example usage: test_unary_copy_e(idtype) without return types