function getDataAsJson(sFileName) {
			var oXHR = new XMLHttpRequest();

			oXHR.open("GET", sBaseUrl + sFileName, /*async*/false);
			oXHR.send();

			return JSON.parse(oXHR.response);
		}
******************************************************************************************
	QUnit.test("getETags", function (assert) {
		var sETag = 'W/"..."',
			mETags,
			that = this;

		function codeUnderTest(sUrl, mScope0) {
			// code under test
			assert.strictEqual(that.oMetaModel.validate(sUrl, mScope0), mScope0);

			assert.notOk("$ETag" in mScope0);
			assert.notOk("$LastModified" in mScope0);
		}

		// code under test (together with c'tor)
		assert.deepEqual(this.oMetaModel.getETags(), {}, "initial value");

		codeUnderTest("/~/A", {
			$Version : "4.0",
			$LastModified : "Fri, 07 Apr 2017 11:21:50 GMT"
		});
		codeUnderTest("/~/B", {
			$Version : "4.0",
			$LastModified : "Tue, 18 Apr 2017 14:40:29 GMT"
		});
		codeUnderTest("/~/C", {
			$Version : "4.0"
		});
		codeUnderTest("/~/D", {
			$Version : "4.0",
			$ETag : sETag
		});

		// code under test
		mETags = this.oMetaModel.getETags();

		assert.deepEqual(mETags, {
			// no need to use UI5Date.getInstance as only the timestamp is relevant
			"/~/A" : new Date(Date.UTC(2017, 3, 7, 11, 21, 50)),
			// no need to use UI5Date.getInstance as only the timestamp is relevant
			"/~/B" : new Date(Date.UTC(2017, 3, 18, 14, 40, 29)),
			"/~/C" : null,
			"/~/D" : sETag // wins over null!
		});
	});

	//*********************************************************************************************
	[{
		message : "Unsupported IncludeAnnotations",
		scope : {
			$Version : "4.0",
			$Reference : {
				"/A/$metadata" : {
					$Include : [
						"A."
					]
				},
				"/B/$metadata" : {
					$IncludeAnnotations : [{
						$TermNamespace : "com.sap.vocabularies.Common.v1"
					}]
				}
			}
		}
	}, {
		message : "A schema cannot span more than one document: tea_busi."
			+ " - is both included and defined",
		scope : {
			$Version : "4.0",
			$Reference : {
				"/B/$metadata" : {
					$Include : [
						"foo.", "tea_busi."
					]
				}
			},
			"tea_busi." : {
				$kind : "Schema"
			}
		}
	}, {
		message : "A schema cannot span more than one document: existing."
			+ " - expected reference URI /B/v1/$metadata but instead saw /B/v2/$metadata",
		scope : {
			$Version : "4.0",
			$Reference : {
				"/A/$metadata" : {
					$Include : [
						"foo.", "bar."
					]
				},
				"/B/v2/$metadata" : {
					$Include : [
						"baz.", "existing."
					]
				}
			}
		}
	}].forEach(function (oFixture) {
		[false, true].forEach(function (bSupportReferences) {
			var sMessage = oFixture.message,
				sTitle = "validate: " + sMessage + ", supportReferences: " + bSupportReferences;

			QUnit.test(sTitle, function (assert) {
				var oError,
					sUrl = "/~/$metadata",
					that = this;

				function codeUnderTest() {
					var oResult = that.oMetaModel.validate(sUrl, oFixture.scope);

					assert.strictEqual(oResult, oFixture.scope);
				}

				this.oMetaModel.bSupportReferences = bSupportReferences;
				// simulate a schema that has been loaded or referenced before
				this.oMetaModel.mSchema2MetadataUrl = {
					// simulate schema that is already read
					"existing." : {"/B/v1/$metadata" : true}
				};
				if (bSupportReferences) {
					oError = new Error(sUrl + ": " + sMessage);
					this.mock(this.oMetaModel.oModel).expects("reportError")
						.withExactArgs(sMessage, sODataMetaModel, sinon.match({
								message : oError.message,
								name : "Error"
							}
						));
				}

				if (bSupportReferences) {
					assert.throws(codeUnderTest, oError);
				} else {
					codeUnderTest();
				}
			});
		});
	});

	//*********************************************************************************************
[false, true].forEach(function (bAnnotationFiles) {
	QUnit.test("_mergeAnnotations: bAnnotationFiles=" + bAnnotationFiles, function (assert) {
		const mScope0 = {
			"A." : {
				$kind : "Schema",
				$Annotations : {}
			},
			"A.Container" : {
				$kind : "EntityContainer"
			},
			"B." : {
				$kind : "Schema",
				$Annotations : {}
			},
			"B.B" : {}
		};
		const mAnnotationScope1 = {
			$Version : "4.0",
			"foo." : {
				$kind : "Schema",
				$Annotations : {}
			},
			"foo.Container" : {
				$kind : "EntityContainer"
			}
		};
		const mAnnotationScope2 = {
			$Version : "4.0",
			"bar." : {
				$kind : "Schema",
				$Annotations : {}
			}
		};

		this.oMetaModel.aAnnotationUris = ["/URI/1", "/URI/2"];

		const aMergeExpectations = [];
		this.oMetaModelMock.expects("validate")
			.withExactArgs(this.oMetaModel.sUrl, mScope0);
		aMergeExpectations.push(this.oMetaModelMock.expects("_doMergeAnnotations")
			.withExactArgs(sinon.match.same(mScope0["A."]), {}));
		aMergeExpectations.push(this.oMetaModelMock.expects("_doMergeAnnotations")
			.withExactArgs(sinon.match.same(mScope0["B."]), sinon.match.object));
		if (bAnnotationFiles) {
			this.oMetaModelMock.expects("validate")
				.withExactArgs("/URI/1", sinon.match.same(mAnnotationScope1));
			this.oMetaModelMock.expects("validate")
				.withExactArgs("/URI/2", sinon.match.same(mAnnotationScope2));
			aMergeExpectations.push(this.oMetaModelMock.expects("_doMergeAnnotations")
				.withExactArgs(sinon.match.same(mAnnotationScope1["foo."]), sinon.match.object,
					true));
			aMergeExpectations.push(this.oMetaModelMock.expects("_doMergeAnnotations")
				.withExactArgs(sinon.match.same(mAnnotationScope2["bar."]), sinon.match.object,
					true));
		}
		assert.deepEqual(this.oMetaModel.mSchema2MetadataUrl, {});

		// code under test
		this.oMetaModel._mergeAnnotations(mScope0,
			bAnnotationFiles ? [mAnnotationScope1, mAnnotationScope2] : []);

		aMergeExpectations.forEach(function (oMergeExpectation) {
			assert.strictEqual(oMergeExpectation.args[0][1], mScope0.$Annotations);
		});
		sinon.assert.callOrder(...aMergeExpectations);
		assert.deepEqual(this.oMetaModel.mSchema2MetadataUrl, bAnnotationFiles ? {
			"A." : {"/a/b/c/d/e/$metadata" : false},
			"B." : {"/a/b/c/d/e/$metadata" : false},
			"bar." : {"/URI/2" : false},
			"foo." : {"/URI/1" : false}
		} : {
			"A." : {"/a/b/c/d/e/$metadata" : false},
			"B." : {"/a/b/c/d/e/$metadata" : false}
		});
	});
});

	//*********************************************************************************************
	QUnit.test("_mergeAnnotations: validation failure for $metadata", function (assert) {
		var oError = new Error(),
			mScope0 = {};

		this.oMetaModelMock.expects("validate")
			.withExactArgs(this.oMetaModel.sUrl, mScope0)
			.throws(oError);

		assert.throws(function () {
			// code under test
			this.oMetaModel._mergeAnnotations(mScope0, []);
		}, oError);
	});

	//*********************************************************************************************
	QUnit.test("_mergeAnnotations: validation failure in annotation file", function (assert) {
		var oError = new Error(),
			mScope0 = {},
			mAnnotationScope1 = {},
			mAnnotationScope2 = {};

		this.oMetaModel.aAnnotationUris = ["n/a", "/my/annotation.xml"];
		this.oMetaModelMock.expects("validate")
			.withExactArgs(this.oMetaModel.sUrl, mScope0);
		this.oMetaModelMock.expects("validate")
			.withExactArgs("n/a", mAnnotationScope1);
		this.oMetaModelMock.expects("validate")
			.withExactArgs("/my/annotation.xml", mAnnotationScope2)
			.throws(oError);

		assert.throws(function () {
			// code under test
			this.oMetaModel._mergeAnnotations(mScope0, [mAnnotationScope1, mAnnotationScope2]);
		}, oError);
	});

	//*********************************************************************************************
	QUnit.test("_mergeAnnotations: with annotation files (legacy)", function (assert) {
		var sNamespace = "com.sap.gateway.default.iwbep.tea_busi.v0001.",
			sWorker = sNamespace + "Worker/",
			sBasicSalaryCurr = sWorker + "SALARY/BASIC_SALARY_CURR",
			sBasicSalaryCurr2 = "another.schema.2.SALARY/BASIC_SALARY_CURR",
			sBonusCurr = sWorker + "SALARY/BONUS_CURR",
			sCommonLabel = "@com.sap.vocabularies.Common.v1.Label",
			sCommonQuickInfo = "@com.sap.vocabularies.Common.v1.QuickInfo",
			sCommonText = "@com.sap.vocabularies.Common.v1.Text",
			sBaseUrl = window.location.pathname.split(/\/(?:test-|)resources\//)[0]
				+ "/test-resources/sap/ui/core/qunit/odata/v4/data/",
			oMetadata = getDataAsJson("metadata.json"),
			oExpectedResult = clone(oMetadata),
			oAnnotation = getDataAsJson("legacy_annotations.json"),
			oAnnotationCopy = clone(oAnnotation);

		function getDataAsJson(sFileName) {
			var oXHR = new XMLHttpRequest();

			oXHR.open("GET", sBaseUrl + sFileName, /*async