using NUnit.Framework; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.TestTools; public class HangmanPlaymodeTests { private HangmanController hangmanController; private int multiplayerConfirmInput = 4; /// /// SetupFunction to reach the hangman-game /// /// [UnitySetUp] public IEnumerator SetupFunction() { string path = $"{Application.persistentDataPath}/wesign_unit_test.json"; string oneUser = $"{{\"version\":1027,\"users\":[{{\"entries\":[],\"username\":\"TEST\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0}}"; File.WriteAllText(path, oneUser); PersistentDataController.PATH = path; PersistentDataController.GetInstance().Load(); AssetDatabase.LoadAssetAtPath("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake(); // Go to the minigame-selection scene to make sure that the minigameIndex is set correctly SystemController.GetInstance().LoadNextScene("Common/Scenes/ListMinigamesScreen"); yield return new WaitForSeconds(0.2f); var minigameScreen = GameObject.FindObjectOfType(); minigameScreen.minigameList.SetCurrentMinigame(MinigameIndex.HANGMAN); // Go to the Hangman-game SystemController.GetInstance().LoadNextScene("Hangman/Scenes/HangmanGame"); yield return new WaitForSeconds(0.2f); // Fetch the hangmanController hangmanController = GameObject.FindObjectOfType(); yield return new WaitForSeconds(0.2f); // Fetch the SignPredictor and deactivate it, this stops the basic game-loop from happening var signPredictor = GameObject.FindObjectOfType(); GameObject signPredictorController = signPredictor.gameObject; signPredictorController.SetActive(false); yield return new WaitForSeconds(0.2f); // Put some old scores in the scoreboard to test its functionality // One from one day ago Score score1 = new Score(); score1.scoreValue = 50; score1.time = DateTime.Now.AddDays(-1).ToString(); // One from one hour ago Score score2 = new Score(); score2.scoreValue = 70; score2.time = DateTime.Now.AddHours(-1).ToString(); // One from 5 minutes ago Score score3 = new Score(); score3.scoreValue = 90; score3.time = DateTime.Now.AddMinutes(-5).ToString(); // Save the new score var progress = UserList.GetCurrentUser().GetMinigameProgress(MinigameIndex.HANGMAN); // Get the current list of scores List latestScores = progress.latestScores; List highestScores = progress.highestScores; // Add the new score latestScores.Add(score1); highestScores.Add(score1); latestScores.Add(score2); highestScores.Add(score2); latestScores.Add(score3); highestScores.Add(score3); // Sort the scores highestScores.Sort((a, b) => b.scoreValue.CompareTo(a.scoreValue)); // Only save the top 10 scores, so this list doesn't keep growing endlessly progress.latestScores = latestScores.Take(10).ToList(); progress.highestScores = highestScores.Take(10).ToList(); PersistentDataController.GetInstance().Save(); } /// /// Tests the singleplayer functionality, only the winning side as losing is easier when the word is chosen beforehand. /// /// [UnityTest] public IEnumerator SinglePlayerTests() { // Start singlePlayer hangmanController.SinglePlayer(); // Assert that a random word was generated and that the game has started string currentWord = hangmanController.getCurrentWord(); Assert.IsTrue(currentWord.Length > 0); Assert.IsTrue(hangmanController.getCurrentMode() == 2); Assert.IsFalse(hangmanController.inputPanel.activeSelf); Assert.IsFalse(hangmanController.playerPanel.activeSelf); Assert.IsTrue(hangmanController.gamePanel.activeSelf); Assert.IsFalse(hangmanController.gameEndedPanel.activeSelf); // Fetch the panel with the info for the fields and check if the controller has implemented them var script = GameObject.FindObjectOfType(); Assert.IsTrue(hangmanController.inputTextField == script.guessesTextField); Assert.IsTrue(hangmanController.feedbackText == script.feedbackText); Assert.IsTrue(hangmanController.feedbackProgress == script.feedbackProgressBar); Assert.IsTrue(hangmanController.feedbackProgressImage == script.feedbackProgressImage); Assert.IsTrue(hangmanController.webcamScreen == script.webcamScreen); Assert.IsTrue(hangmanController.timerCircle == script.timerCircle); Assert.IsTrue(hangmanController.confirmPanel == script.confirmPanel); Assert.IsTrue(hangmanController.confirmText == script.confirmText); Assert.IsTrue(hangmanController.getCorrects() == 0); Assert.IsTrue(hangmanController.getWrongs() == 0); Assert.IsTrue(hangmanController.getUsedLetters().Length == 0); // Start guessing the word, start by trying a wrong letter to check if the image changes string letter = ""; int localWrongs = 0; // Wait to run over the update-functionality yield return new WaitForSeconds(0.2f); // Fetch a letter that isn't in the word foreach (char let in new List() { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }) { if (!currentWord.Contains(let.ToString().ToUpper())) { letter = let.ToString().ToUpper(); break; } } // Input this knowingly-wrong letter yield return ChooseLetter(letter); localWrongs++; // We know the letter is wrong, so check that the wrongs have increased Assert.IsTrue(hangmanController.getWrongs() == localWrongs); // Check all the correct letters Dictionary correctLetters = new Dictionary(); foreach (char let in currentWord.ToUpper()) { letter = let.ToString(); if (correctLetters.ContainsKey(letter)) { correctLetters[letter] += 1; } else { correctLetters.Add(letter, 1); } } int localCorrects = 0; // Now add each of the letters and check if the correct are correctly updated foreach (string s in correctLetters.Keys) { yield return ChooseLetter(s); localCorrects += correctLetters[s]; Assert.IsTrue(hangmanController.getCorrects() == localCorrects); } // The game should be over now, check if the gameEndedPanel is active Assert.IsTrue(hangmanController.gameEndedPanel.activeSelf); // Fetch the gameEndedPanel to check it's values var gameEndedScript = GameObject.FindObjectOfType(); Assert.IsTrue(gameEndedScript.endText.text == "GEWONNEN"); Assert.IsTrue(gameEndedScript.lettersRightText.text == hangmanController.getCorrects().ToString()); Assert.IsTrue(gameEndedScript.lettersWrongText.text == hangmanController.getWrongs().ToString()); Assert.IsTrue(gameEndedScript.lettersTotalText.text == (hangmanController.getCorrects() + hangmanController.getWrongs()).ToString()); yield return null; } /// /// Tests the multiplayer-code /// /// [UnityTest] public IEnumerator MultiPlayerTests() { // Go to the input-screen hangmanController.GoToInput(); // We will enter the word "testing" but will type a wrong letter after the 'i' that will be backspaced away // Afterwards, we can go to the game and assert that the currectWord is indeed "testing" // Correct letters yield return InputLetter("T"); yield return InputLetter("E"); yield return InputLetter("S"); yield return InputLetter("T"); yield return InputLetter("I"); // Wrong letter, will be backspaced away yield return InputLetter("V"); yield return new WaitForSeconds(0.2f); // We can't simulate a key-press so we will need to settle for just calling its logic hangmanController.BackSpacePressed(); // Wrong letter, but this one will be rejected while (hangmanController.getCurrentMode() != multiplayerConfirmInput) { hangmanController.ProcessMostProbableSign(0.0001f, "Q"); yield return new WaitForSeconds(0.2f); } hangmanController.ConfirmDeny(); // Final correct letters yield return InputLetter("N"); yield return InputLetter("G"); // We are satisfied with the word, pass it along hangmanController.TwoPlayer(); Assert.IsTrue(hangmanController.getCurrentWord() == "TESTING"); // Input 9 wrong letters to lose the game int localWrongs = 0; Assert.IsTrue(hangmanController.getWrongs() == localWrongs); foreach (string s in new List() { "A", "B", "C", "D", "F", "H", "J", "K", "L", "M", "N", "O", "P" }) { if (HangmanController.NUMBER_OF_FAILS_BEFORE_GAMEOVER >= localWrongs) { yield return ChooseLetter(s); localWrongs++; Assert.IsTrue(hangmanController.getWrongs() == localWrongs); } } // The game should be over now, resulting in a loss Assert.IsTrue(hangmanController.gameEndedPanel.activeSelf); // Fetch the gameEndedPanel to check it's values var gameEndedScript = GameObject.FindObjectOfType(); Assert.IsTrue(gameEndedScript.endText.text == "VERLOREN"); Assert.IsTrue(gameEndedScript.lettersRightText.text == hangmanController.getCorrects().ToString()); Assert.IsTrue(gameEndedScript.lettersWrongText.text == hangmanController.getWrongs().ToString()); Assert.IsTrue(gameEndedScript.lettersTotalText.text == (hangmanController.getCorrects() + hangmanController.getWrongs()).ToString()); yield return null; } /// /// Tests some remaning functionality regarding the feedback that isn't contained in the previous tests /// /// [UnityTest] public IEnumerator SignChangingTest() { hangmanController.SinglePlayer(); hangmanController.ProcessMostProbableSign(0.001f, "A"); yield return new WaitForSeconds(0.2f); // The sign for A has been held for 0.2f seconds... Assert.IsTrue(hangmanController.getCurrentTime() >= 0.2f && hangmanController.getCurrentTime() <= 0.3f); hangmanController.ProcessMostProbableSign(0.001f, "B"); yield return new WaitForSeconds(0.2f); // The sign changed so the time needs to be at 0.2f again Assert.IsTrue(hangmanController.getCurrentTime() >= 0.2f && hangmanController.getCurrentTime() <= 0.3f); hangmanController.ProcessMostProbableSign(1000, "B"); yield return new WaitForSeconds(0.2f); // The sign changed is way above the threshold, time is no longer tracked and is reset Assert.IsTrue(hangmanController.getCurrentTime() == 0.0); // Check that the time stays zero so long as the distance stays above the threshold Learnable C = hangmanController.fingerSpelling.learnables.Find((l) => l.name == "C"); float threshold = C.thresholdDistance; for (float i = 2 * threshold; i > threshold; i -= threshold / 6) { hangmanController.ProcessMostProbableSign(i, "C"); yield return new WaitForSeconds(0.01f); Assert.IsTrue(hangmanController.getCurrentTime() == 0.0); } // Check that the time rises above zero when you dip just below the threshold hangmanController.ProcessMostProbableSign(threshold - 0.01f, "C"); yield return new WaitForSeconds(0.01f); Assert.IsTrue(hangmanController.getCurrentTime() > 0.0); yield return null; } /// /// Test the functionality of the scoreboard to display multiple games /// /// [UnityTest] public IEnumerator ScoreBoardTest() { // Quickly play 2 games to test if scoreboard is correctly reset and if entries are correctly added // Game 1 hangmanController.GoToInput(); yield return InputLetter("A"); yield return InputLetter("A"); yield return InputLetter("A"); hangmanController.TwoPlayer(); Assert.IsTrue(hangmanController.getCurrentWord() == "AAA"); yield return ChooseLetter("A"); yield return new WaitForSeconds(1f); Assert.IsTrue(hangmanController.gameEndedPanel.activeSelf); var gameEndedScript = GameObject.FindObjectOfType(); int firstEntries = gameEndedScript.scoreboardEntriesContainer.childCount; hangmanController.StartController(); // Game 2 hangmanController.GoToInput(); yield return InputLetter("A"); yield return InputLetter("A"); yield return InputLetter("A"); hangmanController.TwoPlayer(); Assert.IsTrue(hangmanController.getCurrentWord() == "AAA"); yield return ChooseLetter("A"); yield return new WaitForSeconds(1f); Assert.IsTrue(hangmanController.gameEndedPanel.activeSelf); int secondEntries = gameEndedScript.scoreboardEntriesContainer.childCount; // Check that one entry has been added between the two games or that the scoreboard was already full Assert.IsTrue(secondEntries == firstEntries + 1 || firstEntries == 10); yield return null; } /// /// Cleanup after testing /// [TearDown] public void TearDown_HangmanTests() { PersistentDataController.PATH = null; } /// /// Private function to input a letter in multiplayer input-mode /// /// /// private IEnumerator InputLetter(string letter) { while (hangmanController.getCurrentMode() != multiplayerConfirmInput) { // Choose the letter by giving it a very small distance hangmanController.ProcessMostProbableSign(0.001f, letter); yield return new WaitForSeconds(0.2f); } hangmanController.ConfirmAccept(); } /// /// Private function to input a letter in game-mode /// /// /// private IEnumerator ChooseLetter(string letter) { while (!hangmanController.getUsedLetters().Contains(letter)) { // Choose the letter by giving it a very small distance hangmanController.ProcessMostProbableSign(0.001f, letter); yield return new WaitForSeconds(0.2f); } } }