55using System . Collections . Generic ;
66using System . Linq ;
77using System . Net . Http ;
8+ using System . Text . RegularExpressions ;
89using System . Threading . Tasks ;
910using NUnit . Framework ;
1011using Unity . Netcode . Runtime ;
@@ -15,14 +16,18 @@ namespace Unity.Netcode.RuntimeTests
1516{
1617 internal class HelpUrlTests
1718 {
18- private static readonly HttpClient k_HttpClient = new HttpClient ( ) ;
19+ private const string k_PackageName = "com.unity.netcode.gameobjects" ;
20+ private static readonly HttpClient k_HttpClient = new ( ) ;
21+
22+ private bool m_VerboseLogging = false ;
1923
2024 [ UnityTest ]
2125 public IEnumerator ValidateUrlsAreValid ( )
2226 {
2327 var names = new List < string > ( ) ;
2428 var allUrls = new List < string > ( ) ;
2529
30+ // GetFields() can only see public strings. Ensure each HelpUrl is public.
2631 foreach ( var constant in typeof ( HelpUrls ) . GetFields ( ) )
2732 {
2833 if ( constant . IsLiteral && ! constant . IsInitOnly )
@@ -31,12 +36,13 @@ public IEnumerator ValidateUrlsAreValid()
3136 allUrls . Add ( ( string ) constant . GetValue ( null ) ) ;
3237 }
3338 }
34- Debug . Log ( $ "Found { allUrls . Count } URLs") ;
39+
40+ VerboseLog ( $ "Found { allUrls . Count } URLs") ;
3541
3642 var tasks = new List < Task < bool > > ( ) ;
3743 foreach ( var url in allUrls )
3844 {
39- tasks . Add ( IsRemoteFileAvailable ( url ) ) ;
45+ tasks . Add ( AreUnityDocsAvailableAt ( url ) ) ;
4046 }
4147
4248 while ( tasks . Any ( task => ! task . IsCompleted ) )
@@ -46,7 +52,42 @@ public IEnumerator ValidateUrlsAreValid()
4652
4753 for ( int i = 0 ; i < allUrls . Count ; i ++ )
4854 {
49- Assert . IsTrue ( tasks [ i ] . Result , $ "HelpUrls.{ names [ i ] } is an invalid path!") ;
55+ Assert . IsTrue ( tasks [ i ] . Result , $ "HelpUrls.{ names [ i ] } has an invalid path! Path: { allUrls [ i ] } ") ;
56+ }
57+ }
58+
59+ private async Task < bool > AreUnityDocsAvailableAt ( string url )
60+ {
61+ try
62+ {
63+ var split = url . Split ( '#' ) ;
64+ url = split [ 0 ] ;
65+
66+ var stream = await GetContentFromRemoteFile ( url ) ;
67+
68+ var redirectUrl = CalculateRedirectURl ( url , stream ) ;
69+ VerboseLog ( $ "Calculated Redirect URL: { redirectUrl } ") ;
70+
71+ var content = await GetContentFromRemoteFile ( redirectUrl ) ;
72+
73+ // If original url had an anchor part (e.g. some/url.html#anchor)
74+ if ( split . Length > 1 )
75+ {
76+ var anchorString = split [ 1 ] ;
77+
78+ // All headings will have an id with the anchorstring (e.g. <h2 id="anchor">)
79+ if ( ! content . Contains ( $ "id=\" { anchorString } \" >") )
80+ {
81+ return false ;
82+ }
83+ }
84+
85+ return true ;
86+ }
87+ catch ( Exception e )
88+ {
89+ VerboseLog ( e . Message ) ;
90+ return false ;
5091 }
5192 }
5293
@@ -55,29 +96,68 @@ public IEnumerator ValidateUrlsAreValid()
5596 /// </summary>
5697 /// <param name="url">URL to a remote file.</param>
5798 /// <returns>True if the file at the <paramref name="url"/> is able to be downloaded, false if the file does not exist, or if the file is restricted.</returns>
58- private static async Task < bool > IsRemoteFileAvailable ( string url )
99+ private async Task < string > GetContentFromRemoteFile ( string url )
59100 {
60101 //Checking if URI is well formed is optional
61102 var uri = new Uri ( url ) ;
62103 if ( ! uri . IsWellFormedOriginalString ( ) )
63104 {
64- Debug . LogError ( $ "URL { url } is not well formed") ;
65- return false ;
105+ throw new Exception ( $ "URL { url } is not well formed") ;
66106 }
67107
68108 try
69109 {
70- using var request = new HttpRequestMessage ( HttpMethod . Head , uri ) ;
110+ using var request = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
71111 using var response = await k_HttpClient . SendAsync ( request ) ;
112+ if ( ! response . IsSuccessStatusCode || response . Content . Headers . ContentLength <= 0 )
113+ {
114+ throw new Exception ( $ "Failed to get remote file from URL { url } ") ;
115+ }
72116
73- var exists = response . IsSuccessStatusCode && response . Content . Headers . ContentLength > 0 ;
74- Debug . Log ( $ "url { url } returned status code { response . StatusCode } ") ;
75- return exists ;
117+ return await response . Content . ReadAsStringAsync ( ) ;
76118 }
77119 catch
78120 {
79- Debug . LogError ( $ "URL { url } request failed") ;
80- return false ;
121+ throw new Exception ( $ "URL { url } request failed") ;
122+ }
123+ }
124+
125+ private string CalculateRedirectURl ( string originalRequest , string content )
126+ {
127+ var uri = new Uri ( originalRequest ) ;
128+ var baseRequest = $ "{ uri . Scheme } ://{ uri . Host } ";
129+ foreach ( var segment in uri . Segments )
130+ {
131+ if ( segment . Contains ( k_PackageName ) )
132+ {
133+ break ;
134+ }
135+ baseRequest += segment ;
136+ }
137+
138+ var subfolderRegex = new Regex ( @"[?&](\w[\w.]*)=([^?&]+)" ) . Match ( uri . Query ) ;
139+ var subfolder = "" ;
140+ foreach ( Group match in subfolderRegex . Groups )
141+ {
142+ subfolder = match . Value ;
143+ }
144+
145+ string pattern = @"com.unity.netcode.gameobjects\@(\d+.\d+)" ;
146+ var targetDestination = "" ;
147+ foreach ( Match match in Regex . Matches ( content , pattern ) )
148+ {
149+ targetDestination = match . Value ;
150+ break ;
151+ }
152+
153+ return baseRequest + targetDestination + subfolder ;
154+ }
155+
156+ private void VerboseLog ( string message )
157+ {
158+ if ( m_VerboseLogging )
159+ {
160+ Debug . unityLogger . Log ( message ) ;
81161 }
82162 }
83163
0 commit comments