Browse Source

Add test cases for ClientIsLatestVersion; refactored controller_test

Rod Hynes 10 years ago
parent
commit
6b19c4b895
1 changed files with 217 additions and 154 deletions
  1. 217 154
      psiphon/controller_test.go

+ 217 - 154
psiphon/controller_test.go

@@ -46,44 +46,177 @@ func TestMain(m *testing.M) {
 }
 
 // Note: untunneled upgrade tests must execute before
-// the "Run" tests to ensure no tunnel is established.
+// the other tests to ensure no tunnel is established.
 // We need a way to reset the datastore after it's been
 // initialized in order to to clear out its data entries
 // and be able to arbitrarily order the tests.
 
 func TestUntunneledUpgradeDownload(t *testing.T) {
-	doUntunnledUpgradeDownload(t, false)
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 "",
+			clientIsLatestVersion:    false,
+			disableUntunneledUpgrade: false,
+			disableEstablishing:      true,
+			disrupt:                  false,
+			useHostNameTransformer:   false,
+		})
 }
 
 func TestUntunneledResumableUpgradeDownload(t *testing.T) {
-	doUntunnledUpgradeDownload(t, true)
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 "",
+			clientIsLatestVersion:    false,
+			disableUntunneledUpgrade: false,
+			disableEstablishing:      true,
+			disrupt:                  true,
+			useHostNameTransformer:   false,
+		})
+}
+
+func TestUntunneledUpgradeClientIsLatestVersion(t *testing.T) {
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 "",
+			clientIsLatestVersion:    true,
+			disableUntunneledUpgrade: false,
+			disableEstablishing:      true,
+			disrupt:                  false,
+			useHostNameTransformer:   false,
+		})
+}
+
+func TestTunneledUpgradeClientIsLatestVersion(t *testing.T) {
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 "",
+			clientIsLatestVersion:    true,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   false,
+		})
 }
 
 func TestControllerRunSSH(t *testing.T) {
-	controllerRun(t, TUNNEL_PROTOCOL_SSH, false)
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 TUNNEL_PROTOCOL_SSH,
+			clientIsLatestVersion:    false,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   false,
+		})
 }
 
 func TestControllerRunObfuscatedSSH(t *testing.T) {
-	controllerRun(t, TUNNEL_PROTOCOL_OBFUSCATED_SSH, false)
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 TUNNEL_PROTOCOL_OBFUSCATED_SSH,
+			clientIsLatestVersion:    false,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   false,
+		})
 }
 
 func TestControllerRunUnfrontedMeek(t *testing.T) {
-	controllerRun(t, TUNNEL_PROTOCOL_UNFRONTED_MEEK, true)
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 TUNNEL_PROTOCOL_UNFRONTED_MEEK,
+			clientIsLatestVersion:    false,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   false,
+		})
+}
+
+func TestControllerRunUnfrontedMeekWithTransformer(t *testing.T) {
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 TUNNEL_PROTOCOL_UNFRONTED_MEEK,
+			clientIsLatestVersion:    true,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   true,
+		})
 }
 
 func TestControllerRunFrontedMeek(t *testing.T) {
-	controllerRun(t, TUNNEL_PROTOCOL_FRONTED_MEEK, true)
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 TUNNEL_PROTOCOL_FRONTED_MEEK,
+			clientIsLatestVersion:    false,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   false,
+		})
+}
+
+func TestControllerRunFrontedMeekWithTransformer(t *testing.T) {
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 TUNNEL_PROTOCOL_FRONTED_MEEK,
+			clientIsLatestVersion:    true,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   true,
+		})
 }
 
-func TestControllerRunFrontedMeekHTTP(t *testing.T) {
-	controllerRun(t, TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP, false)
+func TestControllerFrontedMeekHTTP(t *testing.T) {
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP,
+			clientIsLatestVersion:    true,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   false,
+		})
 }
 
 func TestControllerRunUnfrontedMeekHTTPS(t *testing.T) {
-	controllerRun(t, TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS, true)
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS,
+			clientIsLatestVersion:    false,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   false,
+		})
 }
 
-func doUntunnledUpgradeDownload(t *testing.T, disrupt bool) {
+func TestControllerRunUnfrontedMeekHTTPSWithTransformer(t *testing.T) {
+	controllerRun(t,
+		&controllerRunConfig{
+			protocol:                 TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS,
+			clientIsLatestVersion:    true,
+			disableUntunneledUpgrade: true,
+			disableEstablishing:      false,
+			disrupt:                  false,
+			useHostNameTransformer:   true,
+		})
+}
+
+type controllerRunConfig struct {
+	protocol                 string
+	clientIsLatestVersion    bool
+	disableUntunneledUpgrade bool
+	disableEstablishing      bool
+	disrupt                  bool
+	useHostNameTransformer   bool
+}
+
+func controllerRun(t *testing.T, runConfig *controllerRunConfig) {
 
 	configFileContents, err := ioutil.ReadFile("controller_test.config")
 	if err != nil {
@@ -95,143 +228,32 @@ func doUntunnledUpgradeDownload(t *testing.T, disrupt bool) {
 		t.Fatalf("error processing configuration file: %s", err)
 	}
 
-	if disrupt {
-		config.UpstreamProxyUrl = disruptorProxyURL
+	if runConfig.clientIsLatestVersion {
+		config.ClientVersion = "999999999"
 	}
 
-	// Clear remote server list so tunnel cannot be established and
-	// untunneled upgrade download case is tested.
-	config.RemoteServerListUrl = ""
-
-	os.Remove(config.UpgradeDownloadFilename)
-
-	err = InitDataStore(config)
-	if err != nil {
-		t.Fatalf("error initializing datastore: %s", err)
-	}
-
-	controller, err := NewController(config)
-	if err != nil {
-		t.Fatalf("error creating controller: %s", err)
-	}
-
-	upgradeDownloaded := make(chan struct{}, 1)
-
-	var clientUpgradeDownloadedBytesCount int32
-
-	SetNoticeOutput(NewNoticeReceiver(
-		func(notice []byte) {
-			// TODO: log notices without logging server IPs:
-			// fmt.Fprintf(os.Stderr, "%s\n", string(notice))
-			noticeType, payload, err := GetNotice(notice)
-			if err != nil {
-				return
-			}
-			switch noticeType {
-			case "Tunnels":
-				count := int(payload["count"].(float64))
-				if count > 0 {
-					// TODO: wrong goroutine for t.FatalNow()
-					t.Fatalf("tunnel established unexpectedly")
-				}
-			case "ClientUpgradeDownloadedBytes":
-				atomic.AddInt32(&clientUpgradeDownloadedBytesCount, 1)
-				t.Logf("ClientUpgradeDownloadedBytes: %d", int(payload["bytes"].(float64)))
-			case "ClientUpgradeDownloaded":
-				select {
-				case upgradeDownloaded <- *new(struct{}):
-				default:
-				}
-			}
-		}))
-
-	// Run controller
-
-	shutdownBroadcast := make(chan struct{})
-	controllerWaitGroup := new(sync.WaitGroup)
-	controllerWaitGroup.Add(1)
-	go func() {
-		defer controllerWaitGroup.Done()
-		controller.Run(shutdownBroadcast)
-	}()
-
-	defer func() {
-		// Test: shutdown must complete within 10 seconds
-
-		close(shutdownBroadcast)
-
-		shutdownTimeout := time.NewTimer(10 * time.Second)
-
-		shutdownOk := make(chan struct{}, 1)
-		go func() {
-			controllerWaitGroup.Wait()
-			shutdownOk <- *new(struct{})
-		}()
-
-		select {
-		case <-shutdownOk:
-		case <-shutdownTimeout.C:
-			t.Fatalf("controller shutdown timeout exceeded")
-		}
-	}()
-
-	// Test: upgrade must be downloaded within 120 seconds
-
-	downloadTimeout := time.NewTimer(120 * time.Second)
-
-	select {
-	case <-upgradeDownloaded:
-		// TODO: verify downloaded file
-
-	case <-downloadTimeout.C:
-		t.Fatalf("upgrade download timeout exceeded")
+	if runConfig.disableEstablishing {
+		// Clear remote server list so tunnel cannot be established.
+		// TODO: also delete all server entries in the datastore.
+		config.RemoteServerListUrl = ""
 	}
 
-	// Test: with disrupt, must be multiple download progress notices
-
-	if disrupt {
-		count := atomic.LoadInt32(&clientUpgradeDownloadedBytesCount)
-		if count <= 1 {
-			t.Fatalf("unexpected upgrade download progress: %d", count)
-		}
+	if runConfig.disableUntunneledUpgrade {
+		// Disable untunneled upgrade downloader to ensure tunneled case is tested
+		config.UpgradeDownloadClientVersionHeader = ""
 	}
-}
-
-type TestHostNameTransformer struct {
-}
-
-func (TestHostNameTransformer) TransformHostName(string) (string, bool) {
-	return "example.com", true
-}
 
-func controllerRun(t *testing.T, protocol string, protocolUsesHostNameTransformer bool) {
-	doControllerRun(t, protocol, nil)
-	if protocolUsesHostNameTransformer {
-		t.Log("running with testHostNameTransformer")
-		doControllerRun(t, protocol, &TestHostNameTransformer{})
+	if runConfig.disrupt {
+		config.UpstreamProxyUrl = disruptorProxyURL
 	}
-}
 
-func doControllerRun(t *testing.T, protocol string, hostNameTransformer HostNameTransformer) {
-
-	configFileContents, err := ioutil.ReadFile("controller_test.config")
-	if err != nil {
-		// Skip, don't fail, if config file is not present
-		t.Skipf("error loading configuration file: %s", err)
-	}
-	config, err := LoadConfig(configFileContents)
-	if err != nil {
-		t.Fatalf("error processing configuration file: %s", err)
+	if runConfig.useHostNameTransformer {
+		config.HostNameTransformer = &TestHostNameTransformer{}
 	}
 
-	// Disable untunneled upgrade downloader to ensure tunneled case is tested
-	config.UpgradeDownloadClientVersionHeader = ""
-
 	os.Remove(config.UpgradeDownloadFilename)
 
-	config.TunnelProtocol = protocol
-
-	config.HostNameTransformer = hostNameTransformer
+	config.TunnelProtocol = runConfig.protocol
 
 	err = InitDataStore(config)
 	if err != nil {
@@ -252,6 +274,9 @@ func doControllerRun(t *testing.T, protocol string, hostNameTransformer HostName
 
 	tunnelEstablished := make(chan struct{}, 1)
 	upgradeDownloaded := make(chan struct{}, 1)
+	confirmedLatestVersion := make(chan struct{}, 1)
+
+	var clientUpgradeDownloadedBytesCount int32
 
 	SetNoticeOutput(NewNoticeReceiver(
 		func(notice []byte) {
@@ -265,23 +290,34 @@ func doControllerRun(t *testing.T, protocol string, hostNameTransformer HostName
 			case "Tunnels":
 				count := int(payload["count"].(float64))
 				if count > 0 {
-					select {
-					case tunnelEstablished <- *new(struct{}):
-					default:
+					if runConfig.disableEstablishing {
+						// TODO: wrong goroutine for t.FatalNow()
+						t.Fatalf("tunnel established unexpectedly")
+					} else {
+						select {
+						case tunnelEstablished <- *new(struct{}):
+						default:
+						}
 					}
 				}
 			case "ClientUpgradeDownloadedBytes":
+				atomic.AddInt32(&clientUpgradeDownloadedBytesCount, 1)
 				t.Logf("ClientUpgradeDownloadedBytes: %d", int(payload["bytes"].(float64)))
 			case "ClientUpgradeDownloaded":
 				select {
 				case upgradeDownloaded <- *new(struct{}):
 				default:
 				}
+			case "ClientIsLatestVersion":
+				select {
+				case confirmedLatestVersion <- *new(struct{}):
+				default:
+				}
 			case "ListeningHttpProxyPort":
 				httpProxyPort = int(payload["port"].(float64))
 			case "ConnectingServer":
 				serverProtocol := payload["protocol"]
-				if serverProtocol != protocol {
+				if runConfig.protocol != "" && serverProtocol != runConfig.protocol {
 					// TODO: wrong goroutine for t.FatalNow()
 					t.Fatalf("wrong protocol selected: %s", serverProtocol)
 				}
@@ -318,34 +354,61 @@ func doControllerRun(t *testing.T, protocol string, hostNameTransformer HostName
 		}
 	}()
 
-	// Test: tunnel must be established within 60 seconds
+	if !runConfig.disableEstablishing {
 
-	establishTimeout := time.NewTimer(60 * time.Second)
+		// Test: tunnel must be established within 60 seconds
 
-	select {
-	case <-tunnelEstablished:
+		establishTimeout := time.NewTimer(60 * time.Second)
 
-	case <-establishTimeout.C:
-		t.Fatalf("tunnel establish timeout exceeded")
-	}
+		select {
+		case <-tunnelEstablished:
+
+		case <-establishTimeout.C:
+			t.Fatalf("tunnel establish timeout exceeded")
+		}
 
-	// Allow for known race condition described in NewHttpProxy():
-	time.Sleep(1 * time.Second)
+		// Test: fetch website through tunnel
 
-	// Test: fetch website through tunnel
-	fetchWebsite(t, httpProxyPort)
+		// Allow for known race condition described in NewHttpProxy():
+		time.Sleep(1 * time.Second)
+		fetchWebsite(t, httpProxyPort)
+	}
 
-	// Test: upgrade must be downloaded within 60 seconds
+	// Test: upgrade check/download must be downloaded within 120 seconds
 
-	downloadTimeout := time.NewTimer(60 * time.Second)
+	upgradeTimeout := time.NewTimer(120 * time.Second)
 
 	select {
 	case <-upgradeDownloaded:
 		// TODO: verify downloaded file
+		if runConfig.clientIsLatestVersion {
+			t.Fatalf("upgrade downloaded unexpectedly")
+		}
+
+	case <-confirmedLatestVersion:
+		if !runConfig.clientIsLatestVersion {
+			t.Fatalf("confirmed latest version unexpectedly")
+		}
 
-	case <-downloadTimeout.C:
+	case <-upgradeTimeout.C:
 		t.Fatalf("upgrade download timeout exceeded")
 	}
+
+	// Test: with disrupt, must be multiple download progress notices
+
+	if runConfig.disrupt && !runConfig.clientIsLatestVersion {
+		count := atomic.LoadInt32(&clientUpgradeDownloadedBytesCount)
+		if count <= 1 {
+			t.Fatalf("unexpected upgrade download progress: %d", count)
+		}
+	}
+}
+
+type TestHostNameTransformer struct {
+}
+
+func (TestHostNameTransformer) TransformHostName(string) (string, bool) {
+	return "example.com", true
 }
 
 func fetchWebsite(t *testing.T, httpProxyPort int) {