1+ using System . Collections . Concurrent ;
2+ using System . Reflection ;
3+ using NHibernate . Proxy ;
4+ using NHibernate . Proxy . DynamicProxy ;
5+ using NUnit . Framework ;
6+
7+ namespace NHibernate . Test . NHSpecificTest . NH3954
8+ {
9+ [ TestFixture , Explicit ( "Demonstrates bug impact on cache, but which tests will fail is a bit unpredictable" ) ]
10+ public class ProxyCacheFixture
11+ {
12+ private ProxyCache _cache ;
13+ private ConcurrentDictionary < ProxyCacheEntry , System . Type > _internalCache ;
14+ private int _hashCode1 ;
15+ private int _hashCode2 ;
16+
17+ private static readonly FieldInfo InternalCacheField =
18+ typeof ( ProxyCache ) . GetField ( "cache" , BindingFlags . Static | BindingFlags . NonPublic ) ;
19+
20+ [ SetUp ]
21+ public void SetUp ( )
22+ {
23+ _cache = new ProxyCache ( ) ;
24+
25+ _internalCache = ( ConcurrentDictionary < ProxyCacheEntry , System . Type > ) InternalCacheField . GetValue ( null ) ;
26+
27+ _cache . StoreProxyType ( typeof ( Entity1FakeProxy ) , typeof ( Entity1 ) ) ;
28+ _cache . StoreProxyType ( typeof ( Entity2FakeProxy ) , typeof ( Entity2 ) , typeof ( INHibernateProxy ) ) ;
29+ _cache . StoreProxyType ( typeof ( Entity3FakeProxy ) , typeof ( Entity3 ) ) ;
30+ _cache . StoreProxyType ( typeof ( Entity4FakeProxy ) , typeof ( Entity4 ) , typeof ( IProxy ) ) ;
31+ _cache . StoreProxyType ( typeof ( Entity5FakeProxy ) , typeof ( Entity5 ) , typeof ( INHibernateProxy ) , typeof ( IProxy ) ) ;
32+
33+ // Artificially inject other entries with same hashcodes
34+ _hashCode1 = new ProxyCacheEntry ( typeof ( Entity1 ) , null ) . GetHashCode ( ) ;
35+ Inject ( new ProxyCacheEntry ( typeof ( Entity1 ) , new [ ] { typeof ( INHibernateProxy ) } ) , _hashCode1 , typeof ( Entity1FakeProxy2 ) ) ;
36+ Inject ( new ProxyCacheEntry ( typeof ( Entity3 ) , null ) , _hashCode1 , typeof ( Entity3FakeProxy2 ) ) ;
37+
38+ _hashCode2 = new ProxyCacheEntry ( typeof ( Entity2 ) , new [ ] { typeof ( INHibernateProxy ) } ) . GetHashCode ( ) ;
39+ Inject ( new ProxyCacheEntry ( typeof ( Entity2 ) , null ) , _hashCode2 , typeof ( Entity2FakeProxy2 ) ) ;
40+ Inject ( new ProxyCacheEntry ( typeof ( Entity4 ) , new [ ] { typeof ( IProxy ) } ) , _hashCode2 , typeof ( Entity4FakeProxy2 ) ) ;
41+ Inject ( new ProxyCacheEntry ( typeof ( Entity5 ) , new [ ] { typeof ( INHibernateProxy ) , typeof ( IProxy ) } ) , _hashCode2 , typeof ( Entity5FakeProxy2 ) ) ;
42+ }
43+
44+ private void Inject ( ProxyCacheEntry entryToTweak , int hashcode , System . Type result )
45+ {
46+ TweakEntry ( entryToTweak , hashcode ) ;
47+ _internalCache [ entryToTweak ] = result ;
48+ }
49+
50+ private static readonly FieldInfo HashCodeField =
51+ typeof ( ProxyCacheEntry ) . GetField ( "hashCode" , BindingFlags . Instance | BindingFlags . NonPublic ) ;
52+
53+ /// <summary>
54+ /// Allows to simulate a hashcode collision. Issue would be unpractical to test otherwise.
55+ /// Hashcode collision must be supported for avoiding unexpected and hard to reproduce failures.
56+ /// </summary>
57+ private void TweakEntry ( ProxyCacheEntry entryToTweak , int hashcode )
58+ {
59+ // Though hashCode is a readonly field, this works at the time of this writing. If it starts breaking and cannot be fixed,
60+ // ignore those tests or throw them away.
61+ HashCodeField . SetValue ( entryToTweak , hashcode ) ;
62+ }
63+
64+ [ Test ]
65+ public void ProxyCacheEntity1FakeProxy ( )
66+ {
67+ var result = _cache . GetProxyType ( typeof ( Entity1 ) ) ;
68+ Assert . AreEqual ( typeof ( Entity1FakeProxy ) , result ) ;
69+ }
70+
71+ [ Test ]
72+ public void ProxyCacheEntity1FakeProxy2 ( )
73+ {
74+ var entry = new ProxyCacheEntry ( typeof ( Entity1 ) , new [ ] { typeof ( INHibernateProxy ) } ) ;
75+ TweakEntry ( entry , _hashCode1 ) ;
76+ var result = _internalCache [ entry ] ;
77+ Assert . AreEqual ( typeof ( Entity1FakeProxy2 ) , result ) ;
78+ }
79+
80+ [ Test ]
81+ public void ProxyCacheEntity2FakeProxy ( )
82+ {
83+ var result = _cache . GetProxyType ( typeof ( Entity2 ) , typeof ( INHibernateProxy ) ) ;
84+ Assert . AreEqual ( typeof ( Entity2FakeProxy ) , result ) ;
85+ }
86+
87+ [ Test ]
88+ public void ProxyCacheEntity2FakeProxy2 ( )
89+ {
90+ var entry = new ProxyCacheEntry ( typeof ( Entity2 ) , null ) ;
91+ TweakEntry ( entry , _hashCode2 ) ;
92+ var result = _internalCache [ entry ] ;
93+ Assert . AreEqual ( typeof ( Entity2FakeProxy2 ) , result ) ;
94+ }
95+
96+ [ Test ]
97+ public void ProxyCacheEntity3FakeProxy ( )
98+ {
99+ var result = _cache . GetProxyType ( typeof ( Entity3 ) ) ;
100+ Assert . AreEqual ( typeof ( Entity3FakeProxy ) , result ) ;
101+ }
102+
103+ [ Test ]
104+ public void ProxyCacheEntity3FakeProxy2 ( )
105+ {
106+ var entry = new ProxyCacheEntry ( typeof ( Entity3 ) , null ) ;
107+ TweakEntry ( entry , _hashCode1 ) ;
108+ var result = _internalCache [ entry ] ;
109+ Assert . AreEqual ( typeof ( Entity3FakeProxy2 ) , result ) ;
110+ }
111+
112+ [ Test ]
113+ public void ProxyCacheEntity4FakeProxy ( )
114+ {
115+ var result = _cache . GetProxyType ( typeof ( Entity4 ) , typeof ( IProxy ) ) ;
116+ Assert . AreEqual ( typeof ( Entity4FakeProxy ) , result ) ;
117+ }
118+
119+ [ Test ]
120+ public void ProxyCacheEntity4FakeProxy2 ( )
121+ {
122+ var entry = new ProxyCacheEntry ( typeof ( Entity4 ) , new [ ] { typeof ( IProxy ) } ) ;
123+ TweakEntry ( entry , _hashCode2 ) ;
124+ var result = _internalCache [ entry ] ;
125+ Assert . AreEqual ( typeof ( Entity4FakeProxy2 ) , result ) ;
126+ }
127+
128+ [ Test ]
129+ public void ProxyCacheEntity5FakeProxy ( )
130+ {
131+ var result = _cache . GetProxyType ( typeof ( Entity5 ) , typeof ( IProxy ) , typeof ( INHibernateProxy ) ) ;
132+ Assert . AreEqual ( typeof ( Entity5FakeProxy ) , result ) ;
133+ }
134+
135+ [ Test ]
136+ public void ProxyCacheEntity5FakeProxy2 ( )
137+ {
138+ // Interfaces order inverted on purpose: must be supported.
139+ var entry = new ProxyCacheEntry ( typeof ( Entity5 ) , new [ ] { typeof ( IProxy ) , typeof ( INHibernateProxy ) } ) ;
140+ TweakEntry ( entry , _hashCode2 ) ;
141+ var result = _internalCache [ entry ] ;
142+ Assert . AreEqual ( typeof ( Entity5FakeProxy2 ) , result ) ;
143+ }
144+
145+ [ Test ]
146+ public void ProxyCacheNone ( )
147+ {
148+ // Beware not testing the lookup failure of any combination, even tweaked, actually added in cache.
149+ // (Otherwise the test may starts failing unexpectedly sometimes, as the original bug ...)
150+ // This one was not added in anyway.
151+ System . Type result ;
152+ Assert . IsFalse ( _cache . TryGetProxyType ( typeof ( Entity2 ) , new [ ] { typeof ( IProxy ) } , out result ) ) ;
153+ }
154+ }
155+ }
0 commit comments