/* * Copyright 2009 Amit Levy, Jeff Prouty, Rylan Hawkins * * 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 edu.washington.cs.cse490h.donut.server; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.List; import org.apache.thrift.TException; import org.easymock.EasyMock; import org.junit.After; import org.junit.Before; import org.junit.Test; import edu.washington.cs.cse490h.donut.Constants; import edu.washington.cs.cse490h.donut.business.KeyId; import edu.washington.cs.cse490h.donut.business.Node; import edu.washington.cs.cse490h.donut.business.TNode; import edu.washington.cs.cse490h.donut.service.KeyLocator; import edu.washington.cs.cse490h.donut.service.LocatorClientFactory; import edu.washington.cs.cse490h.donut.service.NodeNotFoundException; import edu.washington.cs.cse490h.donut.service.RetryFailedException; import edu.washington.cs.cse490h.donut.service.KeyLocator.Iface; /** * @author alevy */ public class DonutClientTest { private LocatorClientFactory clientLocatorMock; private Iface keyLocator; @Before public void setUp() { clientLocatorMock = EasyMock.createMock(LocatorClientFactory.class); keyLocator = EasyMock.createMock(KeyLocator.Iface.class); } @After public void tearDown() { verify(clientLocatorMock, keyLocator); } @Test public void testJoinTNode() throws Exception { Node testNode = new Node("testNode0", 8080, new KeyId(100)); TNode knownNode = new TNode("testNode1", 8080, new KeyId(200)); TNode successorNode = new TNode("successor", 8080, new KeyId(300)); DonutClient donutClient = new DonutClient(testNode, clientLocatorMock); expect(clientLocatorMock.get(knownNode)).andReturn(keyLocator); clientLocatorMock.release(knownNode); expect(keyLocator.findSuccessor(testNode.getNodeId())).andReturn(successorNode); replay(clientLocatorMock, keyLocator); donutClient.join(knownNode); assertSame(testNode.getSuccessor(), successorNode); } @Test public void testPing_True() throws Exception { DonutClient donutClient = new DonutClient(null, clientLocatorMock); TNode node = new TNode(); expect(clientLocatorMock.get(node)).andReturn(keyLocator); clientLocatorMock.release(node); keyLocator.ping(); replay(clientLocatorMock); replay(keyLocator); assertTrue(donutClient.ping(node)); } @Test public void testPing_False() throws Exception { DonutClient donutClient = new DonutClient(null, clientLocatorMock); TNode node = new TNode(); expect(clientLocatorMock.get(node)).andReturn(keyLocator); clientLocatorMock.release(node); keyLocator.ping(); EasyMock.expectLastCall().andThrow(new TException()); replay(clientLocatorMock, keyLocator); assertFalse(donutClient.ping(node)); } @Test public void testCheckPredecessor_StillUp() throws Exception { Node node = new Node(null, 0, null); TNode predecessor = new TNode("pred", 8080, null); node.setPredecessor(predecessor); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(predecessor)).andReturn(keyLocator); clientLocatorMock.release(predecessor); keyLocator.ping(); replay(clientLocatorMock, keyLocator); donutClient.checkPredecessor(); assertEquals(predecessor, node.getPredecessor()); } @Test public void testCheckPredecessor_Down() throws Exception { Node node = new Node(null, 0, null); TNode predecessor = new TNode("pred", 8080, null); node.setPredecessor(predecessor); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(predecessor)).andReturn(keyLocator); clientLocatorMock.release(predecessor); keyLocator.ping(); expectLastCall().andThrow(new TException()); replay(clientLocatorMock, keyLocator); donutClient.checkPredecessor(); assertNull(node.getPredecessor()); } @Test public void testStabilize_Alone() throws Exception { Node node = new Node(null, 0, null); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(node.getTNode())).andReturn(keyLocator); clientLocatorMock.release(node.getTNode()); keyLocator.getPredecessor(); expectLastCall().andThrow(new NodeNotFoundException()); List<TNode> list = new ArrayList<TNode>(2); list.add(node.getTNode()); expect(keyLocator.notify(node.getTNode())).andReturn(list); replay(clientLocatorMock, keyLocator); donutClient.stabilize(); assertSame(node.getTNode(), node.getSuccessor()); assertSame(node.getSuccessorList().get(1), node.getTNode()); } @Test public void testStabilize_HasPredecessor() throws Exception { Node node = new Node("testNode0", 8080, new KeyId(100)); TNode predecessor = new TNode("testNode1", 0, new KeyId(200)); node.setPredecessor(predecessor); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(node.getTNode())).andReturn(keyLocator); clientLocatorMock.release(node.getTNode()); expect(clientLocatorMock.get(predecessor)).andReturn(keyLocator); clientLocatorMock.release(predecessor); expect(keyLocator.getPredecessor()).andReturn(predecessor); List<TNode> list = new ArrayList<TNode>(Constants.SUCCESSOR_LIST_SIZE); list.add(node.getTNode()); list.add(predecessor); expect(keyLocator.notify(node.getTNode())).andReturn(list); replay(clientLocatorMock, keyLocator); donutClient.stabilize(); assertSame(node.getPredecessor(), node.getSuccessor()); assertSame(node.getSuccessorList().get(1), node.getTNode()); assertSame(node.getSuccessorList().get(2), predecessor); } @Test public void testStabilize_JustNotifyBecauseNoPredecessor() throws Exception { Node node = new Node("self", 0, new KeyId(100)); TNode successor = new TNode("other", 0, new KeyId(200)); node.setSuccessor(successor); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(successor)).andReturn(keyLocator); clientLocatorMock.release(successor); expect(keyLocator.getPredecessor()).andThrow(new NodeNotFoundException()); List<TNode> list = new ArrayList<TNode>(Constants.SUCCESSOR_LIST_SIZE); list.add(node.getTNode()); list.add(successor); expect(keyLocator.notify(node.getTNode())).andReturn(list); replay(clientLocatorMock, keyLocator); donutClient.stabilize(); assertSame(successor, node.getSuccessor()); assertSame(node.getSuccessorList().get(1), node.getTNode()); assertSame(node.getSuccessorList().get(2), successor); } @Test public void testStabilize_JustNotifyBecauseNotBetween() throws Exception { Node node = new Node("self", 0, new KeyId(0)); TNode between = new TNode("between", 0, new KeyId(300)); TNode successor = new TNode("other", 0, new KeyId(200)); node.setSuccessor(successor); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(successor)).andReturn(keyLocator); clientLocatorMock.release(successor); expect(keyLocator.getPredecessor()).andReturn(between); List<TNode> list = new ArrayList<TNode>(Constants.SUCCESSOR_LIST_SIZE); expect(keyLocator.notify(node.getTNode())).andReturn(list); replay(clientLocatorMock, keyLocator); donutClient.stabilize(); assertSame(successor, node.getSuccessor()); } @Test public void testStabilize_NotifyAndSetSuccessor() throws Exception { Node node = new Node("self", 0, new KeyId(0)); TNode between = new TNode("between", 0, new KeyId(100)); TNode successor = new TNode("other", 0, new KeyId(200)); node.setSuccessor(successor); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(successor)).andReturn(keyLocator); clientLocatorMock.release(successor); expect(clientLocatorMock.get(between)).andReturn(keyLocator); clientLocatorMock.release(between); expect(keyLocator.getPredecessor()).andReturn(between); List<TNode> list = new ArrayList<TNode>(Constants.SUCCESSOR_LIST_SIZE); list.add(successor); list.add(node.getTNode()); expect(keyLocator.notify(node.getTNode())).andReturn(list); replay(clientLocatorMock, keyLocator); donutClient.stabilize(); assertSame(between, node.getSuccessor()); assertSame(node.getSuccessorList().get(2), node.getTNode()); assertSame(node.getSuccessorList().get(1), successor); } @Test public void testStabilize_NodeThrowsRetryFailedException() throws Exception { Node node = new Node("self", 0, new KeyId(0)); TNode successor = new TNode("other", 0, new KeyId(200)); node.setSuccessor(successor); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(successor)).andThrow(new RetryFailedException()); clientLocatorMock.release(successor); replay(clientLocatorMock, keyLocator); donutClient.stabilize(); assertSame(node.getTNode(), node.getSuccessor()); } @Test public void testStabilize_NodeThrowsTException() throws Exception { Node node = new Node("self", 0, new KeyId(0)); TNode successor = new TNode("other", 0, new KeyId(200)); node.setSuccessor(successor); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(successor)).andReturn(keyLocator); clientLocatorMock.release(successor); expect(keyLocator.getPredecessor()).andThrow(new TException()); replay(clientLocatorMock, keyLocator); donutClient.stabilize(); assertSame(node.getTNode(), node.getSuccessor()); } @Test public void testFixFingers() throws Exception { Node node = new Node(null, 0, new KeyId(0)); TNode finger1 = new TNode(); node.setSuccessor(new TNode("hello", 0, null)); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(node.getTNode())).andReturn(keyLocator); clientLocatorMock.release(node.getTNode()); expect(keyLocator.findSuccessor(new KeyId(1))).andReturn(finger1); replay(clientLocatorMock, keyLocator); donutClient.fixFinger(0); assertSame(finger1, node.getFinger(0)); } @Test public void testFixFingers_Far() throws Exception { Node node = new Node(null, 0, new KeyId(0)); TNode finger10 = new TNode(); node.setSuccessor(new TNode("hello", 0, null)); DonutClient donutClient = new DonutClient(node, clientLocatorMock); expect(clientLocatorMock.get(node.getTNode())).andReturn(keyLocator); clientLocatorMock.release(node.getTNode()); expect(keyLocator.findSuccessor(new KeyId(1024))).andReturn(finger10); replay(clientLocatorMock, keyLocator); donutClient.fixFinger(10); assertSame(finger10, node.getFinger(10)); } @Test public void testUpdateSuccessorListOfSize3() throws Exception { Node node = new Node("self", 0, new KeyId(0)); TNode successor0 = new TNode("other1", 0, new KeyId(100)); List<TNode> list = new ArrayList<TNode>(Constants.SUCCESSOR_LIST_SIZE); for (int i = 0; i < Constants.SUCCESSOR_LIST_SIZE; i++) { list.add(new TNode("other" + i, 0, new KeyId(200 * i))); } node.setSuccessor(successor0); replay(clientLocatorMock, keyLocator); node.updateSuccessorList(list); List<TNode> list2 = node.getSuccessorList(); // This is set to 3 instead of the constant so that if you change the constant you can // make this test more concise with more successors assertSame(list2.size(), Constants.SUCCESSOR_LIST_SIZE); assertSame(successor0, list2.get(0)); for (int i = 0; i < Constants.SUCCESSOR_LIST_SIZE - 1; i++) { assertSame(list.get(i), list2.get(i + 1)); } } @Test public void testUpdateSuccessorListOfSize1() throws Exception { Node node = new Node("self", 0, new KeyId(0)); TNode successor0 = new TNode("other1", 0, new KeyId(100)); node.setSuccessor(successor0); replay(clientLocatorMock, keyLocator); List<TNode> list0 = new ArrayList<TNode>(Constants.SUCCESSOR_LIST_SIZE); for (int i = 0; i < Constants.SUCCESSOR_LIST_SIZE; i++) { list0.add(new TNode("other" + i, 0, new KeyId(200 * i))); } node.updateSuccessorList(list0); List<TNode> list = new ArrayList<TNode>(Constants.SUCCESSOR_LIST_SIZE); TNode successor1 = new TNode("other1", 0, new KeyId(100)); list.add(successor1); node.updateSuccessorList(list); assertSame(node.getSuccessorList().size(), list.size() + 1); assertSame(successor0, node.getSuccessorList().get(0)); assertSame(successor1, node.getSuccessorList().get(1)); } }