Showing
9 changed files
with
1947 additions
and
0 deletions
... | @@ -42,6 +42,16 @@ | ... | @@ -42,6 +42,16 @@ |
42 | <artifactId>onlab-thirdparty</artifactId> | 42 | <artifactId>onlab-thirdparty</artifactId> |
43 | </dependency> | 43 | </dependency> |
44 | 44 | ||
45 | + <dependency> | ||
46 | + <groupId>org.onlab.onos</groupId> | ||
47 | + <artifactId>onlab-misc</artifactId> | ||
48 | + </dependency> | ||
49 | + | ||
50 | + <dependency> | ||
51 | + <groupId>org.easymock</groupId> | ||
52 | + <artifactId>easymock</artifactId> | ||
53 | + <scope>test</scope> | ||
54 | + </dependency> | ||
45 | </dependencies> | 55 | </dependencies> |
46 | 56 | ||
47 | </project> | 57 | </project> | ... | ... |
1 | +package org.onlab.onos.sdnip.bgp; | ||
2 | + | ||
3 | +import static org.hamcrest.Matchers.is; | ||
4 | +import static org.hamcrest.Matchers.not; | ||
5 | +import static org.junit.Assert.assertThat; | ||
6 | + | ||
7 | +import java.util.ArrayList; | ||
8 | + | ||
9 | +import org.junit.Test; | ||
10 | + | ||
11 | +/** | ||
12 | + * Unit tests for the BgpRouteEntry.AsPath class. | ||
13 | + */ | ||
14 | +public class AsPathTest { | ||
15 | + /** | ||
16 | + * Generates an AS Path. | ||
17 | + * | ||
18 | + * @return a generated AS Path | ||
19 | + */ | ||
20 | + private BgpRouteEntry.AsPath generateAsPath() { | ||
21 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
22 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
23 | + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); | ||
24 | + segmentAsNumbers1.add((long) 1); | ||
25 | + segmentAsNumbers1.add((long) 2); | ||
26 | + segmentAsNumbers1.add((long) 3); | ||
27 | + BgpRouteEntry.PathSegment pathSegment1 = | ||
28 | + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); | ||
29 | + pathSegments.add(pathSegment1); | ||
30 | + // | ||
31 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
32 | + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); | ||
33 | + segmentAsNumbers2.add((long) 4); | ||
34 | + segmentAsNumbers2.add((long) 5); | ||
35 | + segmentAsNumbers2.add((long) 6); | ||
36 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
37 | + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); | ||
38 | + pathSegments.add(pathSegment2); | ||
39 | + // | ||
40 | + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
41 | + | ||
42 | + return asPath; | ||
43 | + } | ||
44 | + | ||
45 | + /** | ||
46 | + * Tests valid class constructor. | ||
47 | + */ | ||
48 | + @Test | ||
49 | + public void testConstructor() { | ||
50 | + BgpRouteEntry.AsPath asPath = generateAsPath(); | ||
51 | + | ||
52 | + String expectedString = | ||
53 | + "AsPath{pathSegments=" + | ||
54 | + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " + | ||
55 | + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}"; | ||
56 | + assertThat(asPath.toString(), is(expectedString)); | ||
57 | + } | ||
58 | + | ||
59 | + /** | ||
60 | + * Tests invalid class constructor for null Path Segments. | ||
61 | + */ | ||
62 | + @Test(expected = NullPointerException.class) | ||
63 | + public void testInvalidConstructorNullPathSegments() { | ||
64 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = null; | ||
65 | + new BgpRouteEntry.AsPath(pathSegments); | ||
66 | + } | ||
67 | + | ||
68 | + /** | ||
69 | + * Tests getting the fields of an AS Path. | ||
70 | + */ | ||
71 | + @Test | ||
72 | + public void testGetFields() { | ||
73 | + // Create the fields to compare against | ||
74 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
75 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
76 | + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); | ||
77 | + segmentAsNumbers1.add((long) 1); | ||
78 | + segmentAsNumbers1.add((long) 2); | ||
79 | + segmentAsNumbers1.add((long) 3); | ||
80 | + BgpRouteEntry.PathSegment pathSegment1 = | ||
81 | + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); | ||
82 | + pathSegments.add(pathSegment1); | ||
83 | + // | ||
84 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
85 | + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); | ||
86 | + segmentAsNumbers2.add((long) 4); | ||
87 | + segmentAsNumbers2.add((long) 5); | ||
88 | + segmentAsNumbers2.add((long) 6); | ||
89 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
90 | + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); | ||
91 | + pathSegments.add(pathSegment2); | ||
92 | + | ||
93 | + // Generate the entry to test | ||
94 | + BgpRouteEntry.AsPath asPath = generateAsPath(); | ||
95 | + | ||
96 | + assertThat(asPath.getPathSegments(), is(pathSegments)); | ||
97 | + } | ||
98 | + | ||
99 | + /** | ||
100 | + * Tests getting the AS Path Length. | ||
101 | + */ | ||
102 | + @Test | ||
103 | + public void testGetAsPathLength() { | ||
104 | + BgpRouteEntry.AsPath asPath = generateAsPath(); | ||
105 | + assertThat(asPath.getAsPathLength(), is(4)); | ||
106 | + | ||
107 | + // Create an empty AS Path | ||
108 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
109 | + asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
110 | + assertThat(asPath.getAsPathLength(), is(0)); | ||
111 | + } | ||
112 | + | ||
113 | + /** | ||
114 | + * Tests equality of {@link BgpRouteEntry.AsPath}. | ||
115 | + */ | ||
116 | + @Test | ||
117 | + public void testEquality() { | ||
118 | + BgpRouteEntry.AsPath asPath1 = generateAsPath(); | ||
119 | + BgpRouteEntry.AsPath asPath2 = generateAsPath(); | ||
120 | + | ||
121 | + assertThat(asPath1, is(asPath2)); | ||
122 | + } | ||
123 | + | ||
124 | + /** | ||
125 | + * Tests non-equality of {@link BgpRouteEntry.AsPath}. | ||
126 | + */ | ||
127 | + @Test | ||
128 | + public void testNonEquality() { | ||
129 | + BgpRouteEntry.AsPath asPath1 = generateAsPath(); | ||
130 | + | ||
131 | + // Setup AS Path 2 | ||
132 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
133 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
134 | + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); | ||
135 | + segmentAsNumbers1.add((long) 1); | ||
136 | + segmentAsNumbers1.add((long) 2); | ||
137 | + segmentAsNumbers1.add((long) 3); | ||
138 | + BgpRouteEntry.PathSegment pathSegment1 = | ||
139 | + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); | ||
140 | + pathSegments.add(pathSegment1); | ||
141 | + // | ||
142 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
143 | + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); | ||
144 | + segmentAsNumbers2.add((long) 4); | ||
145 | + segmentAsNumbers2.add((long) 55); // Different | ||
146 | + segmentAsNumbers2.add((long) 6); | ||
147 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
148 | + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); | ||
149 | + pathSegments.add(pathSegment2); | ||
150 | + // | ||
151 | + BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments); | ||
152 | + | ||
153 | + assertThat(asPath1, is(not(asPath2))); | ||
154 | + } | ||
155 | + | ||
156 | + /** | ||
157 | + * Tests object string representation. | ||
158 | + */ | ||
159 | + @Test | ||
160 | + public void testToString() { | ||
161 | + BgpRouteEntry.AsPath asPath = generateAsPath(); | ||
162 | + | ||
163 | + String expectedString = | ||
164 | + "AsPath{pathSegments=" + | ||
165 | + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " + | ||
166 | + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}"; | ||
167 | + assertThat(asPath.toString(), is(expectedString)); | ||
168 | + } | ||
169 | +} |
1 | +package org.onlab.onos.sdnip.bgp; | ||
2 | + | ||
3 | +import static org.easymock.EasyMock.createMock; | ||
4 | +import static org.easymock.EasyMock.expect; | ||
5 | +import static org.easymock.EasyMock.replay; | ||
6 | +import static org.hamcrest.Matchers.is; | ||
7 | +import static org.hamcrest.Matchers.not; | ||
8 | +import static org.junit.Assert.assertThat; | ||
9 | + | ||
10 | +import java.util.ArrayList; | ||
11 | + | ||
12 | +import org.junit.Before; | ||
13 | +import org.junit.Test; | ||
14 | +import org.onlab.packet.IpAddress; | ||
15 | +import org.onlab.packet.IpPrefix; | ||
16 | + | ||
17 | +/** | ||
18 | + * Unit tests for the BgpRouteEntry class. | ||
19 | + */ | ||
20 | +public class BgpRouteEntryTest { | ||
21 | + private BgpSession bgpSession; | ||
22 | + private static final IpAddress BGP_SESSION_BGP_ID = | ||
23 | + IpAddress.valueOf("10.0.0.1"); | ||
24 | + private static final IpAddress BGP_SESSION_IP_ADDRESS = | ||
25 | + IpAddress.valueOf("20.0.0.1"); | ||
26 | + | ||
27 | + private BgpSession bgpSession2; | ||
28 | + private static final IpAddress BGP_SESSION_BGP_ID2 = | ||
29 | + IpAddress.valueOf("10.0.0.2"); | ||
30 | + private static final IpAddress BGP_SESSION_IP_ADDRESS2 = | ||
31 | + IpAddress.valueOf("20.0.0.1"); | ||
32 | + | ||
33 | + private BgpSession bgpSession3; | ||
34 | + private static final IpAddress BGP_SESSION_BGP_ID3 = | ||
35 | + IpAddress.valueOf("10.0.0.1"); | ||
36 | + private static final IpAddress BGP_SESSION_IP_ADDRESS3 = | ||
37 | + IpAddress.valueOf("20.0.0.2"); | ||
38 | + | ||
39 | + @Before | ||
40 | + public void setUp() throws Exception { | ||
41 | + // Mock objects for testing | ||
42 | + bgpSession = createMock(BgpSession.class); | ||
43 | + bgpSession2 = createMock(BgpSession.class); | ||
44 | + bgpSession3 = createMock(BgpSession.class); | ||
45 | + | ||
46 | + // Setup the BGP Sessions | ||
47 | + expect(bgpSession.getRemoteBgpId()) | ||
48 | + .andReturn(BGP_SESSION_BGP_ID).anyTimes(); | ||
49 | + expect(bgpSession.getRemoteIp4Address()) | ||
50 | + .andReturn(BGP_SESSION_IP_ADDRESS).anyTimes(); | ||
51 | + // | ||
52 | + expect(bgpSession2.getRemoteBgpId()) | ||
53 | + .andReturn(BGP_SESSION_BGP_ID2).anyTimes(); | ||
54 | + expect(bgpSession2.getRemoteIp4Address()) | ||
55 | + .andReturn(BGP_SESSION_IP_ADDRESS2).anyTimes(); | ||
56 | + // | ||
57 | + expect(bgpSession3.getRemoteBgpId()) | ||
58 | + .andReturn(BGP_SESSION_BGP_ID3).anyTimes(); | ||
59 | + expect(bgpSession3.getRemoteIp4Address()) | ||
60 | + .andReturn(BGP_SESSION_IP_ADDRESS3).anyTimes(); | ||
61 | + | ||
62 | + replay(bgpSession); | ||
63 | + replay(bgpSession2); | ||
64 | + replay(bgpSession3); | ||
65 | + } | ||
66 | + | ||
67 | + /** | ||
68 | + * Generates a BGP Route Entry. | ||
69 | + * | ||
70 | + * @return a generated BGP Route Entry | ||
71 | + */ | ||
72 | + private BgpRouteEntry generateBgpRouteEntry() { | ||
73 | + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24"); | ||
74 | + IpAddress nextHop = IpAddress.valueOf("5.6.7.8"); | ||
75 | + byte origin = BgpConstants.Update.Origin.IGP; | ||
76 | + // Setup the AS Path | ||
77 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
78 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
79 | + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); | ||
80 | + segmentAsNumbers1.add((long) 1); | ||
81 | + segmentAsNumbers1.add((long) 2); | ||
82 | + segmentAsNumbers1.add((long) 3); | ||
83 | + BgpRouteEntry.PathSegment pathSegment1 = | ||
84 | + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); | ||
85 | + pathSegments.add(pathSegment1); | ||
86 | + // | ||
87 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
88 | + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); | ||
89 | + segmentAsNumbers2.add((long) 4); | ||
90 | + segmentAsNumbers2.add((long) 5); | ||
91 | + segmentAsNumbers2.add((long) 6); | ||
92 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
93 | + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); | ||
94 | + pathSegments.add(pathSegment2); | ||
95 | + // | ||
96 | + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
97 | + // | ||
98 | + long localPref = 100; | ||
99 | + long multiExitDisc = 20; | ||
100 | + | ||
101 | + BgpRouteEntry bgpRouteEntry = | ||
102 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath, | ||
103 | + localPref); | ||
104 | + bgpRouteEntry.setMultiExitDisc(multiExitDisc); | ||
105 | + | ||
106 | + return bgpRouteEntry; | ||
107 | + } | ||
108 | + | ||
109 | + /** | ||
110 | + * Tests valid class constructor. | ||
111 | + */ | ||
112 | + @Test | ||
113 | + public void testConstructor() { | ||
114 | + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry(); | ||
115 | + | ||
116 | + String expectedString = | ||
117 | + "BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " + | ||
118 | + "bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" + | ||
119 | + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " + | ||
120 | + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " + | ||
121 | + "localPref=100, multiExitDisc=20}"; | ||
122 | + assertThat(bgpRouteEntry.toString(), is(expectedString)); | ||
123 | + } | ||
124 | + | ||
125 | + /** | ||
126 | + * Tests invalid class constructor for null BGP Session. | ||
127 | + */ | ||
128 | + @Test(expected = NullPointerException.class) | ||
129 | + public void testInvalidConstructorNullBgpSession() { | ||
130 | + BgpSession bgpSessionNull = null; | ||
131 | + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24"); | ||
132 | + IpAddress nextHop = IpAddress.valueOf("5.6.7.8"); | ||
133 | + byte origin = BgpConstants.Update.Origin.IGP; | ||
134 | + // Setup the AS Path | ||
135 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
136 | + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
137 | + // | ||
138 | + long localPref = 100; | ||
139 | + | ||
140 | + new BgpRouteEntry(bgpSessionNull, prefix, nextHop, origin, asPath, | ||
141 | + localPref); | ||
142 | + } | ||
143 | + | ||
144 | + /** | ||
145 | + * Tests invalid class constructor for null AS Path. | ||
146 | + */ | ||
147 | + @Test(expected = NullPointerException.class) | ||
148 | + public void testInvalidConstructorNullAsPath() { | ||
149 | + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24"); | ||
150 | + IpAddress nextHop = IpAddress.valueOf("5.6.7.8"); | ||
151 | + byte origin = BgpConstants.Update.Origin.IGP; | ||
152 | + BgpRouteEntry.AsPath asPath = null; | ||
153 | + long localPref = 100; | ||
154 | + | ||
155 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath, | ||
156 | + localPref); | ||
157 | + } | ||
158 | + | ||
159 | + /** | ||
160 | + * Tests getting the fields of a BGP route entry. | ||
161 | + */ | ||
162 | + @Test | ||
163 | + public void testGetFields() { | ||
164 | + // Create the fields to compare against | ||
165 | + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24"); | ||
166 | + IpAddress nextHop = IpAddress.valueOf("5.6.7.8"); | ||
167 | + byte origin = BgpConstants.Update.Origin.IGP; | ||
168 | + // Setup the AS Path | ||
169 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
170 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
171 | + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); | ||
172 | + segmentAsNumbers1.add((long) 1); | ||
173 | + segmentAsNumbers1.add((long) 2); | ||
174 | + segmentAsNumbers1.add((long) 3); | ||
175 | + BgpRouteEntry.PathSegment pathSegment1 = | ||
176 | + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); | ||
177 | + pathSegments.add(pathSegment1); | ||
178 | + // | ||
179 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
180 | + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); | ||
181 | + segmentAsNumbers2.add((long) 4); | ||
182 | + segmentAsNumbers2.add((long) 5); | ||
183 | + segmentAsNumbers2.add((long) 6); | ||
184 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
185 | + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); | ||
186 | + pathSegments.add(pathSegment2); | ||
187 | + // | ||
188 | + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
189 | + // | ||
190 | + long localPref = 100; | ||
191 | + long multiExitDisc = 20; | ||
192 | + | ||
193 | + // Generate the entry to test | ||
194 | + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry(); | ||
195 | + | ||
196 | + assertThat(bgpRouteEntry.prefix(), is(prefix)); | ||
197 | + assertThat(bgpRouteEntry.nextHop(), is(nextHop)); | ||
198 | + assertThat(bgpRouteEntry.getBgpSession(), is(bgpSession)); | ||
199 | + assertThat(bgpRouteEntry.getOrigin(), is(origin)); | ||
200 | + assertThat(bgpRouteEntry.getAsPath(), is(asPath)); | ||
201 | + assertThat(bgpRouteEntry.getLocalPref(), is(localPref)); | ||
202 | + assertThat(bgpRouteEntry.getMultiExitDisc(), is(multiExitDisc)); | ||
203 | + } | ||
204 | + | ||
205 | + /** | ||
206 | + * Tests whether a BGP route entry is a local route. | ||
207 | + */ | ||
208 | + @Test | ||
209 | + public void testIsLocalRoute() { | ||
210 | + // | ||
211 | + // Test non-local route | ||
212 | + // | ||
213 | + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry(); | ||
214 | + assertThat(bgpRouteEntry.isLocalRoute(), is(false)); | ||
215 | + | ||
216 | + // | ||
217 | + // Test local route with AS Path that begins with AS_SET | ||
218 | + // | ||
219 | + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24"); | ||
220 | + IpAddress nextHop = IpAddress.valueOf("5.6.7.8"); | ||
221 | + byte origin = BgpConstants.Update.Origin.IGP; | ||
222 | + // Setup the AS Path | ||
223 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
224 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
225 | + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); | ||
226 | + segmentAsNumbers1.add((long) 1); | ||
227 | + segmentAsNumbers1.add((long) 2); | ||
228 | + segmentAsNumbers1.add((long) 3); | ||
229 | + BgpRouteEntry.PathSegment pathSegment1 = | ||
230 | + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); | ||
231 | + pathSegments.add(pathSegment1); | ||
232 | + // | ||
233 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
234 | + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); | ||
235 | + segmentAsNumbers2.add((long) 4); | ||
236 | + segmentAsNumbers2.add((long) 5); | ||
237 | + segmentAsNumbers2.add((long) 6); | ||
238 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
239 | + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); | ||
240 | + pathSegments.add(pathSegment2); | ||
241 | + // | ||
242 | + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
243 | + // | ||
244 | + long localPref = 100; | ||
245 | + long multiExitDisc = 20; | ||
246 | + // | ||
247 | + bgpRouteEntry = | ||
248 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath, | ||
249 | + localPref); | ||
250 | + bgpRouteEntry.setMultiExitDisc(multiExitDisc); | ||
251 | + assertThat(bgpRouteEntry.isLocalRoute(), is(true)); | ||
252 | + | ||
253 | + // | ||
254 | + // Test local route with empty AS Path | ||
255 | + // | ||
256 | + pathSegments = new ArrayList<>(); | ||
257 | + asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
258 | + bgpRouteEntry = | ||
259 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath, | ||
260 | + localPref); | ||
261 | + bgpRouteEntry.setMultiExitDisc(multiExitDisc); | ||
262 | + assertThat(bgpRouteEntry.isLocalRoute(), is(true)); | ||
263 | + } | ||
264 | + | ||
265 | + /** | ||
266 | + * Tests getting the BGP Neighbor AS number for a route. | ||
267 | + */ | ||
268 | + @Test | ||
269 | + public void testGetNeighborAs() { | ||
270 | + // | ||
271 | + // Get neighbor AS for non-local route | ||
272 | + // | ||
273 | + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry(); | ||
274 | + assertThat(bgpRouteEntry.getNeighborAs(), is((long) 1)); | ||
275 | + | ||
276 | + // | ||
277 | + // Get neighbor AS for a local route | ||
278 | + // | ||
279 | + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24"); | ||
280 | + IpAddress nextHop = IpAddress.valueOf("5.6.7.8"); | ||
281 | + byte origin = BgpConstants.Update.Origin.IGP; | ||
282 | + // Setup the AS Path | ||
283 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
284 | + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
285 | + // | ||
286 | + long localPref = 100; | ||
287 | + long multiExitDisc = 20; | ||
288 | + // | ||
289 | + bgpRouteEntry = | ||
290 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath, | ||
291 | + localPref); | ||
292 | + bgpRouteEntry.setMultiExitDisc(multiExitDisc); | ||
293 | + assertThat(bgpRouteEntry.getNeighborAs(), is(BgpConstants.BGP_AS_0)); | ||
294 | + } | ||
295 | + | ||
296 | + /** | ||
297 | + * Tests whether a BGP route entry has AS Path loop. | ||
298 | + */ | ||
299 | + @Test | ||
300 | + public void testHasAsPathLoop() { | ||
301 | + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry(); | ||
302 | + | ||
303 | + // Test for loops: test each AS number in the interval [1, 6] | ||
304 | + for (int i = 1; i <= 6; i++) { | ||
305 | + assertThat(bgpRouteEntry.hasAsPathLoop(i), is(true)); | ||
306 | + } | ||
307 | + | ||
308 | + // Test for non-loops | ||
309 | + assertThat(bgpRouteEntry.hasAsPathLoop(500), is(false)); | ||
310 | + } | ||
311 | + | ||
312 | + /** | ||
313 | + * Tests the BGP Decision Process comparison of BGP routes. | ||
314 | + */ | ||
315 | + @Test | ||
316 | + public void testBgpDecisionProcessComparison() { | ||
317 | + BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry(); | ||
318 | + BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry(); | ||
319 | + | ||
320 | + // | ||
321 | + // Compare two routes that are same | ||
322 | + // | ||
323 | + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true)); | ||
324 | + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true)); | ||
325 | + | ||
326 | + // | ||
327 | + // Compare two routes with different LOCAL_PREF | ||
328 | + // | ||
329 | + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24"); | ||
330 | + IpAddress nextHop = IpAddress.valueOf("5.6.7.8"); | ||
331 | + byte origin = BgpConstants.Update.Origin.IGP; | ||
332 | + // Setup the AS Path | ||
333 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
334 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
335 | + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); | ||
336 | + segmentAsNumbers1.add((long) 1); | ||
337 | + segmentAsNumbers1.add((long) 2); | ||
338 | + segmentAsNumbers1.add((long) 3); | ||
339 | + BgpRouteEntry.PathSegment pathSegment1 = | ||
340 | + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); | ||
341 | + pathSegments.add(pathSegment1); | ||
342 | + // | ||
343 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
344 | + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); | ||
345 | + segmentAsNumbers2.add((long) 4); | ||
346 | + segmentAsNumbers2.add((long) 5); | ||
347 | + segmentAsNumbers2.add((long) 6); | ||
348 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
349 | + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); | ||
350 | + pathSegments.add(pathSegment2); | ||
351 | + // | ||
352 | + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
353 | + // | ||
354 | + long localPref = 50; // Different | ||
355 | + long multiExitDisc = 20; | ||
356 | + bgpRouteEntry2 = | ||
357 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath, | ||
358 | + localPref); | ||
359 | + bgpRouteEntry2.setMultiExitDisc(multiExitDisc); | ||
360 | + // | ||
361 | + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true)); | ||
362 | + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false)); | ||
363 | + localPref = bgpRouteEntry1.getLocalPref(); // Restore | ||
364 | + | ||
365 | + // | ||
366 | + // Compare two routes with different AS_PATH length | ||
367 | + // | ||
368 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments2 = new ArrayList<>(); | ||
369 | + pathSegments2.add(pathSegment1); | ||
370 | + // Different AS Path | ||
371 | + BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments2); | ||
372 | + bgpRouteEntry2 = | ||
373 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath2, | ||
374 | + localPref); | ||
375 | + bgpRouteEntry2.setMultiExitDisc(multiExitDisc); | ||
376 | + // | ||
377 | + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(false)); | ||
378 | + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true)); | ||
379 | + | ||
380 | + // | ||
381 | + // Compare two routes with different ORIGIN | ||
382 | + // | ||
383 | + origin = BgpConstants.Update.Origin.EGP; // Different | ||
384 | + bgpRouteEntry2 = | ||
385 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath, | ||
386 | + localPref); | ||
387 | + bgpRouteEntry2.setMultiExitDisc(multiExitDisc); | ||
388 | + // | ||
389 | + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true)); | ||
390 | + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false)); | ||
391 | + origin = bgpRouteEntry1.getOrigin(); // Restore | ||
392 | + | ||
393 | + // | ||
394 | + // Compare two routes with different MULTI_EXIT_DISC | ||
395 | + // | ||
396 | + multiExitDisc = 10; // Different | ||
397 | + bgpRouteEntry2 = | ||
398 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath, | ||
399 | + localPref); | ||
400 | + bgpRouteEntry2.setMultiExitDisc(multiExitDisc); | ||
401 | + // | ||
402 | + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true)); | ||
403 | + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false)); | ||
404 | + multiExitDisc = bgpRouteEntry1.getMultiExitDisc(); // Restore | ||
405 | + | ||
406 | + // | ||
407 | + // Compare two routes with different BGP ID | ||
408 | + // | ||
409 | + bgpRouteEntry2 = | ||
410 | + new BgpRouteEntry(bgpSession2, prefix, nextHop, origin, asPath, | ||
411 | + localPref); | ||
412 | + bgpRouteEntry2.setMultiExitDisc(multiExitDisc); | ||
413 | + // | ||
414 | + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true)); | ||
415 | + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false)); | ||
416 | + | ||
417 | + // | ||
418 | + // Compare two routes with different BGP address | ||
419 | + // | ||
420 | + bgpRouteEntry2 = | ||
421 | + new BgpRouteEntry(bgpSession3, prefix, nextHop, origin, asPath, | ||
422 | + localPref); | ||
423 | + bgpRouteEntry2.setMultiExitDisc(multiExitDisc); | ||
424 | + // | ||
425 | + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true)); | ||
426 | + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false)); | ||
427 | + } | ||
428 | + | ||
429 | + /** | ||
430 | + * Tests equality of {@link BgpRouteEntry}. | ||
431 | + */ | ||
432 | + @Test | ||
433 | + public void testEquality() { | ||
434 | + BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry(); | ||
435 | + BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry(); | ||
436 | + | ||
437 | + assertThat(bgpRouteEntry1, is(bgpRouteEntry2)); | ||
438 | + } | ||
439 | + | ||
440 | + /** | ||
441 | + * Tests non-equality of {@link BgpRouteEntry}. | ||
442 | + */ | ||
443 | + @Test | ||
444 | + public void testNonEquality() { | ||
445 | + BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry(); | ||
446 | + | ||
447 | + // Setup BGP Route 2 | ||
448 | + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24"); | ||
449 | + IpAddress nextHop = IpAddress.valueOf("5.6.7.8"); | ||
450 | + byte origin = BgpConstants.Update.Origin.IGP; | ||
451 | + // Setup the AS Path | ||
452 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
453 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
454 | + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); | ||
455 | + segmentAsNumbers1.add((long) 1); | ||
456 | + segmentAsNumbers1.add((long) 2); | ||
457 | + segmentAsNumbers1.add((long) 3); | ||
458 | + BgpRouteEntry.PathSegment pathSegment1 = | ||
459 | + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); | ||
460 | + pathSegments.add(pathSegment1); | ||
461 | + // | ||
462 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
463 | + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); | ||
464 | + segmentAsNumbers2.add((long) 4); | ||
465 | + segmentAsNumbers2.add((long) 5); | ||
466 | + segmentAsNumbers2.add((long) 6); | ||
467 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
468 | + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); | ||
469 | + pathSegments.add(pathSegment2); | ||
470 | + // | ||
471 | + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
472 | + // | ||
473 | + long localPref = 500; // Different | ||
474 | + long multiExitDisc = 20; | ||
475 | + BgpRouteEntry bgpRouteEntry2 = | ||
476 | + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath, | ||
477 | + localPref); | ||
478 | + bgpRouteEntry2.setMultiExitDisc(multiExitDisc); | ||
479 | + | ||
480 | + assertThat(bgpRouteEntry1, is(not(bgpRouteEntry2))); | ||
481 | + } | ||
482 | + | ||
483 | + /** | ||
484 | + * Tests object string representation. | ||
485 | + */ | ||
486 | + @Test | ||
487 | + public void testToString() { | ||
488 | + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry(); | ||
489 | + | ||
490 | + String expectedString = | ||
491 | + "BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " + | ||
492 | + "bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" + | ||
493 | + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " + | ||
494 | + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " + | ||
495 | + "localPref=100, multiExitDisc=20}"; | ||
496 | + assertThat(bgpRouteEntry.toString(), is(expectedString)); | ||
497 | + } | ||
498 | +} |
1 | +package org.onlab.onos.sdnip.bgp; | ||
2 | + | ||
3 | +import static org.hamcrest.Matchers.hasItem; | ||
4 | +import static org.hamcrest.Matchers.hasSize; | ||
5 | +import static org.hamcrest.Matchers.is; | ||
6 | +import static org.hamcrest.Matchers.notNullValue; | ||
7 | +import static org.junit.Assert.assertThat; | ||
8 | + | ||
9 | +import java.net.InetAddress; | ||
10 | +import java.net.InetSocketAddress; | ||
11 | +import java.net.SocketAddress; | ||
12 | +import java.util.ArrayList; | ||
13 | +import java.util.Collection; | ||
14 | +import java.util.LinkedList; | ||
15 | +import java.util.concurrent.Executors; | ||
16 | +import java.util.concurrent.TimeUnit; | ||
17 | + | ||
18 | +import org.jboss.netty.bootstrap.ClientBootstrap; | ||
19 | +import org.jboss.netty.buffer.ChannelBuffer; | ||
20 | +import org.jboss.netty.channel.Channel; | ||
21 | +import org.jboss.netty.channel.ChannelFactory; | ||
22 | +import org.jboss.netty.channel.ChannelPipeline; | ||
23 | +import org.jboss.netty.channel.ChannelPipelineFactory; | ||
24 | +import org.jboss.netty.channel.Channels; | ||
25 | +import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; | ||
26 | +import org.junit.After; | ||
27 | +import org.junit.Before; | ||
28 | +import org.junit.Test; | ||
29 | +import org.onlab.onos.sdnip.RouteListener; | ||
30 | +import org.onlab.onos.sdnip.RouteUpdate; | ||
31 | +import org.onlab.packet.IpAddress; | ||
32 | +import org.onlab.packet.IpPrefix; | ||
33 | +import org.onlab.util.TestUtils; | ||
34 | +import org.onlab.util.TestUtils.TestUtilsException; | ||
35 | + | ||
36 | +import com.google.common.net.InetAddresses; | ||
37 | + | ||
38 | +/** | ||
39 | + * Unit tests for the BgpSessionManager class. | ||
40 | + */ | ||
41 | +public class BgpSessionManagerTest { | ||
42 | + private static final IpAddress IP_LOOPBACK_ID = | ||
43 | + IpAddress.valueOf("127.0.0.1"); | ||
44 | + private static final IpAddress BGP_PEER1_ID = IpAddress.valueOf("10.0.0.1"); | ||
45 | + private static final long DEFAULT_LOCAL_PREF = 10; | ||
46 | + private static final long DEFAULT_MULTI_EXIT_DISC = 20; | ||
47 | + | ||
48 | + // The BGP Session Manager to test | ||
49 | + private BgpSessionManager bgpSessionManager; | ||
50 | + | ||
51 | + // Remote Peer state | ||
52 | + private ClientBootstrap peerBootstrap; | ||
53 | + private TestBgpPeerChannelHandler peerChannelHandler = | ||
54 | + new TestBgpPeerChannelHandler(BGP_PEER1_ID, DEFAULT_LOCAL_PREF); | ||
55 | + private TestBgpPeerFrameDecoder peerFrameDecoder = | ||
56 | + new TestBgpPeerFrameDecoder(); | ||
57 | + | ||
58 | + // The socket that the Remote Peer should connect to | ||
59 | + private InetSocketAddress connectToSocket; | ||
60 | + | ||
61 | + private final DummyRouteListener dummyRouteListener = | ||
62 | + new DummyRouteListener(); | ||
63 | + | ||
64 | + /** | ||
65 | + * Dummy implementation for the RouteListener interface. | ||
66 | + */ | ||
67 | + private class DummyRouteListener implements RouteListener { | ||
68 | + @Override | ||
69 | + public void update(RouteUpdate routeUpdate) { | ||
70 | + // Nothing to do | ||
71 | + } | ||
72 | + } | ||
73 | + | ||
74 | + @Before | ||
75 | + public void setUp() throws Exception { | ||
76 | + // | ||
77 | + // Setup the BGP Session Manager to test, and start listening for BGP | ||
78 | + // connections. | ||
79 | + // | ||
80 | + bgpSessionManager = new BgpSessionManager(dummyRouteListener); | ||
81 | + // NOTE: We use port 0 to bind on any available port | ||
82 | + bgpSessionManager.startUp(0); | ||
83 | + | ||
84 | + // Get the port number the BGP Session Manager is listening on | ||
85 | + Channel serverChannel = TestUtils.getField(bgpSessionManager, | ||
86 | + "serverChannel"); | ||
87 | + SocketAddress socketAddress = serverChannel.getLocalAddress(); | ||
88 | + InetSocketAddress inetSocketAddress = | ||
89 | + (InetSocketAddress) socketAddress; | ||
90 | + | ||
91 | + // | ||
92 | + // Setup the BGP Peer, i.e., the "remote" BGP router that will | ||
93 | + // initiate the BGP connection, send BGP UPDATE messages, etc. | ||
94 | + // | ||
95 | + ChannelFactory channelFactory = | ||
96 | + new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), | ||
97 | + Executors.newCachedThreadPool()); | ||
98 | + ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() { | ||
99 | + @Override | ||
100 | + public ChannelPipeline getPipeline() throws Exception { | ||
101 | + // Setup the transmitting pipeline | ||
102 | + ChannelPipeline pipeline = Channels.pipeline(); | ||
103 | + pipeline.addLast("TestBgpPeerFrameDecoder", | ||
104 | + peerFrameDecoder); | ||
105 | + pipeline.addLast("TestBgpPeerChannelHandler", | ||
106 | + peerChannelHandler); | ||
107 | + return pipeline; | ||
108 | + } | ||
109 | + }; | ||
110 | + | ||
111 | + peerBootstrap = new ClientBootstrap(channelFactory); | ||
112 | + peerBootstrap.setOption("child.keepAlive", true); | ||
113 | + peerBootstrap.setOption("child.tcpNoDelay", true); | ||
114 | + peerBootstrap.setPipelineFactory(pipelineFactory); | ||
115 | + | ||
116 | + InetAddress connectToAddress = InetAddresses.forString("127.0.0.1"); | ||
117 | + connectToSocket = new InetSocketAddress(connectToAddress, | ||
118 | + inetSocketAddress.getPort()); | ||
119 | + } | ||
120 | + | ||
121 | + @After | ||
122 | + public void tearDown() throws Exception { | ||
123 | + bgpSessionManager.shutDown(); | ||
124 | + bgpSessionManager = null; | ||
125 | + } | ||
126 | + | ||
127 | + /** | ||
128 | + * Gets BGP RIB-IN routes by waiting until they are received. | ||
129 | + * <p/> | ||
130 | + * NOTE: We keep checking once a second the number of received routes, | ||
131 | + * up to 5 seconds. | ||
132 | + * | ||
133 | + * @param bgpSession the BGP session that is expected to receive the | ||
134 | + * routes | ||
135 | + * @param expectedRoutes the expected number of routes | ||
136 | + * @return the BGP RIB-IN routes as received within the expected | ||
137 | + * time interval | ||
138 | + */ | ||
139 | + private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession, | ||
140 | + long expectedRoutes) | ||
141 | + throws InterruptedException { | ||
142 | + Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn(); | ||
143 | + | ||
144 | + final int maxChecks = 5; // Max wait of 5 seconds | ||
145 | + for (int i = 0; i < maxChecks; i++) { | ||
146 | + if (bgpRibIn.size() == expectedRoutes) { | ||
147 | + break; | ||
148 | + } | ||
149 | + Thread.sleep(1000); | ||
150 | + bgpRibIn = bgpSession.getBgpRibIn(); | ||
151 | + } | ||
152 | + | ||
153 | + return bgpRibIn; | ||
154 | + } | ||
155 | + | ||
156 | + /** | ||
157 | + * Gets BGP merged routes by waiting until they are received. | ||
158 | + * <p/> | ||
159 | + * NOTE: We keep checking once a second the number of received routes, | ||
160 | + * up to 5 seconds. | ||
161 | + * | ||
162 | + * @param expectedRoutes the expected number of routes | ||
163 | + * @return the BGP Session Manager routes as received within the expected | ||
164 | + * time interval | ||
165 | + */ | ||
166 | + private Collection<BgpRouteEntry> waitForBgpRoutes(long expectedRoutes) | ||
167 | + throws InterruptedException { | ||
168 | + Collection<BgpRouteEntry> bgpRoutes = bgpSessionManager.getBgpRoutes(); | ||
169 | + | ||
170 | + final int maxChecks = 5; // Max wait of 5 seconds | ||
171 | + for (int i = 0; i < maxChecks; i++) { | ||
172 | + if (bgpRoutes.size() == expectedRoutes) { | ||
173 | + break; | ||
174 | + } | ||
175 | + Thread.sleep(1000); | ||
176 | + bgpRoutes = bgpSessionManager.getBgpRoutes(); | ||
177 | + } | ||
178 | + | ||
179 | + return bgpRoutes; | ||
180 | + } | ||
181 | + | ||
182 | + /** | ||
183 | + * Tests that the BGP OPEN messages have been exchanged, followed by | ||
184 | + * KEEPALIVE. | ||
185 | + * <p> | ||
186 | + * The BGP Peer opens the sessions and transmits OPEN Message, eventually | ||
187 | + * followed by KEEPALIVE. The tested BGP listener should respond by | ||
188 | + * OPEN Message, followed by KEEPALIVE. | ||
189 | + * | ||
190 | + * @throws TestUtilsException TestUtils error | ||
191 | + */ | ||
192 | + @Test | ||
193 | + public void testExchangedBgpOpenMessages() | ||
194 | + throws InterruptedException, TestUtilsException { | ||
195 | + // Initiate the connection | ||
196 | + peerBootstrap.connect(connectToSocket); | ||
197 | + | ||
198 | + // Wait until the OPEN message is received | ||
199 | + peerFrameDecoder.receivedOpenMessageLatch.await(2000, | ||
200 | + TimeUnit.MILLISECONDS); | ||
201 | + // Wait until the KEEPALIVE message is received | ||
202 | + peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000, | ||
203 | + TimeUnit.MILLISECONDS); | ||
204 | + | ||
205 | + // | ||
206 | + // Test the fields from the BGP OPEN message: | ||
207 | + // BGP version, AS number, BGP ID | ||
208 | + // | ||
209 | + assertThat(peerFrameDecoder.remoteBgpVersion, | ||
210 | + is(BgpConstants.BGP_VERSION)); | ||
211 | + assertThat(peerFrameDecoder.remoteAs, | ||
212 | + is(TestBgpPeerChannelHandler.PEER_AS)); | ||
213 | + assertThat(peerFrameDecoder.remoteBgpIdentifier, is(IP_LOOPBACK_ID)); | ||
214 | + | ||
215 | + // | ||
216 | + // Test that a BgpSession instance has been created | ||
217 | + // | ||
218 | + assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID)); | ||
219 | + assertThat(bgpSessionManager.getBgpSessions(), hasSize(1)); | ||
220 | + BgpSession bgpSession = | ||
221 | + bgpSessionManager.getBgpSessions().iterator().next(); | ||
222 | + assertThat(bgpSession, notNullValue()); | ||
223 | + long sessionAs = TestUtils.getField(bgpSession, "localAs"); | ||
224 | + assertThat(sessionAs, is(TestBgpPeerChannelHandler.PEER_AS)); | ||
225 | + } | ||
226 | + | ||
227 | + /** | ||
228 | + * Tests that the BGP UPDATE messages have been received and processed. | ||
229 | + */ | ||
230 | + @Test | ||
231 | + public void testProcessedBgpUpdateMessages() throws InterruptedException { | ||
232 | + BgpSession bgpSession; | ||
233 | + IpAddress nextHopRouter; | ||
234 | + BgpRouteEntry bgpRouteEntry; | ||
235 | + ChannelBuffer message; | ||
236 | + Collection<BgpRouteEntry> bgpRibIn; | ||
237 | + Collection<BgpRouteEntry> bgpRoutes; | ||
238 | + | ||
239 | + // Initiate the connection | ||
240 | + peerBootstrap.connect(connectToSocket); | ||
241 | + | ||
242 | + // Wait until the OPEN message is received | ||
243 | + peerFrameDecoder.receivedOpenMessageLatch.await(2000, | ||
244 | + TimeUnit.MILLISECONDS); | ||
245 | + // Wait until the KEEPALIVE message is received | ||
246 | + peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000, | ||
247 | + TimeUnit.MILLISECONDS); | ||
248 | + | ||
249 | + // Get the BGP Session handler | ||
250 | + bgpSession = bgpSessionManager.getBgpSessions().iterator().next(); | ||
251 | + | ||
252 | + // Prepare routes to add/delete | ||
253 | + nextHopRouter = IpAddress.valueOf("10.20.30.40"); | ||
254 | + Collection<IpPrefix> addedRoutes = new LinkedList<>(); | ||
255 | + Collection<IpPrefix> withdrawnRoutes = new LinkedList<>(); | ||
256 | + addedRoutes.add(IpPrefix.valueOf("0.0.0.0/0")); | ||
257 | + addedRoutes.add(IpPrefix.valueOf("20.0.0.0/8")); | ||
258 | + addedRoutes.add(IpPrefix.valueOf("30.0.0.0/16")); | ||
259 | + addedRoutes.add(IpPrefix.valueOf("40.0.0.0/24")); | ||
260 | + addedRoutes.add(IpPrefix.valueOf("50.0.0.0/32")); | ||
261 | + withdrawnRoutes.add(IpPrefix.valueOf("60.0.0.0/8")); | ||
262 | + withdrawnRoutes.add(IpPrefix.valueOf("70.0.0.0/16")); | ||
263 | + withdrawnRoutes.add(IpPrefix.valueOf("80.0.0.0/24")); | ||
264 | + withdrawnRoutes.add(IpPrefix.valueOf("90.0.0.0/32")); | ||
265 | + // Write the routes | ||
266 | + message = peerChannelHandler.prepareBgpUpdate(nextHopRouter, | ||
267 | + addedRoutes, | ||
268 | + withdrawnRoutes); | ||
269 | + peerChannelHandler.savedCtx.getChannel().write(message); | ||
270 | + | ||
271 | + // Check that the routes have been received, processed and stored | ||
272 | + bgpRibIn = waitForBgpRibIn(bgpSession, 5); | ||
273 | + assertThat(bgpRibIn, hasSize(5)); | ||
274 | + bgpRoutes = waitForBgpRoutes(5); | ||
275 | + assertThat(bgpRoutes, hasSize(5)); | ||
276 | + | ||
277 | + // Setup the AS Path | ||
278 | + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
279 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
280 | + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); | ||
281 | + segmentAsNumbers1.add((long) 65010); | ||
282 | + segmentAsNumbers1.add((long) 65020); | ||
283 | + segmentAsNumbers1.add((long) 65030); | ||
284 | + BgpRouteEntry.PathSegment pathSegment1 = | ||
285 | + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); | ||
286 | + pathSegments.add(pathSegment1); | ||
287 | + // | ||
288 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
289 | + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); | ||
290 | + segmentAsNumbers2.add((long) 65041); | ||
291 | + segmentAsNumbers2.add((long) 65042); | ||
292 | + segmentAsNumbers2.add((long) 65043); | ||
293 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
294 | + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); | ||
295 | + pathSegments.add(pathSegment2); | ||
296 | + // | ||
297 | + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); | ||
298 | + | ||
299 | + // | ||
300 | + bgpRouteEntry = | ||
301 | + new BgpRouteEntry(bgpSession, | ||
302 | + IpPrefix.valueOf("0.0.0.0/0"), | ||
303 | + nextHopRouter, | ||
304 | + (byte) BgpConstants.Update.Origin.IGP, | ||
305 | + asPath, | ||
306 | + DEFAULT_LOCAL_PREF); | ||
307 | + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); | ||
308 | + assertThat(bgpRibIn, hasItem(bgpRouteEntry)); | ||
309 | + // | ||
310 | + bgpRouteEntry = | ||
311 | + new BgpRouteEntry(bgpSession, | ||
312 | + IpPrefix.valueOf("20.0.0.0/8"), | ||
313 | + nextHopRouter, | ||
314 | + (byte) BgpConstants.Update.Origin.IGP, | ||
315 | + asPath, | ||
316 | + DEFAULT_LOCAL_PREF); | ||
317 | + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); | ||
318 | + assertThat(bgpRibIn, hasItem(bgpRouteEntry)); | ||
319 | + // | ||
320 | + bgpRouteEntry = | ||
321 | + new BgpRouteEntry(bgpSession, | ||
322 | + IpPrefix.valueOf("30.0.0.0/16"), | ||
323 | + nextHopRouter, | ||
324 | + (byte) BgpConstants.Update.Origin.IGP, | ||
325 | + asPath, | ||
326 | + DEFAULT_LOCAL_PREF); | ||
327 | + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); | ||
328 | + assertThat(bgpRibIn, hasItem(bgpRouteEntry)); | ||
329 | + // | ||
330 | + bgpRouteEntry = | ||
331 | + new BgpRouteEntry(bgpSession, | ||
332 | + IpPrefix.valueOf("40.0.0.0/24"), | ||
333 | + nextHopRouter, | ||
334 | + (byte) BgpConstants.Update.Origin.IGP, | ||
335 | + asPath, | ||
336 | + DEFAULT_LOCAL_PREF); | ||
337 | + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); | ||
338 | + assertThat(bgpRibIn, hasItem(bgpRouteEntry)); | ||
339 | + // | ||
340 | + bgpRouteEntry = | ||
341 | + new BgpRouteEntry(bgpSession, | ||
342 | + IpPrefix.valueOf("50.0.0.0/32"), | ||
343 | + nextHopRouter, | ||
344 | + (byte) BgpConstants.Update.Origin.IGP, | ||
345 | + asPath, | ||
346 | + DEFAULT_LOCAL_PREF); | ||
347 | + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); | ||
348 | + assertThat(bgpRibIn, hasItem(bgpRouteEntry)); | ||
349 | + | ||
350 | + // Delete some routes | ||
351 | + addedRoutes = new LinkedList<>(); | ||
352 | + withdrawnRoutes = new LinkedList<>(); | ||
353 | + withdrawnRoutes.add(IpPrefix.valueOf("0.0.0.0/0")); | ||
354 | + withdrawnRoutes.add(IpPrefix.valueOf("50.0.0.0/32")); | ||
355 | + | ||
356 | + // Write the routes | ||
357 | + message = peerChannelHandler.prepareBgpUpdate(nextHopRouter, | ||
358 | + addedRoutes, | ||
359 | + withdrawnRoutes); | ||
360 | + peerChannelHandler.savedCtx.getChannel().write(message); | ||
361 | + | ||
362 | + // Check that the routes have been received, processed and stored | ||
363 | + bgpRibIn = waitForBgpRibIn(bgpSession, 3); | ||
364 | + assertThat(bgpRibIn, hasSize(3)); | ||
365 | + bgpRoutes = waitForBgpRoutes(3); | ||
366 | + assertThat(bgpRoutes, hasSize(3)); | ||
367 | + // | ||
368 | + bgpRouteEntry = | ||
369 | + new BgpRouteEntry(bgpSession, | ||
370 | + IpPrefix.valueOf("20.0.0.0/8"), | ||
371 | + nextHopRouter, | ||
372 | + (byte) BgpConstants.Update.Origin.IGP, | ||
373 | + asPath, | ||
374 | + DEFAULT_LOCAL_PREF); | ||
375 | + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); | ||
376 | + assertThat(bgpRibIn, hasItem(bgpRouteEntry)); | ||
377 | + // | ||
378 | + bgpRouteEntry = | ||
379 | + new BgpRouteEntry(bgpSession, | ||
380 | + IpPrefix.valueOf("30.0.0.0/16"), | ||
381 | + nextHopRouter, | ||
382 | + (byte) BgpConstants.Update.Origin.IGP, | ||
383 | + asPath, | ||
384 | + DEFAULT_LOCAL_PREF); | ||
385 | + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); | ||
386 | + assertThat(bgpRibIn, hasItem(bgpRouteEntry)); | ||
387 | + // | ||
388 | + bgpRouteEntry = | ||
389 | + new BgpRouteEntry(bgpSession, | ||
390 | + IpPrefix.valueOf("40.0.0.0/24"), | ||
391 | + nextHopRouter, | ||
392 | + (byte) BgpConstants.Update.Origin.IGP, | ||
393 | + asPath, | ||
394 | + DEFAULT_LOCAL_PREF); | ||
395 | + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); | ||
396 | + assertThat(bgpRibIn, hasItem(bgpRouteEntry)); | ||
397 | + | ||
398 | + // Close the channel and test there are no routes | ||
399 | + peerChannelHandler.closeChannel(); | ||
400 | + bgpRoutes = waitForBgpRoutes(0); | ||
401 | + assertThat(bgpRoutes, hasSize(0)); | ||
402 | + } | ||
403 | +} |
1 | +package org.onlab.onos.sdnip.bgp; | ||
2 | + | ||
3 | +import static org.hamcrest.Matchers.is; | ||
4 | +import static org.hamcrest.Matchers.not; | ||
5 | +import static org.junit.Assert.assertThat; | ||
6 | + | ||
7 | +import java.util.ArrayList; | ||
8 | + | ||
9 | +import org.junit.Test; | ||
10 | + | ||
11 | +/** | ||
12 | + * Unit tests for the BgpRouteEntry.PathSegment class. | ||
13 | + */ | ||
14 | +public class PathSegmentTest { | ||
15 | + /** | ||
16 | + * Generates a Path Segment. | ||
17 | + * | ||
18 | + * @return a generated PathSegment | ||
19 | + */ | ||
20 | + private BgpRouteEntry.PathSegment generatePathSegment() { | ||
21 | + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
22 | + ArrayList<Long> segmentAsNumbers = new ArrayList<>(); | ||
23 | + segmentAsNumbers.add((long) 1); | ||
24 | + segmentAsNumbers.add((long) 2); | ||
25 | + segmentAsNumbers.add((long) 3); | ||
26 | + BgpRouteEntry.PathSegment pathSegment = | ||
27 | + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); | ||
28 | + | ||
29 | + return pathSegment; | ||
30 | + } | ||
31 | + | ||
32 | + /** | ||
33 | + * Tests valid class constructor. | ||
34 | + */ | ||
35 | + @Test | ||
36 | + public void testConstructor() { | ||
37 | + BgpRouteEntry.PathSegment pathSegment = generatePathSegment(); | ||
38 | + | ||
39 | + String expectedString = | ||
40 | + "PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}"; | ||
41 | + assertThat(pathSegment.toString(), is(expectedString)); | ||
42 | + } | ||
43 | + | ||
44 | + /** | ||
45 | + * Tests invalid class constructor for null Segment AS Numbers. | ||
46 | + */ | ||
47 | + @Test(expected = NullPointerException.class) | ||
48 | + public void testInvalidConstructorNullSegmentAsNumbers() { | ||
49 | + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
50 | + ArrayList<Long> segmentAsNumbers = null; | ||
51 | + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); | ||
52 | + } | ||
53 | + | ||
54 | + /** | ||
55 | + * Tests getting the fields of a Path Segment. | ||
56 | + */ | ||
57 | + @Test | ||
58 | + public void testGetFields() { | ||
59 | + // Create the fields to compare against | ||
60 | + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
61 | + ArrayList<Long> segmentAsNumbers = new ArrayList<>(); | ||
62 | + segmentAsNumbers.add((long) 1); | ||
63 | + segmentAsNumbers.add((long) 2); | ||
64 | + segmentAsNumbers.add((long) 3); | ||
65 | + | ||
66 | + // Generate the entry to test | ||
67 | + BgpRouteEntry.PathSegment pathSegment = generatePathSegment(); | ||
68 | + | ||
69 | + assertThat(pathSegment.getType(), is(pathSegmentType)); | ||
70 | + assertThat(pathSegment.getSegmentAsNumbers(), is(segmentAsNumbers)); | ||
71 | + } | ||
72 | + | ||
73 | + /** | ||
74 | + * Tests equality of {@link BgpRouteEntry.PathSegment}. | ||
75 | + */ | ||
76 | + @Test | ||
77 | + public void testEquality() { | ||
78 | + BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment(); | ||
79 | + BgpRouteEntry.PathSegment pathSegment2 = generatePathSegment(); | ||
80 | + | ||
81 | + assertThat(pathSegment1, is(pathSegment2)); | ||
82 | + } | ||
83 | + | ||
84 | + /** | ||
85 | + * Tests non-equality of {@link BgpRouteEntry.PathSegment}. | ||
86 | + */ | ||
87 | + @Test | ||
88 | + public void testNonEquality() { | ||
89 | + BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment(); | ||
90 | + | ||
91 | + // Setup Path Segment 2 | ||
92 | + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
93 | + ArrayList<Long> segmentAsNumbers = new ArrayList<>(); | ||
94 | + segmentAsNumbers.add((long) 1); | ||
95 | + segmentAsNumbers.add((long) 22); // Different | ||
96 | + segmentAsNumbers.add((long) 3); | ||
97 | + // | ||
98 | + BgpRouteEntry.PathSegment pathSegment2 = | ||
99 | + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); | ||
100 | + | ||
101 | + assertThat(pathSegment1, is(not(pathSegment2))); | ||
102 | + } | ||
103 | + | ||
104 | + /** | ||
105 | + * Tests object string representation. | ||
106 | + */ | ||
107 | + @Test | ||
108 | + public void testToString() { | ||
109 | + BgpRouteEntry.PathSegment pathSegment = generatePathSegment(); | ||
110 | + | ||
111 | + String expectedString = | ||
112 | + "PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}"; | ||
113 | + assertThat(pathSegment.toString(), is(expectedString)); | ||
114 | + } | ||
115 | +} |
1 | +package org.onlab.onos.sdnip.bgp; | ||
2 | + | ||
3 | +import java.util.Collection; | ||
4 | + | ||
5 | +import org.jboss.netty.buffer.ChannelBuffer; | ||
6 | +import org.jboss.netty.buffer.ChannelBuffers; | ||
7 | +import org.jboss.netty.channel.ChannelHandlerContext; | ||
8 | +import org.jboss.netty.channel.ChannelStateEvent; | ||
9 | +import org.jboss.netty.channel.SimpleChannelHandler; | ||
10 | +import org.onlab.packet.IpAddress; | ||
11 | +import org.onlab.packet.IpPrefix; | ||
12 | + | ||
13 | +/** | ||
14 | + * Class for handling the remote BGP Peer session. | ||
15 | + */ | ||
16 | +class TestBgpPeerChannelHandler extends SimpleChannelHandler { | ||
17 | + static final long PEER_AS = 65001; | ||
18 | + static final int PEER_HOLDTIME = 120; // 120 seconds | ||
19 | + final IpAddress bgpId; // The BGP ID | ||
20 | + final long localPref; // Local preference for routes | ||
21 | + final long multiExitDisc = 20; // MED value | ||
22 | + | ||
23 | + ChannelHandlerContext savedCtx; | ||
24 | + | ||
25 | + /** | ||
26 | + * Constructor for given BGP ID. | ||
27 | + * | ||
28 | + * @param bgpId the BGP ID to use | ||
29 | + * @param localPref the local preference for the routes to use | ||
30 | + */ | ||
31 | + TestBgpPeerChannelHandler(IpAddress bgpId, | ||
32 | + long localPref) { | ||
33 | + this.bgpId = bgpId; | ||
34 | + this.localPref = localPref; | ||
35 | + } | ||
36 | + | ||
37 | + /** | ||
38 | + * Closes the channel. | ||
39 | + */ | ||
40 | + void closeChannel() { | ||
41 | + savedCtx.getChannel().close(); | ||
42 | + } | ||
43 | + | ||
44 | + @Override | ||
45 | + public void channelConnected(ChannelHandlerContext ctx, | ||
46 | + ChannelStateEvent channelEvent) { | ||
47 | + this.savedCtx = ctx; | ||
48 | + // Prepare and transmit BGP OPEN message | ||
49 | + ChannelBuffer message = prepareBgpOpen(); | ||
50 | + ctx.getChannel().write(message); | ||
51 | + | ||
52 | + // Prepare and transmit BGP KEEPALIVE message | ||
53 | + message = prepareBgpKeepalive(); | ||
54 | + ctx.getChannel().write(message); | ||
55 | + } | ||
56 | + | ||
57 | + @Override | ||
58 | + public void channelDisconnected(ChannelHandlerContext ctx, | ||
59 | + ChannelStateEvent channelEvent) { | ||
60 | + // Nothing to do | ||
61 | + } | ||
62 | + | ||
63 | + /** | ||
64 | + * Prepares BGP OPEN message. | ||
65 | + * | ||
66 | + * @return the message to transmit (BGP header included) | ||
67 | + */ | ||
68 | + ChannelBuffer prepareBgpOpen() { | ||
69 | + ChannelBuffer message = | ||
70 | + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
71 | + message.writeByte(BgpConstants.BGP_VERSION); | ||
72 | + message.writeShort((int) PEER_AS); | ||
73 | + message.writeShort(PEER_HOLDTIME); | ||
74 | + message.writeInt(bgpId.toInt()); | ||
75 | + message.writeByte(0); // No Optional Parameters | ||
76 | + return prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, message); | ||
77 | + } | ||
78 | + | ||
79 | + /** | ||
80 | + * Prepares BGP UPDATE message. | ||
81 | + * | ||
82 | + * @param nextHopRouter the next-hop router address for the routes to add | ||
83 | + * @param addedRoutes the routes to add | ||
84 | + * @param withdrawnRoutes the routes to withdraw | ||
85 | + * @return the message to transmit (BGP header included) | ||
86 | + */ | ||
87 | + ChannelBuffer prepareBgpUpdate(IpAddress nextHopRouter, | ||
88 | + Collection<IpPrefix> addedRoutes, | ||
89 | + Collection<IpPrefix> withdrawnRoutes) { | ||
90 | + int attrFlags; | ||
91 | + ChannelBuffer message = | ||
92 | + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
93 | + ChannelBuffer pathAttributes = | ||
94 | + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
95 | + | ||
96 | + // Encode the Withdrawn Routes | ||
97 | + ChannelBuffer encodedPrefixes = encodePackedPrefixes(withdrawnRoutes); | ||
98 | + message.writeShort(encodedPrefixes.readableBytes()); | ||
99 | + message.writeBytes(encodedPrefixes); | ||
100 | + | ||
101 | + // Encode the Path Attributes | ||
102 | + // ORIGIN: IGP | ||
103 | + attrFlags = 0x40; // Transitive flag | ||
104 | + pathAttributes.writeByte(attrFlags); | ||
105 | + pathAttributes.writeByte(BgpConstants.Update.Origin.TYPE); | ||
106 | + pathAttributes.writeByte(1); // Data length | ||
107 | + pathAttributes.writeByte(BgpConstants.Update.Origin.IGP); | ||
108 | + // AS_PATH: Two Path Segments of 3 ASes each | ||
109 | + attrFlags = 0x40; // Transitive flag | ||
110 | + pathAttributes.writeByte(attrFlags); | ||
111 | + pathAttributes.writeByte(BgpConstants.Update.AsPath.TYPE); | ||
112 | + pathAttributes.writeByte(16); // Data length | ||
113 | + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; | ||
114 | + pathAttributes.writeByte(pathSegmentType1); | ||
115 | + pathAttributes.writeByte(3); // Three ASes | ||
116 | + pathAttributes.writeShort(65010); // AS=65010 | ||
117 | + pathAttributes.writeShort(65020); // AS=65020 | ||
118 | + pathAttributes.writeShort(65030); // AS=65030 | ||
119 | + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; | ||
120 | + pathAttributes.writeByte(pathSegmentType2); | ||
121 | + pathAttributes.writeByte(3); // Three ASes | ||
122 | + pathAttributes.writeShort(65041); // AS=65041 | ||
123 | + pathAttributes.writeShort(65042); // AS=65042 | ||
124 | + pathAttributes.writeShort(65043); // AS=65043 | ||
125 | + // NEXT_HOP: nextHopRouter | ||
126 | + attrFlags = 0x40; // Transitive flag | ||
127 | + pathAttributes.writeByte(attrFlags); | ||
128 | + pathAttributes.writeByte(BgpConstants.Update.NextHop.TYPE); | ||
129 | + pathAttributes.writeByte(4); // Data length | ||
130 | + pathAttributes.writeInt(nextHopRouter.toInt()); // Next-hop router | ||
131 | + // LOCAL_PREF: localPref | ||
132 | + attrFlags = 0x40; // Transitive flag | ||
133 | + pathAttributes.writeByte(attrFlags); | ||
134 | + pathAttributes.writeByte(BgpConstants.Update.LocalPref.TYPE); | ||
135 | + pathAttributes.writeByte(4); // Data length | ||
136 | + pathAttributes.writeInt((int) localPref); // Preference value | ||
137 | + // MULTI_EXIT_DISC: multiExitDisc | ||
138 | + attrFlags = 0x80; // Optional | ||
139 | + // Non-Transitive flag | ||
140 | + pathAttributes.writeByte(attrFlags); | ||
141 | + pathAttributes.writeByte(BgpConstants.Update.MultiExitDisc.TYPE); | ||
142 | + pathAttributes.writeByte(4); // Data length | ||
143 | + pathAttributes.writeInt((int) multiExitDisc); // Preference value | ||
144 | + // The NLRI prefixes | ||
145 | + encodedPrefixes = encodePackedPrefixes(addedRoutes); | ||
146 | + | ||
147 | + // Write the Path Attributes, beginning with its length | ||
148 | + message.writeShort(pathAttributes.readableBytes()); | ||
149 | + message.writeBytes(pathAttributes); | ||
150 | + message.writeBytes(encodedPrefixes); | ||
151 | + | ||
152 | + return prepareBgpMessage(BgpConstants.BGP_TYPE_UPDATE, message); | ||
153 | + } | ||
154 | + | ||
155 | + /** | ||
156 | + * Encodes a collection of IPv4 network prefixes in a packed format. | ||
157 | + * <p> | ||
158 | + * The IPv4 prefixes are encoded in the form: | ||
159 | + * <Length, Prefix> where Length is the length in bits of the IPv4 prefix, | ||
160 | + * and Prefix is the IPv4 prefix (padded with trailing bits to the end | ||
161 | + * of an octet). | ||
162 | + * | ||
163 | + * @param prefixes the prefixes to encode | ||
164 | + * @return the buffer with the encoded prefixes | ||
165 | + */ | ||
166 | + private ChannelBuffer encodePackedPrefixes(Collection<IpPrefix> prefixes) { | ||
167 | + ChannelBuffer message = | ||
168 | + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
169 | + | ||
170 | + // Write each of the prefixes | ||
171 | + for (IpPrefix prefix : prefixes) { | ||
172 | + int prefixBitlen = prefix.prefixLength(); | ||
173 | + int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up | ||
174 | + message.writeByte(prefixBitlen); | ||
175 | + | ||
176 | + IpAddress address = prefix.toIpAddress(); | ||
177 | + long value = address.toInt() & 0xffffffffL; | ||
178 | + for (int i = 0; i < IpAddress.INET_LEN; i++) { | ||
179 | + if (prefixBytelen-- == 0) { | ||
180 | + break; | ||
181 | + } | ||
182 | + long nextByte = | ||
183 | + (value >> ((IpAddress.INET_LEN - i - 1) * 8)) & 0xff; | ||
184 | + message.writeByte((int) nextByte); | ||
185 | + } | ||
186 | + } | ||
187 | + | ||
188 | + return message; | ||
189 | + } | ||
190 | + | ||
191 | + /** | ||
192 | + * Prepares BGP KEEPALIVE message. | ||
193 | + * | ||
194 | + * @return the message to transmit (BGP header included) | ||
195 | + */ | ||
196 | + ChannelBuffer prepareBgpKeepalive() { | ||
197 | + ChannelBuffer message = | ||
198 | + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
199 | + return prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, message); | ||
200 | + } | ||
201 | + | ||
202 | + /** | ||
203 | + * Prepares BGP NOTIFICATION message. | ||
204 | + * | ||
205 | + * @param errorCode the BGP NOTIFICATION Error Code | ||
206 | + * @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable, | ||
207 | + * otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC | ||
208 | + * @param payload the BGP NOTIFICATION Data if applicable, otherwise null | ||
209 | + * @return the message to transmit (BGP header included) | ||
210 | + */ | ||
211 | + ChannelBuffer prepareBgpNotification(int errorCode, int errorSubcode, | ||
212 | + ChannelBuffer data) { | ||
213 | + ChannelBuffer message = | ||
214 | + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
215 | + // Prepare the NOTIFICATION message payload | ||
216 | + message.writeByte(errorCode); | ||
217 | + message.writeByte(errorSubcode); | ||
218 | + if (data != null) { | ||
219 | + message.writeBytes(data); | ||
220 | + } | ||
221 | + return prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, message); | ||
222 | + } | ||
223 | + | ||
224 | + /** | ||
225 | + * Prepares BGP message. | ||
226 | + * | ||
227 | + * @param type the BGP message type | ||
228 | + * @param payload the message payload to transmit (BGP header excluded) | ||
229 | + * @return the message to transmit (BGP header included) | ||
230 | + */ | ||
231 | + private ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) { | ||
232 | + ChannelBuffer message = | ||
233 | + ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH + | ||
234 | + payload.readableBytes()); | ||
235 | + | ||
236 | + // Write the marker | ||
237 | + for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) { | ||
238 | + message.writeByte(0xff); | ||
239 | + } | ||
240 | + | ||
241 | + // Write the rest of the BGP header | ||
242 | + message.writeShort(BgpConstants.BGP_HEADER_LENGTH + | ||
243 | + payload.readableBytes()); | ||
244 | + message.writeByte(type); | ||
245 | + | ||
246 | + // Write the payload | ||
247 | + message.writeBytes(payload); | ||
248 | + return message; | ||
249 | + } | ||
250 | +} |
1 | +package org.onlab.onos.sdnip.bgp; | ||
2 | + | ||
3 | +import java.util.concurrent.CountDownLatch; | ||
4 | + | ||
5 | +import org.jboss.netty.buffer.ChannelBuffer; | ||
6 | +import org.jboss.netty.channel.Channel; | ||
7 | +import org.jboss.netty.channel.ChannelHandlerContext; | ||
8 | +import org.jboss.netty.handler.codec.frame.FrameDecoder; | ||
9 | +import org.onlab.packet.IpAddress; | ||
10 | + | ||
11 | +/** | ||
12 | + * Class for handling the decoding of the BGP messages at the remote | ||
13 | + * BGP peer session. | ||
14 | + */ | ||
15 | +class TestBgpPeerFrameDecoder extends FrameDecoder { | ||
16 | + int remoteBgpVersion; // 1 octet | ||
17 | + long remoteAs; // 2 octets | ||
18 | + long remoteHoldtime; // 2 octets | ||
19 | + IpAddress remoteBgpIdentifier; // 4 octets -> IPv4 address | ||
20 | + | ||
21 | + final CountDownLatch receivedOpenMessageLatch = new CountDownLatch(1); | ||
22 | + final CountDownLatch receivedKeepaliveMessageLatch = new CountDownLatch(1); | ||
23 | + | ||
24 | + @Override | ||
25 | + protected Object decode(ChannelHandlerContext ctx, | ||
26 | + Channel channel, | ||
27 | + ChannelBuffer buf) throws Exception { | ||
28 | + // Test for minimum length of the BGP message | ||
29 | + if (buf.readableBytes() < BgpConstants.BGP_HEADER_LENGTH) { | ||
30 | + // No enough data received | ||
31 | + return null; | ||
32 | + } | ||
33 | + | ||
34 | + // | ||
35 | + // Mark the current buffer position in case we haven't received | ||
36 | + // the whole message. | ||
37 | + // | ||
38 | + buf.markReaderIndex(); | ||
39 | + | ||
40 | + // | ||
41 | + // Read and check the BGP message Marker field: it must be all ones | ||
42 | + // | ||
43 | + byte[] marker = new byte[BgpConstants.BGP_HEADER_MARKER_LENGTH]; | ||
44 | + buf.readBytes(marker); | ||
45 | + for (int i = 0; i < marker.length; i++) { | ||
46 | + if (marker[i] != (byte) 0xff) { | ||
47 | + // ERROR: Connection Not Synchronized. Close the channel. | ||
48 | + ctx.getChannel().close(); | ||
49 | + return null; | ||
50 | + } | ||
51 | + } | ||
52 | + | ||
53 | + // | ||
54 | + // Read and check the BGP message Length field | ||
55 | + // | ||
56 | + int length = buf.readUnsignedShort(); | ||
57 | + if ((length < BgpConstants.BGP_HEADER_LENGTH) || | ||
58 | + (length > BgpConstants.BGP_MESSAGE_MAX_LENGTH)) { | ||
59 | + // ERROR: Bad Message Length. Close the channel. | ||
60 | + ctx.getChannel().close(); | ||
61 | + return null; | ||
62 | + } | ||
63 | + | ||
64 | + // | ||
65 | + // Test whether the rest of the message is received: | ||
66 | + // So far we have read the Marker (16 octets) and the | ||
67 | + // Length (2 octets) fields. | ||
68 | + // | ||
69 | + int remainingMessageLen = | ||
70 | + length - BgpConstants.BGP_HEADER_MARKER_LENGTH - 2; | ||
71 | + if (buf.readableBytes() < remainingMessageLen) { | ||
72 | + // No enough data received | ||
73 | + buf.resetReaderIndex(); | ||
74 | + return null; | ||
75 | + } | ||
76 | + | ||
77 | + // | ||
78 | + // Read the BGP message Type field, and process based on that type | ||
79 | + // | ||
80 | + int type = buf.readUnsignedByte(); | ||
81 | + remainingMessageLen--; // Adjust after reading the type | ||
82 | + ChannelBuffer message = buf.readBytes(remainingMessageLen); | ||
83 | + | ||
84 | + // | ||
85 | + // Process the remaining of the message based on the message type | ||
86 | + // | ||
87 | + switch (type) { | ||
88 | + case BgpConstants.BGP_TYPE_OPEN: | ||
89 | + processBgpOpen(ctx, message); | ||
90 | + break; | ||
91 | + case BgpConstants.BGP_TYPE_UPDATE: | ||
92 | + // NOTE: Not used as part of the test, because ONOS does not | ||
93 | + // originate UPDATE messages. | ||
94 | + break; | ||
95 | + case BgpConstants.BGP_TYPE_NOTIFICATION: | ||
96 | + // NOTE: Not used as part of the testing (yet) | ||
97 | + break; | ||
98 | + case BgpConstants.BGP_TYPE_KEEPALIVE: | ||
99 | + processBgpKeepalive(ctx, message); | ||
100 | + break; | ||
101 | + default: | ||
102 | + // ERROR: Bad Message Type. Close the channel. | ||
103 | + ctx.getChannel().close(); | ||
104 | + return null; | ||
105 | + } | ||
106 | + | ||
107 | + return null; | ||
108 | + } | ||
109 | + | ||
110 | + /** | ||
111 | + * Processes BGP OPEN message. | ||
112 | + * | ||
113 | + * @param ctx the Channel Handler Context. | ||
114 | + * @param message the message to process. | ||
115 | + */ | ||
116 | + private void processBgpOpen(ChannelHandlerContext ctx, | ||
117 | + ChannelBuffer message) { | ||
118 | + int minLength = | ||
119 | + BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH; | ||
120 | + if (message.readableBytes() < minLength) { | ||
121 | + // ERROR: Bad Message Length. Close the channel. | ||
122 | + ctx.getChannel().close(); | ||
123 | + return; | ||
124 | + } | ||
125 | + | ||
126 | + // | ||
127 | + // Parse the OPEN message | ||
128 | + // | ||
129 | + remoteBgpVersion = message.readUnsignedByte(); | ||
130 | + remoteAs = message.readUnsignedShort(); | ||
131 | + remoteHoldtime = message.readUnsignedShort(); | ||
132 | + remoteBgpIdentifier = IpAddress.valueOf((int) message.readUnsignedInt()); | ||
133 | + // Optional Parameters | ||
134 | + int optParamLen = message.readUnsignedByte(); | ||
135 | + if (message.readableBytes() < optParamLen) { | ||
136 | + // ERROR: Bad Message Length. Close the channel. | ||
137 | + ctx.getChannel().close(); | ||
138 | + return; | ||
139 | + } | ||
140 | + message.readBytes(optParamLen); // NOTE: data ignored | ||
141 | + | ||
142 | + // BGP OPEN message successfully received | ||
143 | + receivedOpenMessageLatch.countDown(); | ||
144 | + } | ||
145 | + | ||
146 | + /** | ||
147 | + * Processes BGP KEEPALIVE message. | ||
148 | + * | ||
149 | + * @param ctx the Channel Handler Context. | ||
150 | + * @param message the message to process. | ||
151 | + */ | ||
152 | + private void processBgpKeepalive(ChannelHandlerContext ctx, | ||
153 | + ChannelBuffer message) { | ||
154 | + if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH != | ||
155 | + BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) { | ||
156 | + // ERROR: Bad Message Length. Close the channel. | ||
157 | + ctx.getChannel().close(); | ||
158 | + return; | ||
159 | + } | ||
160 | + // BGP KEEPALIVE message successfully received | ||
161 | + receivedKeepaliveMessageLatch.countDown(); | ||
162 | + } | ||
163 | +} |
1 | +package org.onlab.util; | ||
2 | + | ||
3 | +import java.lang.reflect.Constructor; | ||
4 | +import java.lang.reflect.Field; | ||
5 | +import java.lang.reflect.InvocationTargetException; | ||
6 | +import java.lang.reflect.Method; | ||
7 | + | ||
8 | + | ||
9 | +/** | ||
10 | + * Utilities for testing. | ||
11 | + */ | ||
12 | +public final class TestUtils { | ||
13 | + | ||
14 | + /** | ||
15 | + * Sets the field, bypassing scope restriction. | ||
16 | + * | ||
17 | + * @param subject Object where the field belongs | ||
18 | + * @param fieldName name of the field to set | ||
19 | + * @param value value to set to the field. | ||
20 | + * @param <T> subject type | ||
21 | + * @param <U> value type | ||
22 | + * @throws TestUtilsException if there are reflection errors while setting | ||
23 | + * the field | ||
24 | + */ | ||
25 | + public static <T, U> void setField(T subject, String fieldName, U value) | ||
26 | + throws TestUtilsException { | ||
27 | + @SuppressWarnings("unchecked") | ||
28 | + Class<T> clazz = (Class<T>) subject.getClass(); | ||
29 | + try { | ||
30 | + Field field = clazz.getDeclaredField(fieldName); | ||
31 | + field.setAccessible(true); | ||
32 | + field.set(subject, value); | ||
33 | + } catch (NoSuchFieldException | SecurityException | | ||
34 | + IllegalArgumentException | IllegalAccessException e) { | ||
35 | + throw new TestUtilsException("setField failed", e); | ||
36 | + } | ||
37 | + } | ||
38 | + | ||
39 | + /** | ||
40 | + * Gets the field, bypassing scope restriction. | ||
41 | + * | ||
42 | + * @param subject Object where the field belongs | ||
43 | + * @param fieldName name of the field to get | ||
44 | + * @return value of the field. | ||
45 | + * @param <T> subject type | ||
46 | + * @param <U> field value type | ||
47 | + * @throws TestUtilsException if there are reflection errors while getting | ||
48 | + * the field | ||
49 | + */ | ||
50 | + public static <T, U> U getField(T subject, String fieldName) | ||
51 | + throws TestUtilsException { | ||
52 | + try { | ||
53 | + @SuppressWarnings("unchecked") | ||
54 | + Class<T> clazz = (Class<T>) subject.getClass(); | ||
55 | + Field field = clazz.getDeclaredField(fieldName); | ||
56 | + field.setAccessible(true); | ||
57 | + | ||
58 | + @SuppressWarnings("unchecked") | ||
59 | + U result = (U) field.get(subject); | ||
60 | + return result; | ||
61 | + } catch (NoSuchFieldException | SecurityException | | ||
62 | + IllegalArgumentException | IllegalAccessException e) { | ||
63 | + throw new TestUtilsException("getField failed", e); | ||
64 | + } | ||
65 | + } | ||
66 | + | ||
67 | + /** | ||
68 | + * Calls the method, bypassing scope restriction. | ||
69 | + * | ||
70 | + * @param subject Object where the method belongs | ||
71 | + * @param methodName name of the method to call | ||
72 | + * @param paramTypes formal parameter type array | ||
73 | + * @param args arguments | ||
74 | + * @return return value or null if void | ||
75 | + * @param <T> subject type | ||
76 | + * @param <U> return value type | ||
77 | + * @throws TestUtilsException if there are reflection errors while calling | ||
78 | + * the method | ||
79 | + */ | ||
80 | + public static <T, U> U callMethod(T subject, String methodName, | ||
81 | + Class<?>[] paramTypes, Object...args) throws TestUtilsException { | ||
82 | + | ||
83 | + try { | ||
84 | + @SuppressWarnings("unchecked") | ||
85 | + Class<T> clazz = (Class<T>) subject.getClass(); | ||
86 | + final Method method; | ||
87 | + if (paramTypes == null || paramTypes.length == 0) { | ||
88 | + method = clazz.getDeclaredMethod(methodName); | ||
89 | + } else { | ||
90 | + method = clazz.getDeclaredMethod(methodName, paramTypes); | ||
91 | + } | ||
92 | + method.setAccessible(true); | ||
93 | + | ||
94 | + @SuppressWarnings("unchecked") | ||
95 | + U result = (U) method.invoke(subject, args); | ||
96 | + return result; | ||
97 | + } catch (NoSuchMethodException | SecurityException | | ||
98 | + IllegalAccessException | IllegalArgumentException | | ||
99 | + InvocationTargetException e) { | ||
100 | + throw new TestUtilsException("callMethod failed", e); | ||
101 | + } | ||
102 | + } | ||
103 | + | ||
104 | + /** | ||
105 | + * Calls the method, bypassing scope restriction. | ||
106 | + * | ||
107 | + * @param subject Object where the method belongs | ||
108 | + * @param methodName name of the method to call | ||
109 | + * @param paramType formal parameter type | ||
110 | + * @param arg argument | ||
111 | + * @return return value or null if void | ||
112 | + * @param <T> subject type | ||
113 | + * @param <U> return value type | ||
114 | + * @throws TestUtilsException if there are reflection errors while calling | ||
115 | + * the method | ||
116 | + */ | ||
117 | + public static <T, U> U callMethod(T subject, String methodName, | ||
118 | + Class<?> paramType, Object arg) throws TestUtilsException { | ||
119 | + return callMethod(subject, methodName, new Class<?>[]{paramType}, arg); | ||
120 | + } | ||
121 | + | ||
122 | + /** | ||
123 | + * Triggers an allocation of an object of type <T> and forces a call to | ||
124 | + * the private constructor. | ||
125 | + * | ||
126 | + * @param constructor Constructor to call | ||
127 | + * @param <T> type of the object to create | ||
128 | + * @return created object of type <T> | ||
129 | + * @throws TestUtilsException if there are reflection errors while calling | ||
130 | + * the constructor | ||
131 | + */ | ||
132 | + public static <T> T callConstructor(Constructor<T> constructor) | ||
133 | + throws TestUtilsException { | ||
134 | + try { | ||
135 | + constructor.setAccessible(true); | ||
136 | + return constructor.newInstance(); | ||
137 | + } catch (InstantiationException | IllegalAccessException | | ||
138 | + InvocationTargetException error) { | ||
139 | + throw new TestUtilsException("callConstructor failed", error); | ||
140 | + } | ||
141 | + } | ||
142 | + | ||
143 | + /** | ||
144 | + * Avoid instantiation. | ||
145 | + */ | ||
146 | + private TestUtils() {} | ||
147 | + | ||
148 | + /** | ||
149 | + * Exception that can be thrown if problems are encountered while executing | ||
150 | + * the utility method. These are usually problems accessing fields/methods | ||
151 | + * through reflection. The original exception can be found by examining the | ||
152 | + * cause. | ||
153 | + */ | ||
154 | + public static class TestUtilsException extends Exception { | ||
155 | + | ||
156 | + private static final long serialVersionUID = 1L; | ||
157 | + | ||
158 | + /** | ||
159 | + * Constructs a new exception with the specified detail message and | ||
160 | + * cause. | ||
161 | + * | ||
162 | + * @param message the detail message | ||
163 | + * @param cause the original cause of this exception | ||
164 | + */ | ||
165 | + public TestUtilsException(String message, Throwable cause) { | ||
166 | + super(message, cause); | ||
167 | + } | ||
168 | + } | ||
169 | +} |
1 | +package org.onlab.util; | ||
2 | + | ||
3 | +import static org.junit.Assert.assertArrayEquals; | ||
4 | +import static org.junit.Assert.assertEquals; | ||
5 | +import static org.junit.Assert.assertNull; | ||
6 | + | ||
7 | +import org.junit.Before; | ||
8 | +import org.junit.Test; | ||
9 | +import org.onlab.util.TestUtils.TestUtilsException; | ||
10 | + | ||
11 | +/** | ||
12 | + * Test and usage examples for TestUtils. | ||
13 | + */ | ||
14 | +public class TestUtilsTest { | ||
15 | + | ||
16 | + /** | ||
17 | + * Test data. | ||
18 | + */ | ||
19 | + private static final class TestClass { | ||
20 | + | ||
21 | + @SuppressWarnings("unused") | ||
22 | + private int privateField = 42; | ||
23 | + | ||
24 | + @SuppressWarnings("unused") | ||
25 | + protected int protectedField = 2501; // CHECKSTYLE IGNORE THIS LINE | ||
26 | + | ||
27 | + /** | ||
28 | + * Protected method with multiple argument. | ||
29 | + * | ||
30 | + * @param x simply returns | ||
31 | + * @param y not used | ||
32 | + * @return x | ||
33 | + */ | ||
34 | + @SuppressWarnings("unused") | ||
35 | + private int privateMethod(Number x, Long y) { | ||
36 | + return x.intValue(); | ||
37 | + } | ||
38 | + | ||
39 | + /** | ||
40 | + * Protected method with no argument. | ||
41 | + * | ||
42 | + * @return int | ||
43 | + */ | ||
44 | + @SuppressWarnings("unused") | ||
45 | + protected int protectedMethod() { | ||
46 | + return 42; | ||
47 | + } | ||
48 | + | ||
49 | + /** | ||
50 | + * Method returning array. | ||
51 | + * | ||
52 | + * @param ary random array | ||
53 | + * @return ary | ||
54 | + */ | ||
55 | + @SuppressWarnings("unused") | ||
56 | + private int[] arrayReturnMethod(int[] ary) { | ||
57 | + return ary; | ||
58 | + } | ||
59 | + | ||
60 | + /** | ||
61 | + * Method without return value. | ||
62 | + * | ||
63 | + * @param s ignored | ||
64 | + */ | ||
65 | + @SuppressWarnings("unused") | ||
66 | + private void voidMethod(String s) { | ||
67 | + System.out.println(s); | ||
68 | + } | ||
69 | + } | ||
70 | + | ||
71 | + private TestClass test; | ||
72 | + | ||
73 | + /** | ||
74 | + * Sets up the test fixture. | ||
75 | + */ | ||
76 | + @Before | ||
77 | + public void setUp() { | ||
78 | + test = new TestClass(); | ||
79 | + } | ||
80 | + | ||
81 | + /** | ||
82 | + * Example to access private field. | ||
83 | + * | ||
84 | + * @throws TestUtilsException TestUtils error | ||
85 | + */ | ||
86 | + @Test | ||
87 | + public void testSetGetPrivateField() throws TestUtilsException { | ||
88 | + | ||
89 | + assertEquals(42, TestUtils.getField(test, "privateField")); | ||
90 | + TestUtils.setField(test, "privateField", 0xDEAD); | ||
91 | + assertEquals(0xDEAD, TestUtils.getField(test, "privateField")); | ||
92 | + } | ||
93 | + | ||
94 | + /** | ||
95 | + * Example to access protected field. | ||
96 | + * | ||
97 | + * @throws TestUtilsException TestUtils error | ||
98 | + */ | ||
99 | + @Test | ||
100 | + public void testSetGetProtectedField() throws TestUtilsException { | ||
101 | + | ||
102 | + assertEquals(2501, TestUtils.getField(test, "protectedField")); | ||
103 | + TestUtils.setField(test, "protectedField", 0xBEEF); | ||
104 | + assertEquals(0xBEEF, TestUtils.getField(test, "protectedField")); | ||
105 | + } | ||
106 | + | ||
107 | + /** | ||
108 | + * Example to call private method and multiple parameters. | ||
109 | + * <p/> | ||
110 | + * It also illustrates that paramTypes must match declared type, | ||
111 | + * not the runtime types of arguments. | ||
112 | + * | ||
113 | + * @throws TestUtilsException TestUtils error | ||
114 | + */ | ||
115 | + @Test | ||
116 | + public void testCallPrivateMethod() throws TestUtilsException { | ||
117 | + | ||
118 | + int result = TestUtils.callMethod(test, "privateMethod", | ||
119 | + new Class<?>[] {Number.class, Long.class}, | ||
120 | + Long.valueOf(42), Long.valueOf(32)); | ||
121 | + assertEquals(42, result); | ||
122 | + } | ||
123 | + | ||
124 | + /** | ||
125 | + * Example to call protected method and no parameters. | ||
126 | + * | ||
127 | + * @throws TestUtilsException TestUtils error | ||
128 | + */ | ||
129 | + @Test | ||
130 | + public void testCallProtectedMethod() throws TestUtilsException { | ||
131 | + | ||
132 | + int result = TestUtils.callMethod(test, "protectedMethod", | ||
133 | + new Class<?>[] {}); | ||
134 | + assertEquals(42, result); | ||
135 | + } | ||
136 | + | ||
137 | + /** | ||
138 | + * Example to call method returning array. | ||
139 | + * <p/> | ||
140 | + * Note: It is not required to receive as Object. | ||
141 | + * Following is just verifying it is not Boxed arrays. | ||
142 | + * | ||
143 | + * @throws TestUtilsException TestUtils error | ||
144 | + */ | ||
145 | + @Test | ||
146 | + public void testCallArrayReturnMethod() throws TestUtilsException { | ||
147 | + | ||
148 | + int[] array = {1, 2, 3}; | ||
149 | + Object aryResult = TestUtils.callMethod(test, "arrayReturnMethod", | ||
150 | + new Class<?>[] {int[].class}, array); | ||
151 | + assertEquals(int[].class, aryResult.getClass()); | ||
152 | + assertArrayEquals(array, (int[]) aryResult); | ||
153 | + } | ||
154 | + | ||
155 | + | ||
156 | + /** | ||
157 | + * Example to call void returning method. | ||
158 | + * <p/> | ||
159 | + * Note: Return value will be null for void methods. | ||
160 | + * | ||
161 | + * @throws TestUtilsException TestUtils error | ||
162 | + */ | ||
163 | + @Test | ||
164 | + public void testCallVoidReturnMethod() throws TestUtilsException { | ||
165 | + | ||
166 | + Object voidResult = TestUtils.callMethod(test, "voidMethod", | ||
167 | + String.class, "foobar"); | ||
168 | + assertNull(voidResult); | ||
169 | + } | ||
170 | +} |
-
Please register or login to post a comment