public void Update(string newContent, UpdateRange updateRange, int?version) { Version = version; Content = newContent; Lexer.Update(new VersionInstance(newContent), updateRange); Parse(); }
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"); }
public void Update(string newContent, UpdateRange updateRange, long version) { Version = version; Content = newContent; var increment = Lexer.Update(new VersionInstance(newContent), updateRange); Parse(increment); }
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); }
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"); }
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"); }
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"); }
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; }
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)); }
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)); }
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"); }