function replacer(sKey, vValue) {
			if (sKey === "") {
				return vValue; // the whole object
			}
			return sKey === "k1" ? "w1" : undefined;
		}
******************************************************************************************
	QUnit.test("updateAll: add/change advertised action", function (assert) {
		var mChangeListeners = {},
			oHelperMock = this.mock(_Helper),
			oSource = {
				"#added" : {value : "new"},
				"#changed" : {value : "new"}
			},
			oTarget = {
				"#changed" : {value : "old"}
			};

		oHelperMock.expects("fireChange").withExactArgs(sinon.match.same(mChangeListeners),
			"path/#added", oSource["#added"]); // Note: no sinon.match.same, deep copy involved
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/#added/value", "new");
		oHelperMock.expects("fireChange").withExactArgs(sinon.match.same(mChangeListeners),
			"path/#changed", sinon.match.same(oTarget["#changed"]));
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/#changed/value", "new");

		// code under test
		_Helper.updateAll(mChangeListeners, "path", oTarget, oSource);

		assert.deepEqual(oTarget, oSource);
	});

	//*********************************************************************************************
	QUnit.test("updateSelected: check key predicate, unchanged", function (assert) {
		var o = { // only a container for checkPredicate so that we can mock it
				checkKeyPredicate : function () {}
			},
			oMock = this.mock(o),
			oSource = {
				"@$ui5._" : {
					predicate : "('1')"
				},
				property : "new"
			},
			oTarget = {
				"@$ui5._" : {
					predicate : "('1')"
				},
				property : "old"
			};

		oMock.expects("checkKeyPredicate").withExactArgs("path").returns(true);

		// code under test
		_Helper.updateSelected({}, "path", oTarget, oSource, undefined, o.checkKeyPredicate);

		assert.deepEqual(oTarget, oSource);
	});

	//*********************************************************************************************
	QUnit.test("updateSelected: check key predicate, changed", function (assert) {
		var o = { // only a container for checkPredicate so that we can mock it
				checkKeyPredicate : function () {}
			},
			oMock = this.mock(o),
			oSource = {
				"@$ui5._" : {
					predicate : "('1')"
				}
			},
			oTarget = {
				"@$ui5._" : {
					predicate : "('0')"
				}
			};

		this.mock(_Helper).expects("buildPath").twice().withExactArgs("path", "@$ui5._");
		oMock.expects("checkKeyPredicate").withExactArgs("path").returns(true);

		assert.throws(function () {
			// code under test
			_Helper.updateSelected({}, "path", oTarget, oSource, undefined, o.checkKeyPredicate);
		}, new Error("Key predicate of 'path' changed from ('0') to ('1')"));

		oMock.expects("checkKeyPredicate").withExactArgs("path").returns(false);

		_Helper.updateSelected({}, "path", oTarget, oSource, undefined, o.checkKeyPredicate);

		assert.deepEqual(oTarget, oSource);
	});

	//*********************************************************************************************
[true, undefined].forEach(function (bAllowUndefined) {
	var sTitle = "informAll: comparison of old and new value fires all events, "
		+ "bAllowUndefined=" + bAllowUndefined;

	QUnit.test(sTitle, function (assert) {
		var mChangeListeners = {},
			vUndefinedValue = bAllowUndefined ? undefined : null,
			oHelperMock = this.mock(_Helper),
			oNew = {
				add : "new",
				changed : "changed",
				deep : {
					add : "new",
					blank : {
						add : "new"
					},
					changed : "changed",
					unchanged : "same"
				},
				falsy : "",
				getDeep : {
					inside : "value"
				},
				getFlat : "now",
				unchanged : "same"
			},
			oOld = {
				changed : "init",
				deep : {
					changed : "init",
					deepOld : {},
					old : "old",
					unchanged : "same"
				},
				falsy : null,
				getDeep : "value",
				getFlat : {
					inside : "any"
				},
				old : "old",
				unchanged : "same"
			},
			sUnchangedNew = JSON.stringify(oNew),
			sUnchangedOld = JSON.stringify(oOld);

		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/add", "new");
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/changed", "changed");
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/deep/add", "new");
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/deep/blank/add", "new");
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/deep/changed", "changed");
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/deep/deepOld",
				vUndefinedValue);
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/deep/old", vUndefinedValue);
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/getDeep/inside", "value");
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/falsy", "");
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/getFlat", "now");
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/getFlat/inside",
				vUndefinedValue);
		oHelperMock.expects("fireChange")
			.withExactArgs(sinon.match.same(mChangeListeners), "path/old", vUndefinedValue);

		// code under test
		_Helper.informAll(mChangeListeners, "path", oOld, oNew, bAllowUndefined);

		// assertion - no change on the old and the new value
		assert.strictEqual(JSON.stringify(oOld), sUnchangedOld);
		assert.strictEqual(JSON.stringify(oNew), sUnchangedNew);
	});
});

	//*********************************************************************************************
[{
	sPath : "foo/bar/baz",
	mQueryOptions : undefined,
	bSelected : false
}, {
	sPath : "foo/bar/baz",
	mQueryOptions : {},
	bSelected : true
}, {
	sPath : "foo/bar/baz",
	mQueryOptions : {$select : ["*"]},
	bSelected : true
}, {
	sPath : "foo/bar/baz",
	mQueryOptions : {$select : ["other", "foo/bar/baz"]},
	bSelected : true
}, {
	sPath : "foo/bar/baz",
	mQueryOptions : {$select : ["foo/bar"]},
	bSelected : true
}, {
	sPath : "foo/barbaz",
	mQueryOptions : {$select : ["foo/bar"]},
	bSelected : false
}, {
	sPath : "foo/barbaz/qux",
	mQueryOptions : {
		$expand : {
			"foo/bar" : {
				$select : ["other"]
			},
			"foo/barbaz" : {}
		}
	},
	bSelected : true
}, {
	sPath : "foo/bar/baz",
	mQueryOptions : {
		$expand : {
			"foo/bar" : {
				$select : ["other", "*"]
			}
		}
	},
	bSelected : true
}, {
	sPath : "foo/bar/baz",
	mQueryOptions : {
		$expand : {
			"foo/bar" : {
				$select : ["other"]
			}
		}
	},
	bSelected : false
}].forEach(function (oFixture, i) {
	QUnit.test("isSelected: " + i, function (assert) {
		assert.strictEqual(
			_Helper.isSelected(oFixture.sPath, oFixture.mQueryOptions), oFixture.bSelected);
	});
});

	//*********************************************************************************************
	QUnit.test("makeAbsolute", function (assert) {
		assert.strictEqual(_Helper.makeAbsolute("/foo/bar", "/baz"), "/foo/bar");
		assert.strictEqual(_Helper.makeAbsolute("baz", "/foo/bar"), "/foo/baz");
		assert.strictEqual(_Helper.makeAbsolute("Foo('1')/Bar(baz='2',qux=3)", "/service/"),
			"/service/Foo('1')/Bar(baz='2',qux=3)");
	});

	//*********************************************************************************************
	QUnit.test("makeRelativeUrl", function (assert) {
		assert.strictEqual(_Helper.makeRelativeUrl("/foo/baz", "/foo/bar"), "baz");
		assert.strictEqual(_Helper.makeRelativeUrl("/foo/bar/qux", "/foo/baz"), "bar/qux");
		assert.strictEqual(_Helper.makeRelativeUrl("/foo/baz", "/foo/bar/qux"), "../baz");
		assert.strictEqual(
			_Helper.makeRelativeUrl("/Bar(baz='2',qux=3)", "/Foo"),
			"Bar(baz='2',qux=3)");
	});

	//*********************************************************************************************
	QUnit.test("drillDown", function (assert) {
		var oObject = {
				foo : "bar",
				bar : {
					baz : "qux"
				},
				null : null
			};

		assert.strictEqual(_Helper.drillDown(oObject, []), oObject);
		assert.strictEqual(_Helper.drillDown(oObject, ["foo"]), "bar");
		assert.strictEqual(_Helper.drillDown(oObject, ["bar", "baz"]), "qux");
		assert.strictEqual(_Helper.drillDown(oObject, "bar/baz"), "qux");
		assert.strictEqual(_Helper.drillDown(oObject, ["unknown"]), undefined);
		assert.strictEqual(_Helper.drillDown(oObject, ["unknown", "value"]), undefined);
		assert.strictEqual(_Helper.drillDown(oObject, ["null"]), null);
		assert.strictEqual(_Helper.drillDown(oObject, ["null", "value"]), undefined);
	});

	//*********************************************************************************************
	QUnit.test("deleteProperty", function (assert) {
		var oObject = {
				foo : "foo",
				bar : "bar",
				baz : "baz"
			},
			oDrilledDown = {
				a : "abc",
				qux : "qux",
				x : "xyz"
			},
			oHelperMock = this.mock(_Helper);

		// code under test
		_Helper.deleteProperty(oObject, "bar");

		assert.deepEqual(oObject, {foo : "foo", baz : "baz"});

		oHelperMock.expects("drillDown")
			.withExactArgs(sinon.match.same(oObject), ["foo", "bar", "baz"])
			.returns(oDrilledDown);

		// code under test
		_Helper.deleteProperty(oObject, "foo/bar/baz/qux");

		assert.deepEqual(oDrilledDown, {a : "abc", x : "xyz"});

		[undefined, null].forEach(function (vValue) {
			oHelperMock.expects("drillDown")
				.withExactArgs(sinon.match.same(oObject), ["qux"])
				.returns(vValue);

			// code under test
			_Helper.deleteProperty(oObject, "qux/foo");

			assert.deepEqual(oObject, {foo : "foo", baz : "baz"}, "unchanged");
		});
	});

	//*********************************************************************************************
	[{
		dataPath : "/Foo",
		metaPath : "/Foo"
	}, { // e.g. function call plus key predicate
		dataPath : "/Foo/name.space.bar_42(key='value')(key='value')",
		metaPath : "/Foo/name.space.bar_42"
	}, {
		dataPath : "/Foo(key='value')(key='value')/bar",
		metaPath : "/Foo/bar"
	}, { // any segment with digits only
		dataPath : "/Foo/" + Date.now(),
		metaPath : "/Foo"
	}, {
		dataPath : "/Foo/" + Date.now() + "/bar",
		metaPath : "/Foo/bar"
	}, { // global removal needed
		dataPath : "/Foo(key='value')/" + Date.now() + "/bar(key='value')/" + Date.now(),
		metaPath : "/Foo/bar"
	}, { // transient entity
		dataPath : "/Foo($uid=id-1-23)/bar",
		metaPath : "/Foo/bar"
	}, { // empty path
		dataPath : "",
		metaPath : ""
	}, { // relative with key predicate
		dataPath : "Foo('42')",
		metaPath : "Foo"
	}, { // relative, only key predicate
		dataPath : "('42')",
		metaPath : ""
	}, { // relative, leading key predicate
		dataPath : "('42')/bar",
		metaPath : "bar"
	}, { // relative, only index
		dataPath : "42",
		metaPath : ""
	}, { // relative, leading index
		dataPath : "42/bar",
		metaPath : "bar"
	}].forEach(function (oFixture) {
		QUnit.test("getMetaPath: " + oFixture.dataPath, function (assert) {
			var sMetaPath = _Helper.getMetaPath(oFixture.dataPath);

			assert.strictEqual(sMetaPath, oFixture.metaPath);
		});
	});
	//TODO $all, $count, $crossjoin, $ref, $value
	// Q: Do we need to keep signatures to resolve overloads?
	// A: Most probably not. The spec says "All bound functions with the same function name and
	//    binding parameter type within a namespace MUST specify the same return type."
	//    "All unbound functions with the same function name within a namespace MUST specify the
	//    same return type." --> We can find the return type (from the binding parameter type).
	//    If it comes to annotations, the signature might make a difference, but then "unordered
	//    set of (non-binding) parameter names" is unique.

	//*********************************************************************************************
[false, true].forEach(function (bMissingPredicate) {
	QUnit.test("getPredicates: missing predicate = " + bMissingPredicate, function (assert) {
		var aContexts = [{
				getValue : function () {}
			}, {
				getValue : function () {}
			}, {
				getValue : function () {}
			}],
			oHelperMock = this.mock(_Helper);

		this.mock(aContexts[0]).expects("getValue").withExactArgs().returns("~value0~");
		oHelperMock.expects("getPrivateAnnotation").withExactArgs("~value0~", "predicate")
			.returns("('A')");
		this.mock(aContexts[1]).expects("getValue").withExactArgs().returns("~value1~");
		oHelperMock.expects("getPrivateAnnotation").withExactArgs("~value1~", "predicate")
			.returns(bMissingPredicate ? undefined : "('B')");
		this.mock(aContexts[2]).expects("getValue").withExactArgs().returns("~value2~");
		oHelperMock.expects("getPrivateAnnotation").withExactArgs("~value2~", "predicate")
			.returns("('C')");

		// code under test
		assert.deepEqual(_Helper.getPredicates(aContexts),
			bMissingPredicate ? null : ["('A')", "('B')", "('C')"]);
	});
});

	//*********************************************************************************************
	QUnit.test("getPredicateIndex", function (assert) {
		function success(sPath, sPredicate) {
			assert.strictEqual(sPath.slice(_Helper.getPredicateIndex(sPath)), sPredicate);
		}

		function fail(sPath) {
			assert.throws(function () {
				_Helper.getPredicateIndex(sPath);
			}, new Error("Not a list context path to an entity: " + sPath));
		}

		success("foo('bar')", "('bar')");
		success("foo('bar()')", "('bar()')");
		success("foo('bar')/baz('qux')", "('qux')");
		fail();
		fail("foo");
		fail("foo('bar'");
		fail("foo('bar')/baz)");
	});

	//*********************************************************************************************
	QUnit.test("checkGroupId", function (assert) {
		// valid group IDs
		_Helper.checkGroupId("myGroup");
		_Helper.checkGroupId("$auto");
		_Helper.checkGroupId("$auto.foo");
		_Helper.checkGroupId("$auto.1");
		_Helper.checkGroupId("$direct");
		_Helper.checkGroupId("$single", undefined, /*bAllowSingle