1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.testutils; 18 19 import android.system.ErrnoException; 20 21 import androidx.annotation.NonNull; 22 23 import com.android.net.module.util.IBpfMap; 24 import com.android.net.module.util.Struct; 25 26 import java.io.IOException; 27 import java.util.Iterator; 28 import java.util.Map; 29 import java.util.NoSuchElementException; 30 import java.util.Objects; 31 import java.util.concurrent.ConcurrentHashMap; 32 33 /** 34 * 35 * Fake BPF map class for tests that have no privilege to access real BPF maps. TestBpfMap does not 36 * load JNI and all member functions do not access real BPF maps. 37 * 38 * Implements IBpfMap so that any class using IBpfMap can use this class in its tests. 39 * 40 * @param <K> the key type 41 * @param <V> the value type 42 */ 43 public class TestBpfMap<K extends Struct, V extends Struct> implements IBpfMap<K, V> { 44 private final ConcurrentHashMap<K, V> mMap = new ConcurrentHashMap<>(); 45 TestBpfMap()46 public TestBpfMap() {} 47 48 // TODO: Remove this constructor TestBpfMap(final Class<K> key, final Class<V> value)49 public TestBpfMap(final Class<K> key, final Class<V> value) { 50 } 51 52 @Override forEach(ThrowingBiConsumer<K, V> action)53 public void forEach(ThrowingBiConsumer<K, V> action) throws ErrnoException { 54 // TODO: consider using mocked #getFirstKey and #getNextKey to iterate. It helps to 55 // implement the entry deletion in the iteration if required. 56 for (Map.Entry<K, V> entry : mMap.entrySet()) { 57 action.accept(entry.getKey(), entry.getValue()); 58 } 59 } 60 61 @Override updateEntry(K key, V value)62 public void updateEntry(K key, V value) throws ErrnoException { 63 mMap.put(key, value); 64 } 65 66 @Override insertEntry(K key, V value)67 public void insertEntry(K key, V value) throws ErrnoException, 68 IllegalArgumentException { 69 // The entry is created if and only if it doesn't exist. See BpfMap#insertEntry. 70 if (mMap.get(key) != null) { 71 throw new IllegalArgumentException(key + " already exist"); 72 } 73 mMap.put(key, value); 74 } 75 76 @Override replaceEntry(K key, V value)77 public void replaceEntry(K key, V value) throws ErrnoException, NoSuchElementException { 78 if (!mMap.containsKey(key)) throw new NoSuchElementException(); 79 mMap.put(key, value); 80 } 81 82 @Override insertOrReplaceEntry(K key, V value)83 public boolean insertOrReplaceEntry(K key, V value) throws ErrnoException { 84 // Returns true if inserted, false if replaced. 85 boolean ret = !mMap.containsKey(key); 86 mMap.put(key, value); 87 return ret; 88 } 89 90 @Override deleteEntry(Struct key)91 public boolean deleteEntry(Struct key) throws ErrnoException { 92 return mMap.remove(key) != null; 93 } 94 95 @Override isEmpty()96 public boolean isEmpty() throws ErrnoException { 97 return mMap.isEmpty(); 98 } 99 100 @Override getNextKey(@onNull K key)101 public K getNextKey(@NonNull K key) { 102 // Expensive, but since this is only for tests... 103 Iterator<K> it = mMap.keySet().iterator(); 104 while (it.hasNext()) { 105 if (Objects.equals(it.next(), key)) { 106 return it.hasNext() ? it.next() : null; 107 } 108 } 109 return null; 110 } 111 112 @Override getFirstKey()113 public K getFirstKey() { 114 for (K key : mMap.keySet()) { 115 return key; 116 } 117 return null; 118 } 119 120 @Override containsKey(@onNull K key)121 public boolean containsKey(@NonNull K key) throws ErrnoException { 122 return mMap.containsKey(key); 123 } 124 125 @Override getValue(@onNull K key)126 public V getValue(@NonNull K key) throws ErrnoException { 127 // Return value for a given key. Otherwise, return null without an error ENOENT. 128 // BpfMap#getValue treats that the entry is not found as no error. 129 return mMap.get(key); 130 } 131 132 @Override clear()133 public void clear() throws ErrnoException { 134 // TODO: consider using mocked #getFirstKey and #deleteEntry to implement. 135 mMap.clear(); 136 } 137 138 @Override close()139 public void close() throws IOException { 140 } 141 } 142