function mock(aData0) {
						return aData0.map(function (oData) {
							var oContext = {getProperty : mustBeMocked},
								oContextMock = that.mock(oContext);

							Object.keys(oData).forEach(function (sKey) {
								oContextMock.expects("getProperty").withExactArgs(sKey)
									.returns(oData[sKey]);
							});

							return oContext;
						});
					}
******************************************************************************************
	//TODO Unknown qualified name some.other.Type at /name.space.Action/0/$ReturnType/$Type,
	//     /ActionImport/@sapui.name
	// --> need to identify action import before we Promise.all([this.requestObject()])
	QUnit.skip("requestValueListInfo: action import parameter", function (assert) {
		var sMappingUrl = "../ValueListService/$metadata",
			oModel = new ODataModel({serviceUrl : "/Foo/DataService/"}),
			oMetadata = {
				$Annotations : {
					"name.space.Action/Category" : {
						"@com.sap.vocabularies.Common.v1.ValueListReferences" : [sMappingUrl]
					}
				},
				$EntityContainer : "zui5_epm_sample.Container",
				"name.space.Action" : [{
					$kind : "Action",
					$Parameter : [{
						$Name : "Category"
					}],
					$ReturnType : {
						$Type : "some.other.Type"
					}
				}],
				"zui5_epm_sample.Container" : {
					ActionImport : {
						$kind : "ActionImport",
						$Action : "name.space.Action"
					}
				}
			},
			oMetaModelMock = this.mock(oModel.getMetaModel()),
			oValueListMappings = {"" : {CollectionPath : ""}},
			oValueListModel = {sServiceUrl : sMappingUrl};

		oMetaModelMock.expects("fetchEntityContainer").atLeast(1)
			.returns(SyncPromise.resolve(oMetadata));
		oMetaModelMock.expects("getOrCreateSharedModel")
			.withExactArgs(sMappingUrl, true, undefined, "name.space.Action")
			.returns(oValueListModel);
		oMetaModelMock.expects("fetchValueListMappings").withExactArgs(
				sinon.match.same(oValueListModel), "name.space.Action",
				sinon.match.same(oMetadata["name.space.Action"][0].$Parameter[0]),
				oMetadata["name.space.Action"])
			.resolves(oValueListMappings);

		// code under test
		return oModel.getMetaModel().requestValueListInfo("/ActionImport/Category")
			.then(function (oResult) {
				assert.deepEqual(oResult, {
					"" : {
						$model : oValueListModel,
						CollectionPath : ""
					}
				});
			});
	});

	//*********************************************************************************************
	["ValueList", "ValueListMapping"].forEach(function (sValueList) {
		QUnit.test("requestValueListInfo: " + sValueList + ", same model w/o reference",
				function (assert) {
			var oAnnotations = {},
				oContext = {
					getBinding : function () {}
				},
				oProperty = {
					$kind : "Property"
				},
				oValueListMappingFoo = {CollectionPath : "foo"},
				oMetadata = {
					$EntityContainer : "value_list.Container",
					"value_list.Container" : {
						$kind : "EntityContainer",
						VH_BusinessPartnerSet : {
							$kind : "EntitySet",
							$Type : "value_list.VH_BusinessPartner"
						}
					},
					"value_list.VH_BusinessPartner" : {
						$kind : "EntityType",
						Country : oProperty
					},
					$Annotations : {
						"value_list.VH_BusinessPartner/Country" : oAnnotations
					}
				},
				oModel = new ODataModel({serviceUrl : "/Foo/ValueListService/"}),
				oMetaModelMock = this.mock(oModel.getMetaModel()),
				sPropertyPath = "/VH_BusinessPartnerSet('0100000000')/Country";

			oAnnotations["@com.sap.vocabularies.Common.v1." + sValueList + "#foo"]
				= oValueListMappingFoo;
			oAnnotations["@com.sap.vocabularies.Common.v1." + sValueList + "#bar"]
				= {CollectionPath : "bar"};
			oMetaModelMock.expects("fetchEntityContainer").atLeast(1)
				.returns(SyncPromise.resolve(oMetadata));
			oMetaModelMock.expects("filterValueListRelevantQualifiers").never();

			// code under test
			return oModel.getMetaModel()
				.requestValueListInfo(sPropertyPath, undefined, oContext)
				.then(function (oResult) {
					assert.strictEqual(oResult.foo.$model, oModel);
					assert.strictEqual(oResult.bar.$model, oModel);
					assert.notOk("$model" in oValueListMappingFoo);
					delete oResult.foo.$model;
					delete oResult.bar.$model;
					assert.deepEqual(oResult, {
						foo : {CollectionPath : "foo"},
						bar : {CollectionPath : "bar"}
					});
				});
		});
	});

	//*********************************************************************************************
	["ValueList", "ValueListMapping"].forEach(function (sValueList) {
		[false, true].forEach(function (bDuplicate) {
			var sTitle = "requestValueListInfo: " + sValueList + ", fixed values: duplicate="
					+ bDuplicate;

			QUnit.test(sTitle, function (assert) {
				var oAnnotations = {
						"@com.sap.vocabularies.Common.v1.ValueListWithFixedValues" : true
					},
					oMetadata = {
						$EntityContainer : "value_list.Container",
						"value_list.Container" : {
							$kind : "EntityContainer",
							VH_BusinessPartnerSet : {
								$kind : "EntitySet",
								$Type : "value_list.VH_BusinessPartner"
							}
						},
						"value_list.VH_BusinessPartner" : {
							$kind : "EntityType",
							Country : {}
						},
						$Annotations : {
							"value_list.VH_BusinessPartner/Country" : oAnnotations
						}
					},
					oModel = new ODataModel({serviceUrl : "/Foo/ValueListService/"}),
					oMetaModel = oModel.getMetaModel(),
					sPropertyPath = "/VH_BusinessPartnerSet('42')/Country";

				oAnnotations["@com.sap.vocabularies.Common.v1." + sValueList + "#foo"] = {
						CollectionPath : "foo",
						SearchSupported : true // BCP: 2280012068
					};
				if (bDuplicate) {
					oAnnotations["@com.sap.vocabularies.Common.v1." + sValueList + "#bar"] = {};
				}
				this.mock(oMetaModel).expects("fetchEntityContainer").atLeast(1)
					.returns(SyncPromise.resolve(oMetadata));

				assert.strictEqual(oMetaModel.getValueListType(sPropertyPath), ValueListType.Fixed);

				// code under test
				return oMetaModel.requestValueListInfo(sPropertyPath).then(function (oResult) {
					assert.notOk(bDuplicate);
					assert.strictEqual(oResult[""].$model, oModel);
					delete oResult[""].$model;
					assert.deepEqual(oResult, {
						"" : { // for fixed values, actual qualifier is ignored here
							$qualifier : "foo",
							CollectionPath : "foo"
						}
					});
				}, function (oError) {
					assert.ok(bDuplicate);
					assert.strictEqual(oError.message, "Annotation "
						+ "'com.sap.vocabularies.Common.v1.ValueListWithFixedValues' but not "
						+ "exactly one 'com.sap.vocabularies.Common.v1.ValueList' for property "
						+ sPropertyPath);
				});
			});
		});
	});

	//*********************************************************************************************
	QUnit.test("requestValueListInfo: property in cross-service reference", function (assert) {
		var sMappingUrl = "../ValueListService/$metadata",
			oModel = new ODataModel({serviceUrl : "/Foo/DataService/"}),
			oMetaModelMock = this.mock(oModel.getMetaModel()),
			oProperty = {
				$kind : "Property"
			},
			oMetadata = {
				$Version : "4.0",
				$Reference : {
					"/Foo/EpmSample/$metadata" : {
						$Include : ["zui5_epm_sample."]
					}
				},
				$EntityContainer : "base.Container",
				"base.Container" : {
					BusinessPartnerList : {
						$kind : "EntitySet",
						$Type : "base.BusinessPartner"
					}
				},
				"base.BusinessPartner" : {
					$kind : "EntityType",
					BP_2_PRODUCT : {
						$kind : "NavigationProperty",
						$Type : "zui5_epm_sample.Product"
					}
				}
			},
			oMetadataProduct = {
				$Version : "4.0",
				"zui5_epm_sample.Product" : {
					$kind : "EntityType",
					Category : oProperty
				},
				"zui5_epm_sample." : {
					$kind : "Schema",
					$Annotations : {
						"zui5_epm_sample.Product/Category" : {
							"@com.sap.vocabularies.Common.v1.ValueListReferences" : [sMappingUrl]
						}
					}
				}
			},
			sPropertyPath = "/BusinessPartnerList('0100000000')/BP_2_PRODUCT('HT-1000')/Category",
			oRequestorMock = this.mock(oModel.oMetaModel.oRequestor),
			oValueListMappings = {
				"" : {CollectionPath : ""}
			},
			oValueListModel = {sServiceUrl : sMappingUrl};

		oRequestorMock.expects("read").withExactArgs("/Foo/DataService/$metadata", false, undefined)
			.resolves(oMetadata);
		oRequestorMock.expects("read").withExactArgs("/Foo/EpmSample/$metadata")
			.resolves(oMetadataProduct);
		oMetaModelMock.expects("getOrCreateSharedModel")
			.withExactArgs(sMappingUrl, true, true, "zui5_epm_sample.Product")
			.returns(oValueListModel);
		oMetaModelMock.expects("fetchValueListMappings")
			.withExactArgs(sinon.match.same(oValueListModel), "zui5_epm_sample.Product",
				sinon.match.same(oProperty), undefined)
			.resolves(oValueListMappings);

		// code under test
		return oModel.getMetaModel().requestValueListInfo(sPropertyPath, true)
			.then(function (oResult) {
				assert.deepEqual(oResult, {
					"" : {
						$model : oValueListModel,
						CollectionPath : ""
					}
				});
			});
	});

	//*********************************************************************************************
	["ValueList", "ValueListMapping"].forEach(function (sValueList) {
		QUnit.test("requestValueListInfo: " + sValueList
				+ ", same qualifier in reference and local", function (assert) {
			var sMappingUrl = "../ValueListService/$metadata",
				oAnnotations = {
					"@com.sap.vocabularies.Common.v1.ValueListReferences" : [sMappingUrl]
				},
				oProperty = {
					$kind : "Property"
				},
				oMetadata = {
					$EntityContainer : "zui5_epm_sample.Container",
					"zui5_epm_sample.Container" : {
						$kind : "EntityContainer",
						ProductList : {
							$kind : "EntitySet",
							$Type : "zui5_epm_sample.Product"
						}
					},
					"zui5_epm_sample.Product" : {
						$kind : "EntityType",
						Category : oProperty
					},
					$Annotations : {
						"zui5_epm_sample.Product/Category" : oAnnotations
					}
				},
				oModel = new ODataModel({serviceUrl : "/Foo/ValueListService/"}),
				oMetaModelMock = this.mock(oModel.getMetaModel()),
				sPropertyPath = "/ProductList('HT-1000')/Category",
				oValueListModel = {};

			oAnnotations["@com.sap.vocabularies.Common.v1." + sValueList + "#foo"] = {};
			oMetaModelMock.expects("fetchEntityContainer").atLeast(1)
				.returns(SyncPromise.resolve(oMetadata));
			oMetaModelMock.expects("getOrCreateSharedModel")
				.withExactArgs(sMappingUrl, true, undefined, "zui5_epm_sample.Product")
				.returns(oValueListModel);
			oMetaModelMock.expects("fetchValueListMappings")
				.withExactArgs(sinon.match.same(oValueListModel), "zui5_epm_sample.Product",
					sinon.match.same(oProperty), undefined)
				.resolves({foo : {}});

			// code under test
			return oModel.getMetaModel().requestValueListInfo(sPropertyPath).then(function () {
				assert.ok(false);
			}, function (oError) {
				assert.strictEqual(oError.message,
					"Annotations 'com.sap.vocabularies.Common.v1.ValueList' with identical "
					+ "qualifier 'foo' for property " + sPropertyPath + " in " + sMappingUrl
					+ " and " + oModel.sServiceUrl + "$metadata");
			});
		});
	});

	//*********************************************************************************************
	QUnit.test("requestValueListInfo: ValueList with CollectionRoot in data service",
			function (assert) {
		var sMappingUrl = "../ValueListService/$metadata",
			oModel = new ODataModel({serviceUrl : "/Foo/DataService/"}),
			oMetaModelMock = this.mock(oModel.getMetaModel()),
			oProperty = {
				$kind : "Property"
			},
			sPropertyPath = "/ProductList('HT-1000')/Category",
			oMetadata = {
				$EntityContainer : "zui5_epm_sample.Container",
				"zui5_epm_sample.Product" : {
					$kind : "EntityType",
					Category : oProperty
				},
				$Annotations : {
					"zui5_epm_sample.Product/Category" : {
						"@com.sap.vocabularies.Common.v1.ValueList#foo" : {
							CollectionPath : "VH_CategorySet",
							CollectionRoot : sMappingUrl,
							SearchSupported : true
						}
					}
				},
				"zui5_epm_sample.Container" : {
					ProductList : {
						$kind : "EntitySet",
						$Type : "zui5_epm_sample.Product"
					}
				}
			},
			oValueListModel = {id : "ValueListModel"}; // for deepEqual

		oMetaModelMock.expects("fetchEntityContainer").atLeast(1)
			.returns(SyncPromise.resolve(oMetadata));
		oMetaModelMock.expects("getOrCreateSharedModel")
			.withExactArgs(sMappingUrl, true, undefined, "zui5_epm_sample.Product")
			.returns(oValueListModel);

		// code under test
		return oModel.getMetaModel().requestValueListInfo(sPropertyPath).then(function (oResult) {
			assert.deepEqual(oResult, {
				foo : {
					$model : oValueListModel,
					CollectionPath : "VH_CategorySet"
				}
			});
			assert.strictEqual(oMetadata.$Annotations["zui5_epm_sample.Product/Category"]
				["@com.sap.vocabularies.Common.v1.ValueList#foo"].CollectionRoot,
				sMappingUrl);
		});
	});

	//*********************************************************************************************
	[false, true].forEach(function (bOverride) {
		QUnit.test("requestValueListInfo: ValueList with CollectionRoot, same qualifier, "
				+ (bOverride ? "override" : "collision"), function (assert) {
			var sCollectionRoot = "", // unrealistic, but enforces "CollectionRoot" in ...
				oProperty = {
					$kind : "Property"
				},
				sValueListService = "../ValueListService/$metadata",
				oMetadata = {
					$EntityContainer : "zui5_epm_sample.Container",
					"zui5_epm_sample.Product" : {
						$kind : "EntityType",
						Category : oProperty
					},
					"zui5_epm_sample.Container" : {
						ProductList : {
							$kind : "EntitySet",
							$Type : "zui5_epm_sample.Product"
						}
					},
					$Annotations : {
						"zui5_epm_sample.Product/Category" : {
							"@com.sap.vocabularies.Common.v1.ValueList#bar" : {
								CollectionPath : "foo",
								CollectionRoot : sCollectionRoot,
								Label : "from data service"
							},
							"@com.sap.vocabularies.Common.v1.ValueListReferences" :
								[sValueListService],
							"@com.sap.vocabularies.Common.v1.ValueListWithFixedValues" : true
						}
					}
				},
				oModel = new ODataModel({serviceUrl : "/Foo/DataService/"}),
				oMetaModelMock = this.mock(oModel.getMetaModel()),
				sPropertyPath = "/ProductList('HT-1000')/Category",
				oValueListModel = {id : "ValueListModel"}, // for deepEqual
				oValueListModel2 = bOverride ? oValueListModel : {},
				oValueListMapping = {
					$model : oValueListModel,
					CollectionPath : "foo",
					Label : "from value list service"
				};

			oMetaModelMock.expects("fetchEntityContainer").atLeast(1)
				.returns(SyncPromise.resolve(oMetadata));
			oMetaModelMock.expects("getOrCreateSharedModel")
				.withExactArgs(sValueListService, true, true, "zui5_epm_sample.Product")
				.returns(oValueListModel);
			oMetaModelMock.expects("getOrCreateSharedModel")
				.withExactArgs(sCollectionRoot, true, true, "zui5_epm_sample.Product")
				.returns(oValueListModel2);
			oMetaModelMock.expects("fetchValueListMappings")
				.withExactArgs(sinon.match.same(oValueListModel), "zui5_epm_sample.Product",
					sinon.match.same(oProperty), undefined)
				.resolves({bar : oValueListMapping});

			// code under test
			return oModel.getMetaModel().requestValueListInfo(sPropertyPath, true)
				.then(function (oResult) {
					assert.strictEqual(bOverride, true);
					assert.deepEqual(oResult, {
						"" : {
							$model : oValueListModel,
							$qualifier : "bar",
							CollectionPath : "foo",
							Label : "from data service"
						}
					});
				}, function (oError) {
					assert.strictEqual(bOverride, false);
					assert.strictEqual(oError.message,
						"Annotations 'com.sap.vocabularies.Common.v1.ValueList' with "
							+ "identical qualifier 'bar' for property " + sPropertyPath
							+ " in " + sValueListService + " and /Foo/DataService/$metadata");
				});
		});
	});

	//*********************************************************************************************
	QUnit.test("requestValueListInfo: two ValueListReferences stay in order", function (assert) {
		var oProperty = {
				$kind : "Property"
			},
			sValueListService1 = "../FirstValueListService/$metadata",
			sValueListService2 = "../SecondValueListService/$metadata",
			oMetadata = {
				$EntityContainer : "zui5_epm_sample.Container",
				"zui5_epm_sample.Product" : {
					$kind : "EntityType",
					Category : oProperty
				},
				"zui5_epm_sample.Container" : {
					ProductList : {
						$kind : "EntitySet",
						$Type : "zui5_epm_sample.Product"
					}
				},
				$Annotations : {
					"zui5_epm_sample.Product/Category" : {
						"@com.sap.vocabularies.Common.v1.ValueListReferences" :
							[sValueListService1, sValueListService2]
					}
				}
			},
			oModel = new ODataModel({serviceUrl : "/Foo/DataService/"}),
			oMetaModelMock = this.mock(oModel.getMetaModel()),
			sPropertyPath = "/ProductList('HT-1000')/Category",
			fnResolve1,
			fnResolve2,
			oResultPromise,
			oValueListModel1 = {id : "FirstValueListModel"}, // for deepEqual
			oValueListModel2 = {id : "SecondValueListModel"},
			oValueListMapping1 = {
				$model : oValueListModel1,
				CollectionPath : "foo",
				Label : "from first value list service"
			},
			oValueListMapping2 = {
				$model : oValueListModel2,
				CollectionPath : "bar",
				Label : "from second value list service"
			};

		oMetaModelMock.expects("fetchEntityContainer").atLeast(1)
			.returns(SyncPromise.resolve(oMetadata));
		oMetaModelMock.expects("getOrCreateSharedModel")
			.withExactArgs(sValueListService1, true, true, "zui5_epm_sample.Product")
			.returns(oValueListModel1);
		oMetaModelMock.expects("getOrCreateSharedModel")
			.withExactArgs(sValueListService2, true, true, "zui5_epm_sample.Product")
			.returns(oValueListModel2);
		oMetaModelMock.expects("fetchValueListMappings")
			.withExactArgs(sinon.match.same(oValueListModel1), "zui5_epm_sample.Product",
				sinon.match.same(oProperty), undefined)
			.returns(new Promise(function (resolve) { fnResolve1 = resolve; }));
		oMetaModelMock.expects("fetchValueListMappings")
			.withExactArgs(sinon.match.same(oValueListModel2), "zui5_epm_sample.Product",
				sinon.match.same(oProperty), undefined)
			.returns(new Promise(function (resolve) { fnResolve2 = resolve; }));

		// code under test
		oResultPromise = oModel.getMetaModel().requestValueListInfo(sPropertyPath, true)
			.then(function (oResult) {
				assert.deepEqual(Object.keys(oResult), ["foo", "bar"]);
				assert.deepEqual(oResult, {
					foo : {
						$model : oValueListModel1,
						CollectionPath : "foo",
						Label : "from first value list service"
					},
					bar : {
						$model : oValueListModel2,
						CollectionPath : "bar",
						Label : "from second value list service"
					}
				});
			});

		fnResolve2({bar : oValueListMapping2});
		// make sure the first fetchValueListMapping call is resolved only after the second
		setTimeout(fnResolve1, 0, {foo : oValueListMapping1});
		return oResultPromise;
	});

	//*********************************************************************************************
	QUnit.test("fetchData", function (assert) {
		var oMetaData = {
				"some.schema." : {
					$kind : "Schema"
				}
			};

		this.oMetaModelMock.expects("fetchEntityContainer")
			.withExactArgs()
			.resolves(oMetaData);

		// code under test
		return this.oMetaModel.fetchData().then(function (oResult) {
			assert.deepEqual(oResult, oMetaData);

			delete oResult["some.schema."].$kind;
			assert.strictEqual(oMetaData["some.schema."].$kind, "Schema", "original is unchanged");
		});
	});

	//*********************************************************************************************
	QUnit.test("getData, requestData", function (assert) {
		return TestUtils.checkGetAndRequest(this, this.oMetaModel, assert, "fetchData");
	});

	//*********************************************************************************************
[false, true].forEach(function (bDestroyAlreadyCalled) {
	[false, true].forEach(function (bEmptyResponse) {
		[false, true].forEach(function (bHasStandardCode) {
			[0, false, true].forEach(function (bHasAlternateKey) {
				var sTitle = "requestCodeList, empty response: " + bEmptyResponse
					+ ", with alternate key: " + bHasAlternateKey
					+ ", with standard code: " + bHasStandardCode
					+ "; #destroy already called: " + bDestroyAlreadyCalled;

				QUnit.test(sTitle, function (assert) {
					var oCodeListBinding = {
							destroy : mustBeMocked,
							requestContexts : mustBeMocked
						},
						oCodeListMetaModel = {
							getObject : mustBeMocked,
							requestObject : mustBeMocked
						},
						oCodeListMetaModelMock = this.mock(oCodeListMetaModel),
						oCodeListModel = {
							bindList : mustBeMocked,
							destroy : mustBeMocked,
							getMetaModel : mustBeMocked,
							sServiceUrl : "/foo/bar/default/iwbep/common/0001/"
						},
						aData = [],
						aSelect = [
							bHasAlternateKey
								? "ExternalCode"
								: "UnitCode", "DecimalPlaces", "MyText"
						],
						sUrl = "../../../../default/iwbep/common/0001/$metadata",
						that = this;

					/*
Returns mock context instances for the given data rows, properly set up with
expectations.

@param {object[]} aData - some data rows
@returns {object[]} mock context instances