def test_invalid_arg_fails(self): with self.assertRaises(TypeError): Cell(None) with self.assertRaises(TypeError): Cell(False) with self.assertRaises(TypeError): Cell(True)
def test_singleton_columns(self): with self.assertRaises(TypeError): Cell('A0') self.assertEqual(Cell('A').posn(), (0,0)) self.assertEqual(Cell('B').posn(), (1,0)) self.assertEqual(Cell('AAA1').posn(), (702,0))
def test(self): self.assertEqual(eval_term('123', dict(), set()), Cell(CellType.NUMBER, 123)) sheet = {1: {'A': Cell(CellType.NUMBER, 100)}} self.assertEqual(eval_term('A1', sheet, set()), Cell(CellType.NUMBER, 100))
def test_expression_cell(self): self.assertEqual(parse_cell('=1+2+b1'), Cell(CellType.EXPRESSION, '1+2+b1')) self.assertEqual(parse_cell('=100+2+b1'), Cell(CellType.EXPRESSION, '100+2+b1')) self.assertEqual(parse_cell('=1'), Cell(CellType.EXPRESSION, '1')) self.assertEqual(parse_cell('=b1'), Cell(CellType.EXPRESSION, 'b1'))
def test_update_no_force(self): c = Cell('A1') d = Cell('B100') self.assertEqual(c._internal(), (0, 0)) self.assertEqual(c.name(), 'A1') c.update_from(d) self.assertEqual(c._internal(), (0, 0)) self.assertEqual(c.name(), 'A1')
def test_update_column_name_and_row_number(self): c = Cell('') d = Cell('B100') self.assertEqual(c._internal(), (None, None)) self.assertEqual(c.name(), 'A1') c.update_from(d) self.assertEqual(c._internal(), (1, 99)) self.assertEqual(c.name(), 'B100')
def test_update_column_name(self): c = Cell('1') d = Cell('B100') self.assertEqual(c._internal(), (None, 0)) self.assertEqual(c.name(), 'A1') c.update_from(d) self.assertEqual(c._internal(), (1, 0)) self.assertEqual(c.name(), 'B1')
def test_update_row_number(self): c = Cell('A') d = Cell('B100') self.assertEqual(c._internal(), (0, None)) self.assertEqual(c.name(), 'A1') c.update_from(d) self.assertEqual(c._internal(), (0, 99)) self.assertEqual(c.name(), 'A100')
def test_format(self): sheet = { 2: { 'B': Cell(CellType.NUMBER, 123), 'A': Cell(CellType.STRING, 'qwe') }, 1: { 'A': Cell(CellType.ERROR, errors.CIRCULAR_DEP), 'B': Cell(CellType.NONE, '') } } formatted_sheet = (f'{errors.CIRCULAR_DEP}\t\n' f'qwe\t123\n') self.assertEqual(format_sheet(sheet), formatted_sheet)
def test_set_name_dollars_ignored(self): self.assertEqual(Cell('$').posn(), (0,0)) self.assertEqual(Cell(' $ ').posn(), (0,0)) with self.assertRaises(TypeError): Cell('$0') with self.assertRaises(TypeError): Cell('$A$0') self.assertEqual(Cell('$A').posn(), (0,0)) self.assertEqual(Cell('$1').posn(), (0,0)) self.assertEqual(Cell('$A$1').posn(), (0,0)) self.assertEqual(Cell('$B$2').posn(), (1,1))
def eval_cell(sheet: Sheet, row: int, column: str, visited: set) -> Cell: """Calculate specified cell of sheet""" cell_address = column + str(row) if cell_address in visited: return Cell(CellType.ERROR, errors.CIRCULAR_DEP) visited.add(cell_address) try: cell = sheet[row][column] except KeyError: return Cell(CellType.ERROR, errors.NONEXISTENT_CELL) # Return already evaluated cell if cell.type != CellType.EXPRESSION: return cell operations, operands = get_operations_and_operands(cell) if len(operands) == 1: # No need to check single term in expression return eval_term(operands[0], sheet, visited) cell_value = 0 # '+' is dummy operation for first operand for operation, operand in zip(['+'] + operations, operands): evaluated_cell = eval_term(operand, sheet, visited) if evaluated_cell.type == CellType.ERROR: # Set error message for cell # if one of referenced cells contains error return evaluated_cell elif (evaluated_cell.type == CellType.STRING or evaluated_cell.type == CellType.NONE): return Cell(CellType.ERROR, errors.WRONG_ARG) try: cell_value = OPERATION_FUNCS[operation](cell_value, evaluated_cell.value) except ZeroDivisionError: return Cell(CellType.ERROR, errors.ZERO_DIV) return Cell(CellType.NUMBER, cell_value)
def test_arg_count(self): #0 args try: Cell() except TypeError: self.fail("Cell: raised TypeError unexpectedly (0 args)") #1 arg try: Cell('A1') except TypeError: self.fail("Cell: raised TypeError unexpectedly (1 arg)") #2 args try: Cell(1,2) except TypeError: self.fail("Cell: raised TypeError unexpectedly (2 args)") #>2 args with self.assertRaises(TypeError): Cell(1,2,3)
def test_error_cell(self): self.assertEqual(parse_cell('\t'), Cell(CellType.ERROR, errors.INVALID_SYNTAX)) self.assertEqual(parse_cell('string'), Cell(CellType.ERROR, errors.INVALID_SYNTAX)) self.assertEqual(parse_cell('-42'), Cell(CellType.ERROR, errors.INVALID_SYNTAX)) self.assertEqual(parse_cell(' '), Cell(CellType.ERROR, errors.INVALID_SYNTAX)) self.assertEqual(parse_cell('='), Cell(CellType.ERROR, errors.INVALID_SYNTAX)) self.assertEqual(parse_cell('=a1-'), Cell(CellType.ERROR, errors.INVALID_SYNTAX)) self.assertEqual(parse_cell('=-1'), Cell(CellType.ERROR, errors.INVALID_SYNTAX)) self.assertEqual(parse_cell('=a+b'), Cell(CellType.ERROR, errors.INVALID_SYNTAX)) self.assertEqual(parse_cell('=/3'), Cell(CellType.ERROR, errors.INVALID_SYNTAX))
def parse_cell(cell: str) -> Cell: """Parse cell string and return corresponding Cell object""" if not cell: return Cell(CellType.NONE, '') elif cell[0] == '\'': return Cell(CellType.STRING, cell[1:]) elif cell[0] == '=': if not is_valid_expression(cell): return Cell(CellType.ERROR, errors.INVALID_SYNTAX) return Cell(CellType.EXPRESSION, cell[1:]) elif cell.isdigit(): return Cell(CellType.NUMBER, int(cell)) else: return Cell(CellType.ERROR, errors.INVALID_SYNTAX)
def test_comparison_different(self): c = Cell('A1') d = Cell('A2') self.assertFalse(c == d) self.assertTrue(c != d)
def test_number_cell(self): self.assertEqual(parse_cell('100500'), Cell(CellType.NUMBER, 100500))
def test_string_cell(self): self.assertEqual(parse_cell('\'string'), Cell(CellType.STRING, 'string'))
def test_update_force(self): c = Cell('A1') d = Cell('B100') self.assertEqual(c.name(), 'A1') c.update_from(d, force=True) self.assertEqual(c.name(), 'B100')
def test(self): input_sheet = { 1: { 'A': Cell(CellType.NUMBER, 123), 'B': Cell(CellType.EXPRESSION, '23+a2-73/C2*13'), 'C': Cell(CellType.ERROR, errors.INVALID_SYNTAX) }, 2: { 'A': Cell(CellType.EXPRESSION, 'g1'), 'B': Cell(CellType.STRING, '=123+b2'), 'C': Cell(CellType.NUMBER, 6) }, 3: { 'A': Cell(CellType.EXPRESSION, '1/b2'), 'B': Cell(CellType.EXPRESSION, '1/0'), 'C': Cell(CellType.STRING, 'ok') } } output_sheet = { 1: { 'A': Cell(CellType.NUMBER, 123), 'B': Cell(CellType.ERROR, errors.NONEXISTENT_CELL), 'C': Cell(CellType.ERROR, errors.INVALID_SYNTAX) }, 2: { 'A': Cell(CellType.ERROR, errors.NONEXISTENT_CELL), 'B': Cell(CellType.STRING, '=123+b2'), 'C': Cell(CellType.NUMBER, 6) }, 3: { 'A': Cell(CellType.ERROR, errors.WRONG_ARG), 'B': Cell(CellType.ERROR, errors.ZERO_DIV), 'C': Cell(CellType.STRING, 'ok') } } self.assertEqual(eval_sheet(input_sheet), output_sheet)
def test(self): self.assertEqual(eval_cell(dict(), 3, 'C', {'C3'}), Cell(CellType.ERROR, errors.CIRCULAR_DEP)) sheet = {1: {'A': Cell(CellType.EXPRESSION, 'a1')}} self.assertEqual(eval_cell(sheet, 1, 'A', set()), Cell(CellType.ERROR, errors.CIRCULAR_DEP)) self.assertEqual(eval_cell(dict(), 3, 'C', set()), Cell(CellType.ERROR, errors.NONEXISTENT_CELL)) sheet = {1: {'A': Cell(CellType.NUMBER, 123)}} self.assertEqual(eval_cell(sheet, 1, 'A', set()), Cell(CellType.NUMBER, 123)) sheet = { 1: { 'A': Cell(CellType.EXPRESSION, '10+2/3*4+b1'), 'B': Cell(CellType.NONE, '') } } self.assertEqual(eval_cell(sheet, 1, 'A', set()), Cell(CellType.ERROR, errors.WRONG_ARG)) sheet = {1: {'A': Cell(CellType.EXPRESSION, '2/0')}} self.assertEqual(eval_cell(sheet, 1, 'A', set()), Cell(CellType.ERROR, errors.ZERO_DIV)) sheet = { 1: { 'A': Cell(CellType.EXPRESSION, 'b1'), 'B': Cell(CellType.STRING, '123') } } self.assertEqual(eval_cell(sheet, 1, 'A', set()), Cell(CellType.STRING, '123')) sheet = { 1: { 'A': Cell(CellType.EXPRESSION, 'b1+2'), 'B': Cell(CellType.STRING, '\'123') } } self.assertEqual(eval_cell(sheet, 1, 'A', set()), Cell(CellType.ERROR, errors.WRONG_ARG))
def test_set_posn_zero(self): self.assertEqual(Cell(0,0).posn(), (0,0))
def test_set_posn_empty(self): self.assertEqual(Cell().posn(), (0,0))
def test_set_name_zero_fails(self): with self.assertRaises(TypeError): Cell('0' ) with self.assertRaises(TypeError): Cell('00')
def test_set_name_colon_range_fails(self): with self.assertRaises(TypeError): Cell('A1:A2')
def test_set_posn_singleton_fails(self): with self.assertRaises(TypeError): Cell(1)
def test_set_name_empty(self): self.assertEqual(Cell().posn(), (0,0)) self.assertEqual(Cell('').posn(), (0,0)) self.assertEqual(Cell(' ').posn(), (0,0))
def test_set_posn(self): self.assertEqual(Cell(1,2).posn(), (1,2))
def test_singleton_rows(self): self.assertEqual(Cell('1').posn(), (0,0)) self.assertEqual(Cell('01').posn(), (0,0)) self.assertEqual(Cell('02').posn(), (0,1))
def test_none_cell(self): self.assertEqual(parse_cell(''), Cell(CellType.NONE, ''))
def test_set_posn_negative_fails(self): with self.assertRaises(TypeError): Cell(-1,2) with self.assertRaises(TypeError): Cell(1,-2)