public void Update(string newContent, UpdateRange updateRange, int?version)
 {
     Version = version;
     Content = newContent;
     Lexer.Update(new VersionInstance(newContent), updateRange);
     Parse();
 }
예제 #2
0
        private async Task ValidateDefaultRostersSet(IByRoleTeamManager teamManager)
        {
            IResult <string> result = await this.Generator.TryUpdateRosters(teamManager, SheetsUri);

            Assert.IsTrue(result.Success, "Update should've succeeded");

            Assert.AreEqual(1, this.ClearedRanges.Count, "Unexpected number of clears");
            Assert.AreEqual($"'{UCSDGoogleSheetsGenerator.RostersSheetName}'!A2:G999", this.ClearedRanges[0], "Unexpected range");

            Assert.AreEqual(2, this.UpdatedRanges.Count, "Unexpected number of update ranges");

            UpdateRange updateRange = this.UpdatedRanges[0];

            Assert.AreEqual(
                $"'{UCSDGoogleSheetsGenerator.RostersSheetName}'!A2:C2",
                updateRange.Range,
                "Unexpected range for the first team");
            CollectionAssert.AreEquivalent(
                new string[] { FirstTeam, "Alice", "Alan" },
                updateRange.Values.ToArray(),
                "Unexpected row for the first team");

            updateRange = this.UpdatedRanges[1];
            Assert.AreEqual(
                $"'{UCSDGoogleSheetsGenerator.RostersSheetName}'!A3:B3",
                updateRange.Range,
                "Unexpected range for the second team");
            CollectionAssert.AreEquivalent(
                new string[] { SecondTeam, "Bob" },
                updateRange.Values.ToArray(),
                "Unexpected row for the second team");
        }
예제 #3
0
        public void Update(string newContent, UpdateRange updateRange, long version)
        {
            Version = version;
            Content = newContent;
            var increment = Lexer.Update(new VersionInstance(newContent), updateRange);

            Parse(increment);
        }
예제 #4
0
        public async Task Update(string spreadsheetId, UpdateRange updateRange)
        {
            string     rangeStr    = updateRange.Range.GetStr();
            ValueRange targetRange = new ValueRange();

            targetRange.Values         = updateRange.Data;
            targetRange.MajorDimension = "ROWS";

            SpreadsheetsResource.ValuesResource.UpdateRequest request =
                _service.Spreadsheets.Values.Update(targetRange, spreadsheetId, rangeStr);
            request.ValueInputOption = SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.USERENTERED;

            await autoAuthorize(request);
        }
예제 #5
0
        public async Task TryCreateScoresheetWithDeadTossups()
        {
            IByRoleTeamManager teamManager = CreateDefaultTeamManager();

            // Do something simple, then read the spreadsheet and verify some fields
            GameState game = new GameState()
            {
                Format      = Format.CreateTossupBonusesShootout(false),
                ReaderId    = 1,
                TeamManager = teamManager
            };

            await game.AddPlayer(2, "Alice");

            game.ScorePlayer(-5);
            game.NextQuestion();
            game.NextQuestion();

            await game.AddPlayer(4, "Bob");

            game.ScorePlayer(10);
            game.TryScoreBonus("0/10/0");

            IResult <string> result = await this.Generator.TryCreateScoresheet(game, SheetsUri, 1);

            Assert.IsTrue(result.Success, $"Failed: {(result.Success ? "" : result.ErrorMessage)}");

            // These checks are O(n^2), since we do Any for all of them. However, n is small here (~15), so it's not
            // too bad.

            // Tossups
            this.AssertInUpdateRange("'ROUND 1'!C4", "-5", "Couldn't find Alice's buzz");
            this.AssertInUpdateRange("'ROUND 1'!I4", "DT", "Couldn't find the first dead tossup marker");
            this.AssertInUpdateRange("'ROUND 1'!I5", "DT", "Couldn't find the second dead tossup marker");

            // Bonuses
            UpdateRange updateRange = this.UpdatedRanges
                                      .FirstOrDefault(valueRange => valueRange.Range == "'ROUND 1'!S6");

            Assert.IsNotNull(updateRange, "Couldn't find range for the bonus");
            CollectionAssert.AreEquivalent(
                new int[] { 10 },
                updateRange.Values.ToArray(),
                "Unexpected scoring for the bonus");
        }
예제 #6
0
        public async Task NoBonusesWrittenAfterBonusLimit()
        {
            IByRoleTeamManager teamManager = CreateDefaultTeamManager();

            GameState game = new GameState()
            {
                Format      = Format.CreateTossupBonusesShootout(false),
                ReaderId    = 1,
                TeamManager = teamManager
            };

            int lastBonusPhase = this.Generator.LastBonusRow - this.Generator.FirstPhaseRow;

            for (int i = 0; i < lastBonusPhase; i++)
            {
                await game.AddPlayer(2, "Alice");

                game.ScorePlayer(10);
                Assert.IsTrue(game.TryScoreBonus("0"), $"Scoring a bonus should've succeeded in phase {i}");
            }

            await game.AddPlayer(2, "Alice");

            game.ScorePlayer(15);
            Assert.IsTrue(game.TryScoreBonus("30"), $"Scoring a bonus should've succeeded in the last phase");

            IResult <string> result = await this.Generator.TryCreateScoresheet(game, SheetsUri, 1);

            Assert.IsTrue(result.Success, $"Creation should've succeeded at the limit.");

            UpdateRange updateRange = this.UpdatedRanges
                                      .FirstOrDefault(valueRange => valueRange.Range == $"'Round 1'!I{this.Generator.LastBonusRow}:K{this.Generator.LastBonusRow}");

            Assert.IsNotNull(updateRange, "Couldn't find range for the last bonus");
            CollectionAssert.AreEquivalent(
                new bool[] { true, true, true },
                updateRange.Values.ToArray(),
                "Unexpected scoring for the last bonus");

            updateRange = this.UpdatedRanges
                          .FirstOrDefault(valueRange => valueRange.Range == $"'Round 1'!I{this.Generator.LastBonusRow + 1}:K{this.Generator.LastBonusRow + 1}");
            Assert.IsNull(updateRange, "Bonus past the last bonus phase should'nt be exported");
        }
예제 #7
0
        public async Task TryCreateScoresheetWithDeadTossupsInTiebreakers()
        {
            IByRoleTeamManager teamManager = CreateDefaultTeamManager();

            // Do something simple, then read the spreadsheet and verify some fields
            GameState game = new GameState()
            {
                Format      = Format.CreateTossupBonusesShootout(false),
                ReaderId    = 1,
                TeamManager = teamManager
            };

            for (int i = 0; i < this.Generator.PhasesLimit - 1; i++)
            {
                game.NextQuestion();
            }

            await game.AddPlayer(2, "Alice");

            game.ScorePlayer(15);

            IResult <string> result = await this.Generator.TryCreateScoresheet(game, SheetsUri, 1);

            Assert.IsTrue(result.Success, $"Failed: {(result.Success ? "" : result.ErrorMessage)}");

            // These checks are O(n^2), since we do Any for all of them. However, n is small here (~15), so it's not
            // too bad.

            // Tossups
            // FirstRowPhase is included in the limit, so subtract 1 from what the row should be
            int buzzRow = this.Generator.PhasesLimit + this.Generator.FirstPhaseRow - 1;

            this.AssertInUpdateRange($"'ROUND 1'!C{buzzRow}", "15", "Couldn't find Alice's buzz");
            this.AssertInUpdateRange("'ROUND 1'!I4", "DT", "Couldn't find the first dead tossup marker");
            this.AssertInUpdateRange($"'ROUND 1'!I{this.Generator.LastBonusRow + 1}", "DT", "Couldn't find the tiebreaker dead tossup marker");

            // No bonus in the tie breaker
            UpdateRange updateRange = this.UpdatedRanges
                                      .FirstOrDefault(valueRange => valueRange.Range == $"'ROUND 1'!I{buzzRow}");

            Assert.IsNull(updateRange, "There shouldn't be an update for the bonus yet");
        }
예제 #8
0
        public void Update(VersionInstance newContent, UpdateRange updateRange)
        {
            IsPushCompleted = false;
            _lastTokenCount = Tokens.Count;
            AffectedAreaInfo affectedArea = GetAffectedArea(updateRange);

            // The number of lines
            int lineDelta   = NumberOfNewLines(updateRange.Text) - updateRange.Range.LineSpan();
            int columnDelta = NumberOfCharactersInLastLine(updateRange.Text) - updateRange.Range.ColumnSpan();

            int indexOffset = updateRange.Text.Length - (Content.IndexOf(updateRange.Range.End) - Content.IndexOf(updateRange.Range.Start));

            // Adjust token ranges.
            for (int i = affectedArea.EndingTokenIndex; i < Tokens.Count; i++)
            {
                if (updateRange.Range.End <= Tokens[i].Range.Start)
                {
                    // Use the old content for getting the update range index.
                    int s = Content.IndexOf(Tokens[i].Range.Start) + indexOffset,
                        e = Content.IndexOf(Tokens[i].Range.End) + indexOffset;

                    // Use the new content to update the positions.
                    newContent.UpdatePosition(Tokens[i].Range.Start, s);
                    newContent.UpdatePosition(Tokens[i].Range.End, e);
                }
            }

            _currentTokenPush = new IncrementalTokenInsert(this, affectedArea.StartingTokenIndex, affectedArea.EndingTokenIndex);
            CurrentController = new LexController(newContent.Text, _currentTokenPush);

            // Set start range
            CurrentController.Index  = affectedArea.StartIndex;
            CurrentController.Line   = newContent.GetLine(CurrentController.Index);
            CurrentController.Column = newContent.GetColumn(CurrentController.Index);

            Content = newContent;
            IncrementalChangeStart = affectedArea.StartingTokenIndex;
            IncrementalChangeEnd   = affectedArea.EndingTokenIndex;
        }
예제 #9
0
        public IncrementInfo Update(VersionInstance newContent, UpdateRange updateRange)
        {
            int lastTokenCount            = Tokens.Count;
            AffectedAreaInfo affectedArea = GetAffectedArea(updateRange);

            // The number of lines
            int lineDelta   = NumberOfNewLines(updateRange.Text) - updateRange.Range.LineSpan();
            int columnDelta = NumberOfCharactersInLastLine(updateRange.Text) - updateRange.Range.ColumnSpan();

            int indexOffset = updateRange.Text.Length - (Content.IndexOf(updateRange.Range.End) - Content.IndexOf(updateRange.Range.Start));

            // Adjust token ranges.
            for (int i = affectedArea.EndingTokenIndex; i < Tokens.Count; i++)
            {
                if (updateRange.Range.End <= Tokens[i].Range.Start)
                {
                    // Use the old content for getting the update range index.
                    int s = Content.IndexOf(Tokens[i].Range.Start) + indexOffset,
                        e = Content.IndexOf(Tokens[i].Range.End) + indexOffset;

                    // Use the new content to update the positions.
                    newContent.UpdatePosition(Tokens[i].Range.Start, s);
                    newContent.UpdatePosition(Tokens[i].Range.End, e);
                }
            }

            var           tokenInsert = new IncrementalTokenInsert(Tokens, affectedArea.StartingTokenIndex, affectedArea.EndingTokenIndex);
            LexController controller  = new LexController(newContent.Text, tokenInsert);

            // Set start range
            controller.Index  = affectedArea.StartIndex;
            controller.Line   = newContent.GetLine(controller.Index);
            controller.Column = newContent.GetColumn(controller.Index);

            controller.Match();
            Content = newContent;
            return(new IncrementInfo(affectedArea.StartingTokenIndex, affectedArea.EndingTokenIndex, Tokens.Count - lastTokenCount));
        }
예제 #10
0
        AffectedAreaInfo GetAffectedArea(UpdateRange updateRange)
        {
            // Get the range of the tokens overlapped.
            bool startSet = false;

            // The default starting range of the update range's start and end positions in the document.
            int updateStartIndex = Content.IndexOf(updateRange.Range.Start);

            int startIndex         = updateStartIndex; // The position where lexing will start.
            int startingTokenIndex = -1;               // The position in the token list where new tokens will be inserted into.
            int endingTokenIndex   = int.MaxValue;     // The token where lexing will end.

            // If there are no tokens or the update range preceeds the range of the first token, set the starting index to 0.
            if (Tokens.Count == 0 || updateRange.Range.End < Tokens[0].Range.Start)
            {
                startIndex = 0;
            }

            // Find the first token to the left of the update range and the first token to the right of the update range.
            // Set 'startIndex', 'startingTokenIndex' with the left token and 'endingTokenIndex' with the right token.
            //
            // In the event of a left-side or right-side token in relation to the update range is not found,
            // the default values of 'startIndex', 'startingTokenIndex', and 'endingTokenIndex' should handle it fine.
            for (int i = 0; i < Tokens.Count; i++)
            {
                // If the current token overlaps the update range, set startTokenIndex.
                if (Tokens[i].Range.DoOverlap(updateRange.Range))
                {
                    // Don't set the starting position and index again if it was already set via overlap.
                    // We use a seperate if rather than an && because the proceeding else-ifs cannot run if the token overlaps the update range.
                    if (!startSet)
                    {
                        // 'startIndex' cannot be higher than 'updateStartIndex'.
                        // Simply setting 'startIndex' to 'IndexOf...' may cause an issue in the common scenario:

                        // Character            : |1|2|3|4|5|6|7|
                        // Update start position:    x     x
                        // Token start position :        +     +

                        // startIndex will be 3, causing the characters between the first x and + to be skipped.
                        // So pick the lower of the 2 values.
                        startIndex = Math.Min(updateStartIndex, Content.IndexOf(Tokens[i].Range.Start));
                        // Set the starting token to the current token's index.
                        startingTokenIndex = i;
                        // Once an overlapping token is found, do not set 'startIndex' or 'startingTokenIndex' again.
                        startSet = true;
                    }
                }
                // Sometimes, there is no overlapping token. In that case, we use the closest token to the left of the update range.
                // We determine if a token is to the left by checking if the token's ending position is less than the update range's start position.
                //
                // If the overlapping token is not found,
                //   and the token is to the left of the update range,
                //   then set the 'startIndex' and 'startingTokenIndex'.
                //
                // This block will run every iteration until the if statement above executes.
                else if (!startSet && Tokens[i].Range.End < updateRange.Range.Start)
                {
                    // TODO: Since the end position of the token was already checked, doing Math.Min is probably redundant
                    // simply doing 'startIndex = IndexOf(Tokens[i].Range.Start)' will probably suffice.
                    startIndex = Math.Min(updateStartIndex, Content.IndexOf(Tokens[i].Range.Start));
                    // Set the starting token to the current token's index.
                    startingTokenIndex = i;
                }
                // If the token was overlapping, it would have been caught earlier.
                // This block will run once no more overlapping tokens are found.
                else if (startSet)
                {
                    // Subtract by 1 because i - 1 is the last token that overlaps with the update range.
                    endingTokenIndex = i;

                    // No more iterations are need once the end is found.
                    break;
                }
                // If there is no overlapping token, set the ending using the first token that is completely to the right of the update range.
                else if (!startSet && updateRange.Range.End < Tokens[i].Range.Start)
                {
                    // Set the token index where lexing will stop.
                    endingTokenIndex = i;

                    // No more iterations are need once the end is found.
                    break;
                }
            }

            return(new AffectedAreaInfo(startIndex, startingTokenIndex, endingTokenIndex));
        }
예제 #11
0
        public async Task TryCreateScoresheetSucceeds()
        {
            // Do something simple, then read the spreadsheet and verify some fields
            IByRoleTeamManager teamManager = CreateDefaultTeamManager();

            // Do something simple, then read the spreadsheet and verify some fields
            GameState game = new GameState()
            {
                Format      = Format.CreateTossupBonusesShootout(false),
                ReaderId    = 1,
                TeamManager = teamManager
            };

            await game.AddPlayer(2, "Alice");

            game.ScorePlayer(15);
            game.TryScoreBonus("10/0/10");

            await game.AddPlayer(3, "Alan");

            game.ScorePlayer(-5);
            await game.AddPlayer(4, "Bob");

            game.ScorePlayer(10);
            game.TryScoreBonus("0/10/0");

            IResult <string> result = await this.Generator.TryCreateScoresheet(game, SheetsUri, 1);

            Assert.IsTrue(result.Success, $"Failed: {(result.Success ? "" : result.ErrorMessage)}");

            // Assert we cleared these two fields
            Assert.IsTrue(this.ClearedRanges.Contains("'Round 1'!C4:H31"), "First team scores are not in the list of cleared ranges");
            Assert.IsTrue(this.ClearedRanges.Contains("'Round 1'!O4:T31"), "Second team scores are not in the list of cleared ranges");

            // Assert we cleared the second team and the players
            Assert.IsTrue(this.ClearedRanges.Contains("'Round 1'!O1:O1"), "Second team name wasn't cleared");
            Assert.IsTrue(this.ClearedRanges.Contains("'Round 1'!C3:H3"), "First team's player names weren't cleared");
            Assert.IsTrue(this.ClearedRanges.Contains("'Round 1'!O3:T3"), "Second team's player names weren't cleared");

            // These checks are O(n^2), since we do Any for all of them. However, n is small here (~15), so it's not
            // too bad.

            // Team names
            this.AssertInUpdateRange("'Round 1'!C1", FirstTeam, "Couldn't find first team");
            this.AssertInUpdateRange("'Round 1'!O1", SecondTeam, "Couldn't find second team");

            // Player names
            this.AssertInUpdateRange("'Round 1'!C3", "Alice", "Couldn't find Alice");
            this.AssertInUpdateRange("'Round 1'!D3", "Alan", "Couldn't find Alan");
            this.AssertInUpdateRange("'Round 1'!O3", "Bob", "Couldn't find Bob");

            // Tossups
            this.AssertInUpdateRange("'Round 1'!C4", "15", "Couldn't find Alice's buzz");
            this.AssertInUpdateRange("'Round 1'!D5", "-5", "Couldn't find Alan's buzz");
            this.AssertInUpdateRange("'Round 1'!O5", "10", "Couldn't find Bob's buzz");

            // Bonuses
            UpdateRange updateRange = this.UpdatedRanges
                                      .FirstOrDefault(valueRange => valueRange.Range == "'Round 1'!I4:K4");

            Assert.IsNotNull(updateRange, "Couldn't find range for the first bonus");
            CollectionAssert.AreEquivalent(
                new bool[] { true, false, true },
                updateRange.Values.ToArray(),
                "Unexpected scoring for the first bonus");

            updateRange = this.UpdatedRanges
                          .FirstOrDefault(valueRange => valueRange.Range == "'Round 1'!U5:W5");
            Assert.IsNotNull(updateRange, "Couldn't find range for the second bonus");
            CollectionAssert.AreEquivalent(
                new bool[] { false, true, false },
                updateRange.Values.ToArray(),
                "Unexpected scoring for the second bonus");
        }