blob: cc41cdfad80fe17f5aa36af59e134dfcef70e078 [file] [log] [blame]
// Copyright 2017 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package net.starlark.java.eval;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for how Starlark value implementations interact with {@link Mutability#freeze}. */
@RunWith(JUnit4.class)
public final class StarlarkMutableTest {
@Test
public void testListViewsCheckMutability() throws Exception {
Mutability mutability = Mutability.create("test");
StarlarkList<Object> list =
StarlarkList.copyOf(
mutability, ImmutableList.of(StarlarkInt.of(1), StarlarkInt.of(2), StarlarkInt.of(3)));
mutability.freeze();
{
Iterator<?> it = list.iterator();
it.next();
assertThrows(
UnsupportedOperationException.class,
() -> it.remove());
}
{
Iterator<?> it = list.listIterator();
it.next();
assertThrows(
UnsupportedOperationException.class,
() -> it.remove());
}
{
Iterator<?> it = list.listIterator(1);
it.next();
assertThrows(
UnsupportedOperationException.class,
() -> it.remove());
}
{
List<Object> sublist = list.subList(1, 2);
assertThrows(
UnsupportedOperationException.class,
() -> sublist.set(0, 4));
}
}
@Test
public void testDictViewsCheckMutability() throws Exception {
Mutability mutability = Mutability.create("test");
Dict<Object, Object> dict =
Dict.copyOf(
mutability,
ImmutableMap.of(
StarlarkInt.of(1), StarlarkInt.of(2), StarlarkInt.of(3), StarlarkInt.of(4)));
mutability.freeze();
{
Iterator<Map.Entry<Object, Object>> it = dict.entrySet().iterator();
Map.Entry<Object, Object> entry = it.next();
assertThrows(
UnsupportedOperationException.class,
() -> entry.setValue(5));
}
{
Iterator<Object> it = dict.keySet().iterator();
it.next();
assertThrows(
UnsupportedOperationException.class,
() -> it.remove());
}
{
Iterator<Object> it = dict.values().iterator();
it.next();
assertThrows(
UnsupportedOperationException.class,
() -> it.remove());
}
}
@Test
public void testDictBuilder() throws Exception {
// put
Dict<String, String> dict1 =
Dict.<String, String>builder()
.put("one", "1")
.put("two", "2.0")
.put("two", "2") // overwrites previous entry
.put("three", "3")
.buildImmutable();
assertThat(dict1.toString()).isEqualTo("{\"one\": \"1\", \"two\": \"2\", \"three\": \"3\"}");
assertThrows(EvalException.class, dict1::clearEntries); // immutable
// putAll
Dict<String, String> dict2 =
Dict.<String, String>builder()
.putAll(dict1)
.putAll(ImmutableMap.of("four", "4", "five", "5"))
.buildImmutable();
assertThat(dict2.toString())
.isEqualTo(
"{\"one\": \"1\", \"two\": \"2\", \"three\": \"3\", \"four\": \"4\", \"five\": \"5\"}");
// builder reuse and mutability
Dict.Builder<String, String> builder = Dict.<String, String>builder().putAll(dict1);
builder.put("three", "33"); // overwrites previous entry
Mutability mu = Mutability.create("test");
Dict<String, String> dict3 = builder.build(mu);
dict3.putEntry("four", "4"); // new entry
dict3.putEntry("two", "22"); // overwrites previous entry
assertThat(dict3.toString())
.isEqualTo("{\"one\": \"1\", \"two\": \"22\", \"three\": \"33\", \"four\": \"4\"}");
mu.close();
assertThrows(EvalException.class, dict1::clearEntries); // frozen
builder.put("five", "5"); // keep building
Dict<String, String> dict4 = builder.buildImmutable();
assertThat(dict4.toString())
.isEqualTo("{\"one\": \"1\", \"two\": \"2\", \"three\": \"33\", \"five\": \"5\"}");
assertThat(dict3.toString())
.isEqualTo(
"{\"one\": \"1\", \"two\": \"22\", \"three\": \"33\", \"four\": \"4\"}"); // unchanged
}
@Test
public void testImmutableDictUsesImmutableMap() {
Mutability mu = Mutability.IMMUTABLE;
Dict<String, String> dict = Dict.<String, String>builder().put("cat", "dog").build(mu);
assertThat(dict.getContentsForTesting()).isInstanceOf(ImmutableMap.class);
assertThat(dict.getContentsForTesting()).containsExactly("cat", "dog");
}
@Test
public void testMutableDictSwitchesToImmutableMapWhenFrozen() {
Mutability mu = Mutability.create("test");
Dict<String, String> dict = Dict.<String, String>builder().put("cat", "dog").build(mu);
assertThat(dict.getContentsForTesting()).isInstanceOf(LinkedHashMap.class);
assertThat(dict.getContentsForTesting()).containsExactly("cat", "dog");
mu.freeze();
assertThat(dict.getContentsForTesting()).isInstanceOf(ImmutableMap.class);
assertThat(dict.getContentsForTesting()).containsExactly("cat", "dog");
}
@Test
public void testMutableDictSwitchesToImmutableMapWhenFrozen_usesCanonicalEmptyImmutableMap() {
Mutability mu = Mutability.create("test");
Dict<String, String> dict = Dict.<String, String>builder().build(mu);
assertThat(dict.getContentsForTesting()).isInstanceOf(LinkedHashMap.class);
assertThat(dict.getContentsForTesting()).isEmpty();
mu.freeze();
assertThat(dict.getContentsForTesting()).isSameInstanceAs(ImmutableMap.of());
}
}