def msg_fields(obj):
     return ("{sep}{0}{sep}{1}"
             "{sep}fields-props {2}"
             "{sep}props-fields {3}"
             "{sep}json {4}".format(*tuple(
                 map(lambda x: "%s %s" % (len(x), x), [
                     obj_props,
                     fields.names(),
                     set(fields.names()).difference(obj_props),
                     set(obj_props).difference(fields.names())
                 ])),
                                    format_long_args(json.dumps(obj)),
                                    sep="\n>> "))
    def subtest_parse_xyzjson_map_dupe_case(self, folder, fname):
        with self.subTest(folder=folder, fname=fname):
            import random
            mix_case = lambda txt, idx: "".join([(s.lower()
                                                  if s.isupper() else s.upper(
                                                  )) if i == idx else s
                                                 for i, s in enumerate(txt)])
            new_feat = lambda ft, props: dict(ft, properties=dict(props))
            n_new_ft = 2
            with self.subTest(folder=folder, fname=fname):
                resource = TestFolder(folder)
                txt = resource.load(fname)
                obj = json.loads(txt)
                features = obj["features"]
                features[0]["properties"].update(fid=1)  # test fid
                lst_k = list()
                lst_new_k = list()
                props_ = dict(obj["features"][0]["properties"])
                props_ = sorted(props_.keys())
                debug_msg = ""
                for k in props_:
                    lst_k.append(k)
                    for i in range(n_new_ft):
                        ft = dict(features[0])
                        props = dict(ft["properties"])
                        new_k = k
                        while new_k == k:
                            idx = random.randint(0, len(k) - 1)
                            if k == "fid": idx = i
                            new_k = mix_case(k, idx)
                        if new_k not in lst_new_k: lst_new_k.append(new_k)
                        debug_msg += format_long_args("\n", "mix_case", k,
                                                      new_k, props[k], idx)
                        props[new_k] = props.pop(k) or ""
                        new_ft = new_feat(ft, props)
                        features.append(new_ft)
                map_fields = self.subtest_parse_xyzjson_map_chunk(obj,
                                                                  chunk_size=1)

                # assert that parser handle dupe of case insensitive prop name, e.g. name vs Name
                self.assertEqual(len(map_fields), 1, "not single geom")
                lst_fields = list(map_fields.values())[0]
                for k in lst_k:
                    self.assertIn(k, lst_fields[0].names())

                # debug
                debug_msg += format_long_args("\n", lst_fields[0].names())
                for k, fields in zip(lst_new_k, lst_fields[1:]):
                    if k.lower() in {parser.QGS_ID, parser.QGS_XYZ_ID}:
                        k = "{}_{}".format(
                            k, "".join(
                                str(i) for i, s in enumerate(k)
                                if s.isupper()))
                    debug_msg += format_long_args("\n", k in fields.names(), k,
                                                  fields.names())

                # self.assertEqual(len(lst_fields), len(lst_new_k) + 1)
                for k, fields in zip(lst_new_k, lst_fields[1:]):
                    if k.lower() in {parser.QGS_ID, parser.QGS_XYZ_ID}:
                        k = "{}_{}".format(
                            k, "".join(
                                str(i) for i, s in enumerate(k)
                                if s.isupper()))
                    self.assertIn(
                        k, fields.names(),
                        "len lst_fields vs. len keys: %s != %s" %
                        (len(lst_fields), len(lst_new_k) + 1) + debug_msg)