All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.evosuite.runtime.vnet.VirtualNetwork Maven / Gradle / Ivy

There is a newer version: 1.0.6
Show newest version
/**
 * Copyright (C) 2010-2016 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * This file is part of EvoSuite.
 *
 * EvoSuite 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, either version 3.0 of the License, or
 * (at your option) any later version.
 *
 * EvoSuite 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 Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with EvoSuite. If not, see .
 */
package org.evosuite.runtime.vnet;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import org.evosuite.runtime.mock.java.net.MockInetAddress;
import org.evosuite.runtime.mock.java.net.MockURL;


/**
 * Singleton class used to simulate a virtual network.
 * This is useful to test classes that use UDP/TCP connections
 * 
 * @author arcuri
 *
 */
public class VirtualNetwork {

    /**
     * Specify a network protocol
     */
	public enum ConnectionType {UDP,TCP};


	/**
	 * Singleton instance
	 */
	private static final VirtualNetwork instance = new VirtualNetwork();

	/**
	 * When we simulate a remote incoming connection, we still need a remote port.
	 * Note: in theory we could have the same port if we simulate several different
	 * remote hosts. But, for unit testing purposes, it is likely an unnecessary
	 * overhead/complication
	 */
	private static final int START_OF_REMOTE_EPHEMERAL_PORTS = 40000; 

	/**
	 * Set of listening ports locally opened by the SUTs.
	 * Eg, when the SUTs work as a server, we keep track
	 * of which local ports/interfaces they listen to.
	 */
	private final Set localListeningPorts;

	/**
	 * Set of addresses/ports the SUT tried to contact.
	 */
	private final Set remoteContactedPorts;

    /**
     * key -> address of a remote server
     * 

* value -> a queue of instances of remote servers for the given address. * Note: we need a queue as a server listening on a port could handle several * connections (eg with thread-pool), and each one needs its own object instance. */ private final Map> remoteCurrentServers; /** * Buffer of incoming connections. * *

* Key -> local address/port *

* Value -> queue of foreign addresses/ports waiting to connect to the given local address (key) */ private final Map> incomingConnections; /** * Keep track of all TCP connections opened during the tests. * This is for example useful to check what data the SUT sent. */ private final Set openedTcpConnections; /** * Current remote port number that can be opened */ private final AtomicInteger remotePortIndex; /** * Keeping track of all sent UDP messages is likely not a viable option. * So, for each remote host, we can keep track of how many UDP were sent to it. * This can be used to create concise assertions. * Note: for unit testing purposes, it does not really matter if the remote host is listening, * as UDP is stateless. */ private final Map sentUdpPackets; /** * key -> local address/port for SUT *

* value -> queue of incoming UDP packets */ private final Map> udpPacketsToSUT; /** * Define what interfaces are available: * eg, a loopback one and a wifi */ private final List networkInterfaces; /** * Key -> resolved URL (ie based on DNS) of the remote file * Value -> the remote file we ll allow the tests to read from * *

* This data structure represents remote files that are on a different host, and that could be accessed * for example by http/s using an URL object. * *

* For simplicity, we focus on text files (eg webpages), as those are the most common example. * *

* Note: ideally we should have a full mock of remote servers. For example, accessing a http URL * should be equivalent to open a TCP socket and send a GET command manually. However, as we * do unit testing, this level of realism seems unnecessary (and anyway far too complicated to * implement at the moment). */ private final Map remoteFiles; /** * Keep track of what remote URL the SUT tried to access/read from */ private final Set remoteAccessedFiles; private DNS dns; /** * private, singleton constructor */ private VirtualNetwork(){ localListeningPorts = new CopyOnWriteArraySet<>(); incomingConnections = new ConcurrentHashMap<>(); openedTcpConnections = new CopyOnWriteArraySet<>(); remotePortIndex = new AtomicInteger(START_OF_REMOTE_EPHEMERAL_PORTS); remoteContactedPorts = new CopyOnWriteArraySet<>(); remoteCurrentServers = new ConcurrentHashMap<>(); networkInterfaces = new CopyOnWriteArrayList<>(); remoteFiles = new ConcurrentHashMap<>(); remoteAccessedFiles = new CopyOnWriteArraySet<>(); sentUdpPackets = new ConcurrentHashMap<>(); udpPacketsToSUT = new ConcurrentHashMap<>(); dns = new DNS(); } public static VirtualNetwork getInstance(){ return instance; } //------------------------------------------ public void init(){ reset(); //just to be sure initNetworkInterfaces(); MockURL.initStaticState(); } public void reset(){ dns = new DNS(); incomingConnections.clear(); remotePortIndex.set(START_OF_REMOTE_EPHEMERAL_PORTS); remoteCurrentServers.clear(); networkInterfaces.clear(); remoteFiles.clear(); udpPacketsToSUT.clear(); sentUdpPackets.clear(); localListeningPorts.clear(); openedTcpConnections.clear(); remoteContactedPorts.clear(); remoteAccessedFiles.clear(); } // ------- observers ---------------------- public Set getViewOfRemoteAccessedFiles(){ return Collections.unmodifiableSet(remoteAccessedFiles); } public Set getViewOfOpenedTcpConnections(){ return Collections.unmodifiableSet(openedTcpConnections); } public Set getViewOfLocalListeningPorts(){ return Collections.unmodifiableSet(localListeningPorts); } public Set getViewOfRemoteContactedPorts() {return Collections.unmodifiableSet(remoteContactedPorts);} public Map getCopyOfSentUDP(){ //as AtomicInteger is modifiable, we cannot return a view. we need a copy Map map = new LinkedHashMap<>(); for(EndPointInfo info : sentUdpPackets.keySet()){ map.put(info,sentUdpPackets.get(info).get()); } return map; } /** * Get a copy of all available interfaces * @return */ public List getAllNetworkInterfaceStates(){ return new ArrayList<>(networkInterfaces); } //------------------------------------------ /** * Create a new remote file that can be accessed by the given URL * @param url * @param content * @return {@code false} if URL is malformed, if the protocol is not a remote one (eg "file"), or * if the file was already created */ public boolean addRemoteTextFile(String url, String content){ URL mockURL; try { /* be sure to use the mocked URL, in case we have DNS resolution */ mockURL = MockURL.URL(url); } catch (MalformedURLException e) { return false; } if(mockURL.getProtocol().toLowerCase().equals("file")){ return false; // those are handled in VFS } String key = url.toString(); if(remoteFiles.containsKey(key)){ return false; } RemoteFile rf = new RemoteFile(key,content); remoteFiles.put(key,rf); return true; } /** * Represent the fact that a UDP was sent to a remote host * * @param packet */ public void sentPacketBySUT(DatagramPacket packet){ InetAddress addr = packet.getAddress(); int port = packet.getPort(); EndPointInfo info = new EndPointInfo(addr.getHostAddress(),port,ConnectionType.UDP); remoteContactedPorts.add(info); synchronized(sentUdpPackets){ AtomicInteger counter = sentUdpPackets.get(info); if(counter == null){ counter = new AtomicInteger(0); sentUdpPackets.put(info,counter); } counter.incrementAndGet(); } } /** * * @param sutAddress * @param sutPort * @return {@code null} if there is no buffered incoming packet for the given SUT address */ public DatagramPacket pullUdpPacket(String sutAddress, int sutPort){ EndPointInfo sut = new EndPointInfo(sutAddress,sutPort,ConnectionType.UDP); Queue queue = udpPacketsToSUT.get(sut); if(queue == null || queue.isEmpty()){ return null; } DatagramPacket p = queue.poll(); return p; } public void sendPacketToSUT(byte[] data, InetAddress remoteAddress, int remotePort, String sutAddress, int sutPort){ DatagramPacket packet = new DatagramPacket(data.clone(),data.length,remoteAddress, remotePort); EndPointInfo sut = new EndPointInfo(sutAddress,sutPort,ConnectionType.UDP); synchronized(udpPacketsToSUT){ Queue queue = udpPacketsToSUT.get(sut); if(queue == null){ queue = new ConcurrentLinkedQueue<>(); udpPacketsToSUT.put(sut,queue); } queue.add(packet); } } /** * If it is present on the VNET, return a remote file handler to read such file pointed by the URL. * * @param url * @return {@code null} if there is no such file */ public RemoteFile getFile(URL url){ String s = url.toString(); if(!remoteAccessedFiles.contains(s)){ remoteAccessedFiles.add(s); } return remoteFiles.get(s); } /** * Create new port to open on remote host * * @return a integer representing a port number on remote host */ public int getNewRemoteEphemeralPort(){ return remotePortIndex.getAndIncrement(); } /** * Create new port on local host * * @return a integer representing a port number on local host */ public int getNewLocalEphemeralPort(){ return remotePortIndex.getAndIncrement(); //Note: could use a new variable, but doesn't really matter } /** * * @param name * @return {@code null} if the interface does not exist */ public NetworkInterfaceState getNetworkInterfaceState(String name){ for(NetworkInterfaceState ni : networkInterfaces){ if(ni.getNetworkInterface().getName().equals(name)){ return ni; } } return null; } /** * Use mocked DNS to resolve host * @param host * @return */ public String dnsResolve(String host){ return dns.resolve(host); } /** * Simulate an incoming connection. The connection is put on a buffer till * the SUT open a listening port * * @param originAddr * @param originPort * @param destAddr * @param destPort */ public synchronized NativeTcp registerIncomingTcpConnection( String originAddr, int originPort, String destAddr, int destPort){ EndPointInfo origin = new EndPointInfo(originAddr,originPort,ConnectionType.TCP); EndPointInfo dest = new EndPointInfo(destAddr,destPort,ConnectionType.TCP); Queue queue = incomingConnections.get(dest); if(queue == null){ queue = new ConcurrentLinkedQueue<>(); incomingConnections.put(dest, queue); } NativeTcp connection = new NativeTcp(dest,origin); queue.add(connection); return connection; } /** * Return a TCP connection for the given local address if there is any inbound remote connection to it * * @param localAddress * @param localPort * @return {@code null} if the test case has not set up it an incoming TCP connection */ public synchronized NativeTcp pullTcpConnection(String localAddress, int localPort){ EndPointInfo local = new EndPointInfo(localAddress,localPort,ConnectionType.TCP); Queue queue = incomingConnections.get(local); if(queue == null || queue.isEmpty()){ return null; } NativeTcp connection = queue.poll(); openedTcpConnections.add(connection); return connection; } /** * * @param addr * @return {@code false} if it was not possible to open the listening port */ public synchronized boolean openTcpServer(String addr, int port) throws IllegalArgumentException{ return openServer(addr,port,ConnectionType.TCP); } /** * * @param addr * @return {@code false} if it was not possible to open the listening port */ public synchronized boolean openUdpServer(String addr, int port) throws IllegalArgumentException{ return openServer(addr,port,ConnectionType.UDP); } private boolean openServer(String addr, int port, ConnectionType type) throws IllegalArgumentException{ if(port == 0){ throw new IllegalArgumentException("Cannot try to bind to wildcard port 0"); } EndPointInfo info = new EndPointInfo(addr,port,type); if(localListeningPorts.contains(info)){ /* there is already an existing opened port. Note: it is possible to have a UDP and TCP on same port */ return false; } if(! isValidLocalServer(info)){ return false; } localListeningPorts.add(info); return true; } /** * Register a remote server that can reply to SUT's connection requests */ public synchronized void addRemoteTcpServer(RemoteTcpServer server){ Queue queue = remoteCurrentServers.get(server.getAddress()); if(queue==null){ queue = new ConcurrentLinkedQueue<>(); remoteCurrentServers.put(server.getAddress(), queue); } queue.add(server); } /** * Create a mocked TCP connection from the SUT to a remote host * * @param localOrigin * @param remoteTarget * @return * @throws IllegalArgumentException * @throws IOException */ public synchronized NativeTcp connectToRemoteAddress(EndPointInfo localOrigin, EndPointInfo remoteTarget) throws IllegalArgumentException, IOException{ if(localOrigin==null || remoteTarget==null){ throw new IllegalArgumentException("Null input"); } if(!localOrigin.getType().equals(ConnectionType.TCP) || !remoteTarget.getType().equals(ConnectionType.TCP)){ throw new IllegalArgumentException("Non-TCP connections"); } if(!isValidLocalServer(localOrigin)){ throw new IllegalArgumentException("Invalid local address: "+localOrigin); } remoteContactedPorts.add(remoteTarget); Queue queue = remoteCurrentServers.get(remoteTarget); if(queue==null || queue.isEmpty()){ throw new IOException("Remote address/port is not opened: "+remoteTarget); } RemoteTcpServer server = queue.poll(); NativeTcp connection = server.connect(localOrigin); return connection; } //------------------------------------------ private boolean isValidLocalServer(EndPointInfo info){ return true; //TODO } private void initNetworkInterfaces() { try{ NetworkInterfaceState loopback = new NetworkInterfaceState( "Evo_lo0", 1, null, 16384, true, MockInetAddress.getByName("127.0.0.1")); networkInterfaces.add(loopback); NetworkInterfaceState wifi = new NetworkInterfaceState( "Evo_en0", 5, new byte[]{0, 42, 0, 42, 0, 42}, 1500, false, MockInetAddress.getByName("192.168.1.42")); networkInterfaces.add(wifi); } catch(Exception e){ //this should never happen throw new RuntimeException("EvoSuite error: "+e.getMessage()); } } //------------------------------------------ }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy