Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions exist-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@
<exclude>src/test/java/org/exist/xquery/functions/xmldb/XMLDBAuthenticateTest.java</exclude>
<exclude>src/main/java/org/exist/xquery/functions/util/Eval.java</exclude>
<exclude>src/main/java/org/exist/xquery/value/ArrayListValueSequence.java</exclude>
<exclude>src/test/java/org/exist/xquery/value/BifurcanMapTest.java</exclude>
<exclude>src/main/java/org/exist/xquery/value/AtomicValueComparator.java</exclude>
<exclude>src/main/java/org/exist/xquery/value/ItemComparator.java</exclude>
<exclude>src/main/java/org/exist/xquery/value/SequenceComparator.java</exclude>
Expand Down Expand Up @@ -820,6 +821,7 @@ The original license statement is also included below.]]></preamble>
<include>src/test/java/org/exist/xquery/functions/xmldb/XMLDBAuthenticateTest.java</include>
<include>src/main/java/org/exist/xquery/functions/util/Eval.java</include>
<include>src/main/java/org/exist/xquery/value/ArrayListValueSequence.java</include>
<include>src/test/java/org/exist/xquery/value/BifurcanMapTest.java</include>
<include>src/main/java/org/exist/xquery/value/AtomicValueComparator.java</include>
<include>src/main/java/org/exist/xquery/value/ItemComparator.java</include>
<include>src/main/java/org/exist/xquery/value/SequenceComparator.java</include>
Expand Down
192 changes: 192 additions & 0 deletions exist-core/src/test/java/org/exist/xquery/value/BifurcanMapTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright (C) 2014, Evolved Binary Ltd
*
* This file was originally ported from FusionDB to eXist-db by
* Evolved Binary, for the benefit of the eXist-db Open Source community.
* Only the ported code as it appears in this file, at the time that
* it was contributed to eXist-db, was re-licensed under The GNU
* Lesser General Public License v2.1 only for use in eXist-db.
*
* This license grant applies only to a snapshot of the code as it
* appeared when ported, it does not offer or infer any rights to either
* updates of this source code or access to the original source code.
*
* The GNU Lesser General Public License v2.1 only license follows.
*
* ---------------------------------------------------------------------
*
* Copyright (C) 2014, Evolved Binary Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2.1.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.xquery.value;

import io.lacuna.bifurcan.IMap;
import io.lacuna.bifurcan.LinearMap;
import org.junit.Test;

import javax.annotation.Nullable;

import static org.exist.xquery.functions.map.MapType.newLinearMap;
import static org.junit.Assert.*;

/**
* Tests to demonstrate Bifurcan Map behaviour
*
* @author <a href="mailto:[email protected]">Adam Retter</a>
*/
public class BifurcanMapTest {

/**
* Reproduces the XQSuite Test `mt:immutable-remove-then-remove()` from `maps.xql`:
*
* <code>
* let $removed := map:remove(map { 1: true(), 2: true() }, 2)
* let $expected := $removed(1)
* let $result := map:remove($removed, 1)
* return
* (
* $expected eq $removed(1),
* $expected ne $result(1)
* )
* </code>
*/
@Test
public void immutableRemoveThenRemove() {

/*
1. Create the initial map: `map { 1: true(), 2: true() }`
*/
final IMap<AtomicValue, Sequence> map = createMap();
checkMapIsForked(map);

/*
2. Remove the entry in the initial map with key `2`: `let $removed := map:remove(..., 2)`
*/
final IMap<AtomicValue, Sequence> removed = removeFromMap(map, new IntegerValue(2));
// taken from MapType.java constructor
checkMapIsForked(removed);

/*
3. Get the entry in the removed map with key `1`: `$expected := $removed(1)`
*/
final Sequence expected = getFromMap(removed, new IntegerValue(1));

/*
4. Remove the entry in the removed map with key `1`: `let $result := map:remove($removed, 1)`
*/
final IMap<AtomicValue, Sequence> result = removeFromMap(removed, new IntegerValue(1));
// taken from MapType.java constructor
checkMapIsForked(result);

/*
`$expected eq $removed(1)`
*/
assertEquals(expected, getFromMap(removed, new IntegerValue(1)));

/*
`$expected ne $result(1)`
*/
assertNotEquals(expected, getFromMap(result, new IntegerValue(1)));
}

/*
Taken from MapExpr.java
*/
private static IMap<AtomicValue, Sequence> createMap() {
final IMap<AtomicValue, Sequence> map = newLinearMap(null);
map.put(new IntegerValue(1), BooleanValue.TRUE);
map.put(new IntegerValue(2), BooleanValue.TRUE);

// return an immutable map
return map.forked();
}

/*
Taken from MapFunction.java MapFunction#remove(Sequence[])
*/
private static IMap<AtomicValue, Sequence> removeFromMap(final IMap<AtomicValue, Sequence> map, final AtomicValue... keys) {
// create a transient map
IMap<AtomicValue, Sequence> newMap = map.linear();

for (final AtomicValue key: keys) {
newMap = newMap.remove(key);
}

// return an immutable map
return newMap.forked();
}

/*
Taken from MapType.java MapType#get(AtomicValue)
*/
private static @Nullable Sequence getFromMap(final IMap<AtomicValue, Sequence> map, final AtomicValue key) {
return map.get(key, null);
}

@Test
public void bifurcanImmutableRemoveThenRemove() {

/*
1. Create the initial map: `map { 1: true(), 2: true() }`
*/
IMap<Integer, Boolean> map = new LinearMap<>();
map.put(1, true);
map.put(2, true);
map = map.forked(); // make the map immutable
checkMapIsForked(map);

/*
2. Remove the entry in the initial map with key `2`: `let $removed := map:remove(..., 2)`
*/
IMap<Integer, Boolean> removed = map.linear(); // create a transient map for modifications
assertFalse(removed == map);
removed = removed.remove(2);
removed = removed.forked(); // make the map immutable
checkMapIsForked(removed);

/*
3. Get the entry in the removed map with key `1`: `$expected := $removed(1)`
*/
final Boolean expected = removed.get(1, null);

/*
4. Remove the entry in the removed map with key `1`: `let $result := map:remove($removed, 1)`
*/
IMap<Integer, Boolean> result = removed.linear(); // create a transient map for modifications
assertFalse(result == removed);
result = result.remove(1);
result = result.forked(); // make the map immutable
checkMapIsForked(result);

/*
`$expected eq $removed(1)`
*/
assertEquals(expected, removed.get(1, null));

/*
`$expected ne $result(1)`
*/
assertNotEquals(expected, result.get(1, null));
}

/*
Taken from MapType.java constructor
*/
private static <K,V> void checkMapIsForked(final IMap<K, V> map) throws IllegalArgumentException {
if (map.isLinear()) {
throw new IllegalArgumentException("Map must be immutable, but linear Map was provided");
}
}
}
12 changes: 9 additions & 3 deletions exist-core/src/test/xquery/maps/MapsLookup.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,19 @@
<remove-collection collection="/db/xq3-test"/>
</tearDown>

<!-- fix - "variable declaration of '$var' cannot be executed because of a circularity" -->
<!-- TODO(AR) - this raises "err:XQST0054 It is a static error if a variable depends on itself" because dependencies are calculated over zealously -->
<test output="text" id="maps-lookup-001" ignore="true">
<task>maps-lookup-001</task>
<code><![CDATA[xquery version "3.0";

declare variable $var := map { 'a':local:fun#1, 'b':0 };
declare function local:fun($a) { $var?b = $a };
declare variable $var := map {
'a' : local:fun#1,
'b' : 0
};

declare function local:fun($a) {
$var?b = $a
};
local:fun(1)

]]></code>
Expand Down
Loading