1+ /*
2+ * Licensed to the Apache Software Foundation (ASF) under one
3+ * or more contributor license agreements. See the NOTICE file
4+ * distributed with this work for additional information
5+ * regarding copyright ownership. The ASF licenses this file
6+ * to you under the Apache License, Version 2.0 (the
7+ * "License"); you may not use this file except in compliance
8+ * with the License. You may obtain a copy of the License at
9+ *
10+ * http://www.apache.org/licenses/LICENSE-2.0
11+ *
12+ * Unless required by applicable law or agreed to in writing,
13+ * software distributed under the License is distributed on an
14+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+ * KIND, either express or implied. See the License for the
16+ * specific language governing permissions and limitations
17+ * under the License.
18+ */
19+ package org .apache .felix .bundlerepository .impl ;
20+
21+ import junit .framework .TestCase ;
22+ import org .apache .felix .utils .log .Logger ;
23+ import org .easymock .EasyMock ;
24+ import org .osgi .framework .*;
25+
26+ import java .util .Dictionary ;
27+ import java .util .Hashtable ;
28+
29+ public class LazyLocalResourceImplTest extends TestCase
30+ {
31+ public void testEquals () {
32+ // Create mock Bundles using the helper
33+ Bundle bundle1a = createMockBundle ("test.bundle" , "1.0.0" , 1L );
34+ Bundle bundle1b = createMockBundle ("test.bundle" , "1.0.0" , 2L ); // Different ID, same identity
35+ Bundle bundle1c = createMockBundle ("test.bundle" , "1.0.0" , 5L ); // For transitivity test
36+ Bundle bundle2 = createMockBundle ("test.bundle" , "1.1.0" , 3L ); // Different version
37+ Bundle bundle3 = createMockBundle ("another.bundle" , "1.0.0" , 4L ); // Different BSN
38+
39+ // Replay all mocks created by the helper
40+ EasyMock .replay (bundle1a , bundle1b , bundle1c , bundle2 , bundle3 );
41+
42+ // Create LazyLocalResourceImpl instances
43+ LazyLocalResourceImpl res1a = new LazyLocalResourceImpl (bundle1a , new Logger (bundle1a .getBundleContext ()));
44+ // Reference to the same object
45+ LazyLocalResourceImpl res1b = new LazyLocalResourceImpl (bundle1b , new Logger (bundle1b .getBundleContext ()));
46+ LazyLocalResourceImpl res1c = new LazyLocalResourceImpl (bundle1c , new Logger (bundle1c .getBundleContext ()));
47+ LazyLocalResourceImpl res2 = new LazyLocalResourceImpl (bundle2 , new Logger (bundle2 .getBundleContext ()));
48+ LazyLocalResourceImpl res3 = new LazyLocalResourceImpl (bundle3 , new Logger (bundle3 .getBundleContext ()));
49+
50+ // 1. Reflexive
51+ assertEquals ("A resource must be equal to itself." , res1a , res1a );
52+
53+ // 2. Symmetric
54+ assertEquals ("Resources with same BSN/Version should be equal (Symmetry Part 1)." , res1a , res1b );
55+ assertEquals ("Resources with same BSN/Version should be equal (Symmetry Part 2)." , res1b , res1a );
56+
57+ // 3. Transitive
58+ assertEquals ("res1b should be equal to res1c (Transitive premise 1)." , res1b , res1c );
59+ assertEquals ("res1a should be equal to res1c (Transitive conclusion)." , res1a , res1c );
60+
61+ // 4. Consistency (Implicitly tested by repeated calls)
62+ assertEquals ("Consistency check" , res1a , res1b );
63+
64+ // 5. Null comparison
65+ assertFalse ("Resource should not be equal to null." , res1a .equals (null ));
66+
67+ // 6. Different type comparison
68+ assertFalse ("Resource should not be equal to an object of a different type." , res1a .equals (new Object ()));
69+
70+ // 7. Inequality cases
71+ assertFalse ("Resources with different versions should not be equal." , res1a .equals (res2 ));
72+ assertFalse ("Resources with different symbolic names should not be equal." , res1a .equals (res3 ));
73+
74+ // 8. Test with lazy initialization interaction
75+ Bundle bundleLazyA = createMockBundle ("lazy.test" , "1.0" , 6L );
76+ Bundle bundleLazyB = createMockBundle ("lazy.test" , "1.0" , 7L );
77+ EasyMock .replay (bundleLazyA , bundleLazyB ); // Replay the lazy mocks
78+
79+ LazyLocalResourceImpl resLazyA = new LazyLocalResourceImpl (bundleLazyA , new Logger (bundleLazyA .getBundleContext ()));
80+ LazyLocalResourceImpl resLazyB = new LazyLocalResourceImpl (bundleLazyB , new Logger (bundleLazyB .getBundleContext ()));
81+
82+ // Check equality before internal Resource is initialized
83+ assertEquals ("Equality should hold even before explicit initialization." , resLazyA , resLazyB );
84+
85+ // Force initialization of resLazyA's internal resource
86+ assertNotNull ("Getting symbolic name should work" , resLazyA .getSymbolicName ());
87+ assertEquals ("Equality should hold after one resource is initialized." , resLazyA , resLazyB );
88+ assertEquals ("Symmetric equality should hold after one resource is initialized." , resLazyB , resLazyA );
89+
90+ // Force initialization of resLazyB's internal resource
91+ assertNotNull ("Getting version should work" , resLazyB .getVersion ());
92+ assertEquals ("Equality should hold after both resources are initialized." , resLazyA , resLazyB );
93+
94+ // Verify mocks - confirms expected methods were called on bundles
95+ EasyMock .verify (bundle1a , bundle1b , bundle1c , bundle2 , bundle3 , bundleLazyA , bundleLazyB );
96+ }
97+
98+ public void testHashCode () {
99+ // Create mock Bundles
100+ Bundle bundle1a = createMockBundle ("hash.bundle" , "1.0.0" , 10L );
101+ Bundle bundle1b = createMockBundle ("hash.bundle" , "1.0.0" , 11L ); // Should be equal to 1a
102+ Bundle bundle2 = createMockBundle ("hash.bundle" , "2.0.0" , 12L ); // Different version
103+ Bundle bundle3 = createMockBundle ("another.hash.bundle" , "1.0.0" , 13L ); // Different BSN
104+
105+ // Replay mocks
106+ EasyMock .replay (bundle1a , bundle1b , bundle2 , bundle3 );
107+
108+ LazyLocalResourceImpl res1a = new LazyLocalResourceImpl (bundle1a , new Logger (bundle1a .getBundleContext ()));
109+ LazyLocalResourceImpl res1b = new LazyLocalResourceImpl (bundle1b , new Logger (bundle1b .getBundleContext ()));
110+ LazyLocalResourceImpl res2 = new LazyLocalResourceImpl (bundle2 , new Logger (bundle2 .getBundleContext ()));
111+ LazyLocalResourceImpl res3 = new LazyLocalResourceImpl (bundle3 , new Logger (bundle3 .getBundleContext ()));
112+
113+ // 1. Consistency
114+ int hash1a_call1 = res1a .hashCode ();
115+ assertNotNull ("Getting properties should work" , res1a .getProperties ()); // Access a property to potentially trigger initialization
116+ int hash1a_call2 = res1a .hashCode ();
117+ assertEquals ("HashCode must be consistent across multiple invocations." , hash1a_call1 , hash1a_call2 );
118+
119+ // 2. Equality implies equal hash codes
120+ assertEquals ("Precondition failed: res1a should be equal to res1b." , res1a , res1b );
121+ assertEquals ("Equal objects must have equal hash codes." , res1a .hashCode (), res1b .hashCode ());
122+
123+ // Check inequality cases (hash codes *might* collide)
124+ assertFalse ("Precondition failed: res1a should not be equal to res2." , res1a .equals (res2 ));
125+ assertFalse ("Precondition failed: res1a should not be equal to res3." , res1a .equals (res3 ));
126+
127+ // 3. Test with lazy initialization interaction
128+ Bundle bundleLazyHash = createMockBundle ("lazy.hash" , "1.0" , 14L );
129+ EasyMock .replay (bundleLazyHash ); // Replay the lazy mock
130+
131+ LazyLocalResourceImpl resLazyHash = new LazyLocalResourceImpl (bundleLazyHash , new Logger (bundleLazyHash .getBundleContext ()));
132+
133+ int hashBeforeInit = resLazyHash .hashCode ();
134+ // Force initialization by accessing a property that delegates
135+ assertNotNull ("Getting capabilities should work" , resLazyHash .getCapabilities ());
136+ int hashAfterInit = resLazyHash .hashCode ();
137+
138+ assertEquals ("HashCode should be consistent before and after lazy initialization." , hashBeforeInit , hashAfterInit );
139+
140+ // Verify mocks
141+ EasyMock .verify (bundle1a , bundle1b , bundle2 , bundle3 , bundleLazyHash );
142+ }
143+
144+ private Bundle createMockBundle (String symbolicName , String versionString , long bundleId ) {
145+ Bundle mockBundle = EasyMock .createMock (Bundle .class );
146+ BundleContext mockContext = EasyMock .createNiceMock (BundleContext .class );
147+ Version version = (versionString != null ) ? Version .parseVersion (versionString ) : Version .emptyVersion ;
148+ Dictionary <String , String > headers = new Hashtable <>();
149+
150+ if (symbolicName != null ) {
151+ headers .put (Constants .BUNDLE_SYMBOLICNAME , symbolicName );
152+ // Add BSN directive if needed, assuming default visibility
153+ EasyMock .expect (mockBundle .getSymbolicName ()).andReturn (symbolicName ).anyTimes ();
154+ } else {
155+ EasyMock .expect (mockBundle .getSymbolicName ()).andReturn (null ).anyTimes ();
156+ }
157+ headers .put (Constants .BUNDLE_VERSION , version .toString ());
158+ headers .put (Constants .BUNDLE_MANIFESTVERSION , "2" ); // Often required by resource parsers
159+
160+ EasyMock .expect (mockBundle .getHeaders ()).andReturn (headers ).anyTimes ();
161+ EasyMock .expect (mockBundle .getHeaders (EasyMock .anyString ())).andReturn (headers ).anyTimes (); // Handle locale variant
162+ EasyMock .expect (mockBundle .getVersion ()).andReturn (version ).anyTimes ();
163+ EasyMock .expect (mockBundle .getBundleId ()).andReturn (bundleId ).anyTimes ();
164+ EasyMock .expect (mockBundle .getLocation ()).andReturn ("mock:/" + bundleId ).anyTimes (); // For Resource ID
165+ EasyMock .expect (mockBundle .getBundleContext ()).andReturn (mockContext ).anyTimes ();
166+ EasyMock .expect (mockBundle .getRegisteredServices ()).andReturn (new ServiceReference [0 ]).anyTimes ();
167+
168+ return mockBundle ;
169+ }
170+ }
0 commit comments