Permalink
Browse files

added tests for s3g command line tools (Thanks to Jetty)

  • Loading branch information...
1 parent 26434d6 commit 2bf291fe768d5567a3c5135c615b1aa5540b2f44 @aleonard aleonard committed Aug 24, 2012
View
1 tests/g3_tests
View
892 tests/s3g_tests/G3FirmwareTests.py
@@ -0,0 +1,892 @@
+#!/usr/bin/python
+
+import unittest
+import optparse
+import serial
+import io
+import struct
+import array
+import time
+import os, sys
+import logging
+import tempfile
+import inspect
+
+lib_path = os.path.abspath('./s3g')
+sys.path.append(lib_path)
+
+# The s3g API
+import s3g
+
+# The s3g functions to test and the call arguments to use
+from test_s3g_functions import *
+
+# Testing utility routines
+from test_utilities import *
+
+# Defaults
+extensive = True
+port = '/dev/ttyACM0'
+hasInterface = True
+
+firmware = 'G3Firmware'
+#firmware = 'Jetty'
+#firmware = 'MightyBoard'
+
+# Process our command line arguments now
+# we need to know which firmware we will be testing in order to know
+# which profiles to load
+
+if __name__ == '__main__':
+
+ parser = optparse.OptionParser()
+ parser.add_option("-e", "--extensive", dest="extensive", default=str(extensive))
+ parser.add_option("-m", "--mightyboard", dest="isMightyBoard", default="True")
+ parser.add_option("-f", "--firmware", dest="firmware", default=firmware)
+ parser.add_option("-i", "--interface", dest="hasInterface", default="True")
+ parser.add_option("-p", "--port", dest="port", default=port)
+ (options, args) = parser.parse_args()
+
+ if options.extensive.lower() == "false":
+ print "Foregoing Heater Tests"
+ extensive = False
+ else:
+ extensive = True
+
+ if options.hasInterface.lower() in ['0', 'false', 'no']:
+ print "Foregoing tests requiring Interface Boards"
+ hasInterface = False
+
+ port = options.port
+ firmware = options.firmware
+
+# Now load the firmware 'profiles'
+
+if firmware.lower() in ['g3firmware', 'jetty']:
+
+ from G3Firmware_constants import *
+ from G3Firmware_unsupported_functions import *
+ hasInterface = False
+
+else:
+
+ from MightyBoard_constants import *
+ from MightyBoard_unsupported_functions import *
+
+class commonFunctionTests(unittest.TestCase):
+
+ def test_ConvertFromNUL(self):
+ b = bytearray("asdf\x00")
+ expectedReturn = "asdf"
+ self.assertEqual(expectedReturn, ConvertFromNUL(b))
+
+"""
+Test core packet handling routines used by the s3g module
+ -- test encode/decode functionality
+ -- test error handling (malformed packets, etc.)
+"""
+
+class s3gPacketTests(unittest.TestCase):
+
+ def setUp(self):
+ self.r = s3g.s3g()
+ self.r.writer = initWriterComms(port, 115200, timeout=1)
+
+ def tearDown(self):
+ self.r.writer.file.close()
+ self.r = None
+
+ def GetVersionPayload(self):
+ payload = struct.pack('<BH',
+ s3g.host_query_command_dict['GET_VERSION'],
+ s3g.s3g_version)
+ return payload
+
+ def GetVersionPacket(self):
+ """
+ Helper method to generate a Get Version packet to be modified and sent
+ """
+ return s3g.Encoder.encode_payload(self.GetVersionPayload())
+
+ def test_GetVersionPayload(self):
+ payload = self.GetVersionPayload()
+ self.assertEqual(payload[0], chr(s3g.host_query_command_dict['GET_VERSION']))
+ self.assertEqual(payload[1:], s3g.Encoder.encode_uint16(s3g.s3g_version))
+
+ def test_GetVersionPacket(self):
+ testPayload = self.GetVersionPayload()
+ packet = self.GetVersionPacket()
+ self.assertEqual(packet[0], s3g.header)
+ self.assertEqual(packet[1], len(packet[2:-1]))
+ self.assertEqual(packet[2:-1], testPayload)
+ self.assertEqual(packet[-1], s3g.Encoder.CalculateCRC(testPayload))
+
+ def test_NoHeader(self):
+ packet = self.GetVersionPacket()
+ packet[0] = '\x00'
+ self.assertRaises(s3g.TransmissionError, self.r.writer.send_packet, packet)
+
+ def test_EmptyPacket(self):
+ packet = bytearray()
+ self.assertRaises(s3g.TransmissionError, self.r.writer.send_packet, packet)
+
+ def test_TrailingPacket(self):
+ packet = self.GetVersionPacket()
+ addition = bytearray('\xff\xff')
+ packet.extend(addition)
+ self.r.writer.send_packet(packet)
+ self.assertTrue(True)
+
+ def test_PreceedingPacket(self):
+ packet = self.GetVersionPacket()
+ addition = bytearray('\xa4\x5f')
+ addition.extend(packet)
+ self.r.writer.send_packet(addition)
+ self.assertTrue(True)
+
+ def test_BadCRC(self):
+ packet = self.GetVersionPacket()
+ payload = packet[2:-1]
+ crc = s3g.Encoder.CalculateCRC(payload)
+ packet[-1] = crc+1
+ self.assertRaises(s3g.TransmissionError, self.r.writer.send_packet, packet)
+
+ def test_LongLength(self):
+ packet = self.GetVersionPacket()
+ packet[1] = '\x0f'
+ self.assertRaises(s3g.TransmissionError, self.r.writer.send_packet, packet)
+
+ def test_ShortLength(self):
+ packet = self.GetVersionPacket()
+ packet[1] = '\x01'
+ self.assertRaises(s3g.TransmissionError, self.r.writer.send_packet, packet)
+
+ def test_LongPayload(self):
+ packet = self.GetVersionPacket()
+ packet.insert(2, '\x00')
+ self.assertRaises(s3g.TransmissionError, self.r.writer.send_packet, packet)
+
+ def test_ShortPayload(self):
+ packet = self.GetVersionPacket()
+ packet = packet[0:2] + packet[3:]
+ self.assertRaises(s3g.TransmissionError, self.r.writer.send_packet, packet)
+
+ def test_MaxLength(self):
+ """
+ Packet.cc code in the G3Firmware and MightyBoard firmware only allows a
+ maximum payload length of 31 (s3g.maximum_payload_length - 1). Refer to
+ InPacket::processByte() where, when processing the length byte, it only
+ allows "(b < MAX_PACKET_PAYLOAD)" and not "(b <= MAX_PACKET_PAYLOAD)"
+ """
+ payload = self.GetVersionPayload()
+ for i in range(s3g.maximum_payload_length - (1 + len(payload))):
+ payload += chr(0x00)
+ self.r.writer.send_command(payload)
+
+ def test_OversizedLength(self):
+ payload = bytearray(s3g.maximum_payload_length+1)
+ self.assertRaises(s3g.PacketLengthError, s3g.Encoder.encode_payload, payload)
+
+class s3gSendReceiveTests(unittest.TestCase):
+
+ def setUp(self):
+ self.r = s3g.s3g()
+ self.r.writer = initWriterComms(port, 115200, timeout=1)
+
+ def tearDown(self):
+ self.r.writer.file.close()
+ self.r = None
+
+ """
+ Use introspection to discover every member function of the s3g class. Then if
+ the discovered member function is NOT in s3g_functions_to_ignore[] and is in
+ s3g_functions{} with a call argument tuple (!= None), then call the method
+ function with the call argument tuple.
+
+ If the method function is in the firmware-specific unsupported_functions[] list,
+ then an error will be anticipated. If it is not in that list, then a successful
+ return will be expected.
+
+ Additionally,
+
+ 1. The keys of the s3g_functions{} are enumerated, looking for keys no longer
+ specifying member functions of the s3g class. This is done to help keep this
+ test suite and the s3g class in sync.
+
+ 2. When a member function of the s3g class is encountered which is not in the
+ s3g_functions_to_ignore[] list and also not in the s3g_functions{} dictionary,
+ an error is signalled. This too is done to keep the s3g class and this test
+ suite in sync.
+ """
+
+ @skipUnless(all([x in globals() for x in ['s3g_functions_to_ignore', 's3g_function_calls', 'unsupported_functions', 'buggy_functions']]))
+ def test_s3g_functions(self):
+ # Loop over every member of the s3g.s3g class
+ for name in dir( self.r ):
+
+ # Proceed only if this member is a class method
+ method = getattr( self.r, name)
+ if inspect.ismethod( method ):
+
+ # Proceed only if this method function is NOT in the ignore list, s3g_functions_to_ignore[]
+ if not ( name in s3g_functions_to_ignore ) and not ( name in buggy_functions ):
+
+ # See if this method function is a key in the s3g_function_calls{} dictionary
+ if name in s3g_function_calls:
+
+ args = s3g_function_calls[ name ]
+
+ # Is this a supported function which should be skipped?
+ if args is None:
+
+ print 'Skipping ' + name + '()'
+
+ else:
+
+ # See if there are instructions to execute after calling the function
+ # E.g., turning a heater off or waiting a few seconds for the bot's
+ # firmware to initialize itself after a reset.
+ post_call = ''
+ if isinstance( args, list ):
+ post_call = args[1]
+ args = args[0]
+
+ # Note whether the firmware supports this s3g function
+ # We want to call it regardless of whether it is supported or not
+ # The isSupported flag merely tells us if we should expect a "Success"
+ # or "Unsupported" response from the bot.
+ #
+ # There's a slight twist on all of this: an unsupported BUT buffered
+ # command actually gets a "Success" response back from the bot. That's
+ # because the bot merely acknowledges receipt of a well-formed packet
+ # when it sees that it is a buffered command (Host Action Command).
+
+ isSupported = not( name in unsupported_functions )
+ if name in s3g_buffered_functions:
+ isSupported = True
+
+ # Now call the method (r.self.method). The isSupported flag indicates
+ # whether we should expect an exception to be raised or not
+
+ print 'Invoking ' + name + ( '%s' % (args,) if isinstance( args, tuple) else '(%s)' % args )
+ callFunc( method, self, isSupported, args )
+
+ # And execute any additional instructions
+ if post_call != '':
+ evalStr( self.r, post_call )
+
+ else:
+
+ # We've encountered a member function of the class s3g.s3g which
+ # isn't in the s3g_function_calls{} dictionary nor in the list
+ # s3g_functions_to_ignore[].
+ #
+ # This suggests that s3g.py was updated but not the unit tests.
+
+ print 'WARNING: the member function %s() is in the class s3g ' % name + \
+ 'but not in either s3g_functions_to_ignore[] or ' + \
+ 's3g_function_calls{}. Please correct the unit test ' + \
+ 'test_Blah.'
+
+ # Now another test to ensure that the test scripts are in sync with s3g.py:
+ # see if s3g_function_calls{} contains names not in dir(s3g.s3g()).
+
+ names = dir( self.r )
+ for key in s3g_function_calls:
+ if not ( key in names ) or ( not inspect.ismethod( getattr( self.r, key) ) ):
+ print 'WARNING: the s3g_function_calls{} dictionary contains an entry, ' + \
+ '%s, which is not a member function of the s3g class of s3g.py' % key
+
+ # Made it this far: do an explicit assert if we managed to get by
+ # without doing any asserts...
+
+ self.assertTrue(True)
+
+class s3gFunctionTests(unittest.TestCase):
+
+ def setUp(self):
+ self.r = s3g.s3g()
+ self.r.writer = initWriterComms(port, 115200, timeout=1)
+
+ def tearDown(self):
+ self.r.writer.file.close()
+ self.r = None
+
+ def test_SetGetPlatformTargetTemperature(self):
+ target = constants['target_temp']
+ self.assertEqual(self.r.get_platform_target_temperature(0), 0)
+ self.r.set_platform_temperature(0, target)
+ self.assertEqual(self.r.get_platform_target_temperature(0), target)
+ self.r.set_platform_temperature(0, 0)
+
+ def test_SetGetToolheadTargetTemperature(self):
+ target = constants['target_temp']
+ toolhead = constants['toolhead']
+ self.assertEqual(self.r.get_toolhead_target_temperature(toolhead), 0)
+ self.r.set_toolhead_temperature(toolhead, target)
+ self.assertEqual(self.r.get_toolhead_target_temperature(toolhead), target)
+ self.r.set_toolhead_target_temperature(constants['toolhead'], 0)
+
+ def test_ReadWordsFromEeprom(self):
+ """
+ Read the LCD type and compare to our expected value
+ """
+ offset, length, default, format = EEPROM_offsets['read_test']
+ test = self.r.read_from_EEPROM(offset, length)
+ test = array.array('B', test)
+ test = struct.unpack(format, test)
+ self.assertTrue(all([test[i] == default[i] for i in range(0,len(test))]))
+
+ def test_WriteToEepromG3firmware(self):
+ """
+ Write a new bot name to the EEPROM and verify that it reads back correctly
+ """
+ nameOffset, length, default = EEPROM_offsets['machine_name']
+ origName = self.r.read_from_EEPROM(nameOffset, length)
+ name = 'ILOVETESTINGALOT'
+ self.r.write_to_EEPROM(nameOffset, name)
+ readName = self.r.read_from_EEPROM(nameOffset, len(name))
+ self.r.write_to_EEPROM(nameOffset, origName)
+ self.assertEqual(name, readName)
+
+ def test_IsPlatformReady(self):
+ """
+ Determine if the platform is ready by setting the temperature to its current reading and asking if its ready (should return true, then setting the temperature to +50 what it is now then querying it agian, expecting a false answer
+ """
+ curTemp = self.r.get_platform_temperature(0)
+ self.r.set_platform_temperature(0, curTemp)
+ self.assertTrue(self.r.is_platform_ready(0))
+ self.r.set_platform_temperature(0, curTemp+50)
+ self.assertEqual(self.r.is_platform_ready(0), False)
+ self.r.set_platform_temperature(0, 0)
+
+ def test_IsToolReady(self):
+ toolhead = constants['toolhead']
+ curTemp = self.r.get_toolhead_temperature(toolhead)
+ self.r.set_toolhead_temperature(toolhead, curTemp)
+ self.assertTrue(self.r.is_tool_ready(toolhead))
+ self.r.set_toolhead_temperature(toolhead, curTemp + 50)
+ self.assertEqual(self.r.is_tool_ready(toolhead, False))
+ self.r.set_toolhead_temperature(toolhead, 0)
+
+ def test_SetGetExtendedPosition(self):
+ position = [50, 51, 52, 53, 54]
+ self.r.set_extended_position(position)
+ self.assertEqual(position, self.r.get_extended_position()[0])
+
+ def test_QueueExtendedPoint(self):
+ self.r.find_axes_maximums(['x', 'y'], 500, 100)
+ self.r.find_axes_minimums(['z'], 500, 100)
+ self.r.recall_home_positions(['x', 'y', 'z', 'a', 'b'])
+
+ newPosition = [51, 52, 10, 0, 0]
+ rate = 500
+ self.r.queue_extended_point(newPosition, rate)
+ time.sleep(5)
+ self.assertEqual(newPosition, self.r.get_extended_position()[0])
+
+ def test_FindAxesMaximums(self):
+ axes = ['x', 'y']
+ rate = 500
+ timeout = 10
+ xymax_endstops = 0x05
+ self.r.find_axes_maximums(axes, rate, timeout)
+ time.sleep(timeout)
+ self.assertEqual(self.r.get_extended_position()[1]&0x0F, xymax_endstops)
+
+ def test_FindAxesMinimums(self):
+ axes = ['z']
+ rate = 500
+ timeout = 50
+ zmin_endstops = 0x20
+ self.r.find_axes_minimums(axes, rate, timeout)
+ time.sleep(timeout)
+ self.assertEqual(self.r.get_extended_position()[1]&0x30, zmin_endstops)
+
+ def test_Init(self):
+ position = [10, 9, 8, 7, 6]
+ self.r.set_extended_position(position)
+ #this function doesn't do anything, so we are testing that position is NOT cleared after command recieved
+ self.r.init()
+ time.sleep(5)
+ self.assertEqual(position, self.r.get_extended_position()[0])
+
+ def test_GetAvailableBufferSize(self):
+ bufferSize = constants['buffer_size']
+ self.assertEqual(bufferSize, self.r.get_available_buffer_size())
+
+ def test_AbortImmediately(self):
+ bufferSize = constants['buffer_size']
+ toolheads = constants['tool_heads']
+ target = constants['target_temp']
+ for toolhead in toolheads:
+ self.r.set_toolhead_temperature(toolhead, target)
+ self.r.set_platform_temperature(0, target)
+ self.r.find_axes_minimums(['z'], 500, 5)
+ self.r.find_axes_maximums(['x','y'], 500, 5)
+ self.r.abort_immediately()
+ time.sleep(5)
+ self.assertEqual(bufferSize, self.r.get_available_buffer_size())
+ for toolhead in toolheads:
+ self.assertEqual(0, self.r.get_toolhead_target_temperature(toolhead))
+ self.assertEqual(0, self.r.get_platform_target_temperature(0))
+ self.assertTrue(self.r.is_finished())
+
+ @skipIf(any(x in unsupported_functions for x in ['build_start_notification', 'build_end_notification', 'get_build_name', 'get_build_stats']))
+ def test_BuildStartNotification(self):
+ buildName = "test"
+ self.r.build_start_notification(buildName)
+ readBuildName = self.r.get_build_name()
+ readBuildName = ConvertFromNUL(readBuildName)
+ self.assertEqual(buildName, readBuildName)
+ stats = self.r.get_build_stats()
+ build_running_state = 1
+ self.assertEqual(stats['BuildState'], build_running_state)
+ self.r.build_end_notification()
+
+ @skipIf(any(x in unsupported_functions for x in ['build_start_notification', \
+ 'build_end_notification', \
+ 'get_build_stats']))
+ def test_BuildEndNotification(self):
+ self.r.build_start_notification("test")
+ time.sleep(2)
+ stats = self.r.get_build_stats()
+ build_running_state = 1
+ self.assertEqual(stats['BuildState'], build_running_state)
+ self.r.build_end_notification()
+ stats = self.r.get_build_stats()
+ build_finished_state = 2
+ self.assertEqual(stats['BuildState'], build_finished_state)
+
+ @skipIf(any(x in unsupported_functions for x in ['build_start_notification', \
+ 'get_build_stats']))
+ def test_BuildStats(self):
+ """
+ we've tested build start, stop and pause in other tests
+ test build cancel
+ test line number
+ test print time
+ """
+ # start build
+ self.r.build_start_notification("test")
+ start_time = time.time()
+ stats = self.r.get_build_stats()
+ build_running_state = 1
+ self.assertEqual(stats['BuildState'], build_running_state)
+ # send 5 commands
+ for cmd in range(0,5):
+ self.r.toggle_axes(['x', 'y', 'z', 'a', 'b'], True)
+ stats = self.r.get_build_stats()
+ self.assertEqual(stats['LineNumber'], 5)
+ # send 100 commands
+ for cmd in range(0,100):
+ self.r.toggle_axes(['x', 'y', 'z', 'a', 'b'], False)
+ stats = self.r.get_build_stats()
+ self.assertEqual(stats['LineNumber'], 105)
+ time.sleep(60)
+ #check how much time has passed
+ time_minutes = int((time.time()-start_time) / 60)
+ stats = self.r.get_build_stats()
+ self.assertEqual(stats['BuildMinutes'], time_minutes)
+ #cancel build
+ self.r.abort_immediately()
+ self.assertRaises(s3g.BuildCancelledError, self.r.get_build_stats)
+ stats = self.r.get_build_stats()
+ build_cancelled_state = 4
+ self.assertEqual(stats['BuildState'], build_cancelled_state)
+
+ @skipIf('get_communication_stats' in unsupported_functions)
+ def test_CommStats(self):
+ info1 = self.r.get_communication_stats()
+ info2 = self.r.get_communication_stats()
+ self.assertNotEqual(info1['PacketsReceived'], info2['PacketsReceived'])
+
+ def test_ClearBuffer(self):
+ bufferSize = constants['buffer_size']
+ target = constants['target_temp']
+ self.r.set_platform_temperature(0, target)
+ self.r.wait_for_platform_ready(0, 0, 0xFFFF)
+ axes = ['z']
+ rate = 500
+ timeout = 5
+ for i in range(10):
+ self.r.find_axes_minimums(axes, rate, timeout)
+ self.assertNotEqual(bufferSize, self.r.get_available_buffer_size())
+ self.r.clear_buffer()
+ time.sleep(5) # we need to sleep after sending any reset functions
+ self.assertEqual(bufferSize, self.r.get_available_buffer_size())
+ self.r.set_platform_temperature(0, 0) # Just to be safe
+
+ @skipIf('get_build_stats' in unsupported_functions)
+ def test_PauseandBuildStats(self):
+ stats = self.r.get_build_stats()
+ build_paused_state = 3
+ self.assertNotEqual(stats['BuildState'], build_paused_state)
+ self.r.pause()
+ stats = self.r.get_build_stats()
+ self.assertEqual(stats['BuildState'], build_paused_state)
+
+ @skipIf('get_build_stats' in unsupported_functions)
+ def test_ToolheadPauseandBuildStats(self):
+ stats = self.r.get_build_stats()
+ build_paused_state = 3
+ self.assertNotEqual(stats['BuildState'], build_paused_state)
+ self.r.toolhead_pause(0)
+ stats = self.r.get_build_stats()
+ self.assertEqual(stats['BuildState'], build_paused_state)
+
+ def test_IsFinished(self):
+ timeout = 30
+ # home axes
+ self.r.find_axes_maximums(['x', 'y'], 500, timeout)
+ self.r.find_axes_minimums(['z'], 500, timeout)
+ self.r.recall_home_positions(['x', 'y', 'z', 'a', 'b'])
+ time.sleep(timeout)
+ # go to a new point
+ newPoint = [50, 50, 10, 0, 0]
+ duration = 5
+ micros = 1000000
+ self.r.queue_extended_point_new(newPoint, duration*micros, [])
+ # assert that bot is not finished
+ self.assertFalse(self.r.is_finished())
+ time.sleep(duration)
+ # assert that bot is finished
+ self.assertTrue(self.r.is_finished())
+
+ def test_Reset(self):
+ bufferSize = constants['buffer_size']
+ toolhead = constants['toolhead']
+ target = constants['target_temp']
+ self.r.set_toolhead_temperature(toolhead, target)
+ self.r.set_platform_temperature(0, target)
+ self.r.wait_for_platform_ready(0, 0, 0xFFFF)
+ for i in range(30):
+ self.r.toggle_axes(['x', 'y', 'z'], False)
+ self.assertNotEqual(self.r.get_available_buffer_size(), bufferSize)
+ self.r.reset()
+ self.assertEqual(self.r.get_available_buffer_size(), bufferSize)
+ self.assertTrue(self.r.is_finished())
+ self.assertEqual(self.r.get_toolhead_target_temperature(toolhead, 0))
+ self.assertEqual(self.r.get_platform_target_temperature(0), 0)
+
+ def test_Delay(self):
+ self.r.find_axes_maximums(['x', 'y'], 500, 50)
+ self.r.find_axes_minimums(['z'], 500, 50)
+ self.r.recall_home_positions(['x', 'y', 'z', 'a', 'b'])
+ delay = 5
+ duration = 5
+ millis = 1000
+ newPoint = [50, 50, 10, 0, 0]
+ self.r.delay(delay*millis)
+ self.r.queue_extended_point_new(newPoint, duration*millis, [])
+ self.assertFalse(self.r.is_finished())
+ for i in range(0, duration+delay-1):
+ print i
+ self.assertFalse(self.r.is_finished())
+ time.sleep(1)
+ time.sleep(1)
+ self.assertTrue(self.r.isFinished())
+
+ def test_ExtendedStop(self):
+ bufferSize = constants['buffer_size']
+ self.r.find_axes_maximums(['x', 'y'], 200, 50)
+ self.r.find_axes_minimums(['z'], 500, 50)
+ self.r.recall_home_positions(['x', 'y', 'z', 'a', 'b'])
+ delay = 5
+ duration = 5
+ millis = 1000
+ newPoint = [50, 50, 10, 0, 0]
+ self.r.delay(delay*millis)
+ self.r.queue_extended_point_new(newPoint, duration*millis, [])
+ for i in range(5):
+ self.r.toggle_axes(['x', 'y', 'z'], False)
+ self.r.extended_stop(True, True)
+ time.sleep(2) #Give the machine time to respond
+ self.assertTrue(self.r.is_finished())
+ self.assertEqual(bufferSize, self.r.get_available_buffer_size())
+
+ def test_QueueExtendedPointNew(self):
+ self.r.find_axes_maximums(['x', 'y'], 200, 50)
+ self.r.find_axes_minimums(['z'], 500, 50)
+ self.r.recall_home_positions(['x', 'y', 'z', 'a', 'b'])
+ while(self.r.is_finished is False):
+ time.sleep(1)
+ newPoint = [1, 2, 3, 0, 0]
+ millis = 1000
+ duration = 5
+ self.r.queue_extended_point_new(newPoint, duration*millis, [])
+ time.sleep(duration)
+ self.assertEqual(newPoint, self.r.get_extended_position()[0])
+ anotherPoint = [5, 6, 7, 8, 9]
+ self.r.queue_extended_point_new(anotherPoint, duration*millis, [])
+ time.sleep(duration)
+ self.assertEqual(anotherPoint, self.r.get_extended_position()[0])
+
+ def test_StoreHomePositions(self):
+ old_home = self.read_from_EEPROM(EEPROM_offsets['home_positions'][0], EEPROM_offsets['home_positions'][1])
+ position = [20,20,30,40,50]
+ self.r.set_extended_position(position)
+ self.r.store_home_positions(['X', 'Y', 'Z', 'A', 'B'])
+ x = self.r.read_from_EEPROM(EEPROM_offsets['x_home'][0], EEPROM_offsets['x_home'][1])
+ y = self.r.read_from_EEPROM(EEPROM_offsets['y_home'][0], EEPROM_offsets['y_home'][1])
+ z = self.r.read_from_EEPROM(EEPROM_offsets['z_home'][0], EEPROM_offsets['z_home'][1])
+ a = self.r.read_from_EEPROM(EEPROM_offsets['a_home'][0], EEPROM_offsets['a_home'][1])
+ b = self.r.read_from_EEPROM(EEPROM_offsets['b_home'][0], EEPROM_offsets['b_home'][1])
+ readHome = []
+ for cor in [x, y, z, a, b]:
+ readHome.append(s3g.Encoder.decode_int32(cor))
+ self.r.write_to_EEPROM(EEPROM_offsets['home_positions'][0], old_home)
+ self.assertEqual(readHome, position)
+
+ def test_RecallHomePositions(self):
+ old_home = self.read_from_EEPROM(EEPROM_offsets['home_positions'][0], EEPROM_offsets['home_positions'][1])
+ pointToSet = [1, 2, 3, 4, 5]
+ self.r.set_extended_position(pointToSet)
+ self.r.store_home_positions(['X', 'Y', 'Z', 'A', 'B'])
+ newPoint = [50, 51, 52, 53, 54]
+ self.r.set_extended_position(newPoint)
+ self.r.recall_home_positions([])
+ time.sleep(5)
+ self.assertEqual(newPoint, self.r.get_extended_position()[0])
+ self.r.recall_home_positions(['X', 'Y', 'Z', 'A', 'B'])
+ time.sleep(5)
+ self.r.write_to_EEPROM(EEPROM_offsets['home_positions'][0], old_home)
+ self.assertEqual(pointToSet, self.r.get_extended_position()[0])
+
+ def test_GetPIDState(self):
+ toolhead = constants['toolhead']
+ pidDict = self.r.get_PID_state(toolhead)
+ for key in pidDict:
+ self.assertNotEqual(pidDict[key], None)
+
+class s3gUserFunctionTests(unittest.TestCase):
+
+ def setUp(self):
+ self.r = s3g.s3g()
+ self.r.writer = initWriterComms(port, 115200, timeout=1)
+ time.sleep(1)
+
+ def tearDown(self):
+ self.r.writer.file.close()
+ self.r = None
+
+ """
+ def test_WaitForPlatformReady(self):
+
+ def test_WaitForToolReady(self):
+ """
+
+ def test_ToggleAxes(self):
+ self.r.toggle_axes(['X', 'Y', 'Z', 'A', 'B'], True)
+ obs = raw_input("\nPlease try to move all (x,y,z) the axes! Can you move them without using too much force? (y/n) ")
+ self.assertEqual('n', obs)
+ self.r.toggle_axes(['X', 'Y', 'Z', 'A', 'B'], False)
+ obs = raw_input("\nPlease try to move all (x,y,z) the axes! Can you move them without using too much force? (y/n) ")
+ self.assertEqual('y', obs)
+
+ @skipUnless(hasInterface and not('display_message' in unsupported))
+ def test_DisplayMessage(self):
+ message = str(time.clock())
+ self.r.display_message(0, 0, message, 10, False, False, False)
+ readMessage = raw_input("\nWhat is the message on the replicator's display? ")
+ self.assertEqual(message, readMessage)
+
+ def test_ToggleFan(self):
+ toolhead = constants['toolhead']
+ self.r.toggle_fan(toolhead, True)
+ obs = raw_input("\nIs toolhead %i's fan on? (y/n) " % constants['toolhead'])
+ self.r.toggle_fan(toolhead, False)
+ self.assertEqual(obs, 'y')
+
+ def test_GetVersion(self):
+ expectedVersion = raw_input("\nWhat is the version number of your bot? ")
+ expectedVersion = int(expectedVersion.replace('.', '0'))
+ self.assertEqual(expectedVersion, self.r.get_version())
+
+ def test_GetToolheadVersion(self):
+ toolhead = constants['toolhead']
+ expectedVersion = raw_input("\nWhat is the version number of toolhead %d on your bot? " % toolhead)
+ expectedVersion = int(expectedVersion.replace('.', '0'))
+ self.assertEqual(expectedVersion, self.r.get_toolhead_version(toolhead))
+
+ def test_GetToolStatus(self):
+ toolhead = constants['toolhead']
+ target = constants['target_temp']
+ self.r.set_toolhead_temperature(toolhead, 0)
+ time.sleep(0.1)
+ returnDic = self.r.get_tool_status(toolhead)
+ self.assertFalse(returnDic['ExtruderReady']) # Extruder is not at temp
+ self.assertFalse(returnDic['PlatformError'])
+ self.assertFalse(returnDic['ExtruderError'])
+ self.r.set_toolhead_temperature(toolhead, target)
+ time.sleep(0.1)
+ returnDic = self.r.get_tool_status(toolhead)
+ self.assertEqual(returnDic['ExtruderReady'], self.r.is_tool_ready(toolhead))
+ # G3Firmware for the v3.6 EC will detected a shorted thermistor on the HBP,
+ # but not an open, floating connection to the thermistor
+ if not ( firmware.lower() in ['g3firmware', 'jetty'] ):
+ raw_input("\nPlease unplug the platform!! Press enter to continue.")
+ self.r.writer.file.close()
+ self.r.writer.file = initSerialComms(port, 115200, timeout=1)
+ self.r.set_platform_temperature(toolhead, target)
+ time.sleep(5)
+ returnDic = self.r.get_tool_status(toolhead)
+ self.assertTrue(returnDic['PlatformError'])
+ raw_input("\nPlease turn the bot off, plug in the platform and unplug extruder %d's thermocouple!! Press enter to continue." % toolhead)
+ else:
+ raw_input("\nPlease turn the bot off and unplug extruder %d's thermocouple!! Press enter to continue." % toolhead)
+ self.r.writer.file.close()
+ self.r.writer.file = initSerialComms(port, 115200, timeout=1)
+ self.r.set_toolhead_temperature(toolhead, target)
+ time.sleep(5)
+ returnDic = self.r.get_tool_status(constants['toolhead'])
+ self.assertTrue(returnDic['ExtruderError'])
+ self.r.writer.file.close()
+ raw_input("\nPlease turn the bot off and plug in the platform and extruder %d's thermocouple!! Press enter to continue." % toolhead)
+ self.r.writer.file = initSerialComms(port, 115200, timeout=1)
+
+ @skipIf('wait_for_button' in unsupported_functions)
+ def test_WaitForButton(self):
+ self.r.wait_for_button('up', 0, False, False, False)
+ obs = raw_input("\nIs the center button flashing? (y/n) ")
+ self.assertEqual(obs, 'y')
+ obs = raw_input("\nPress all the buttons EXCEPT the 'up' button. Is the center button still flashing? (y/n) ")
+ self.assertEqual(obs, 'y')
+ obs = raw_input("\nPress the 'up' button. Did the center button stop flashing? (y/n) ")
+ self.assertEqual(obs, 'y')
+ raw_input("\nTesting wait_for_button timeout. Please watch the interface board and note the time! Press enter to continue")
+ self.r.wait_for_button('up', 5, True, False, False)
+ obs = raw_input("\nDid the center button flash for about 5 seconds and stop? (y/n) ")
+ self.assertEqual(obs, 'y')
+ raw_input("\nTesting bot reset after tiemout. Please watch/listen to verify if the replicator is resetting. Press enter to continue.")
+ self.r.wait_for_button('up', 1, False, True, False)
+ time.sleep(1)
+ obs = raw_input("\nDid the bot just reset? (y/n) ")
+ self.assertEqual(obs, 'y')
+ self.r.wait_for_button('up', 0, False, False, True)
+ obs = raw_input("\nPlease press the up button and note if the LCD screen resest or not. Did the screen reset? (y/n) ")
+ self.assertEqual(obs, 'y')
+
+ @skipIf('set_RGB_LED' in unsupported_functions)
+ def test_SetRGBLED(self):
+ self.r.set_RGB_LED(0, 255, 0, 0)
+ obs = raw_input("\nAre the LEDs in the bot green? (y/n) ")
+ self.assertEqual(obs, 'y')
+ self.r.set_RGB_LED(0, 255, 0, 128)
+ obs = raw_input("\nAre the LEDs blinking? (y/n) ")
+ self.assertEqual('y', obs)
+
+ @skipIf('set_beep' in unsupported_functions)
+ def test_SetBeep(self):
+ raw_input("\nAbout to start playing some music. Start listening! Press enter to continue")
+ self.r.set_beep(261.626, 5)
+ obs = raw_input("\nDid you hear a C note? (y/n) ")
+ self.assertEqual('y', obs)
+
+ @skipIf(any(x in unsupported_functions for x in ['set_build_percent', 'build_start_notification']))
+ def test_SetBuildPercent(self):
+ percent = 42
+ self.r.build_start_notification("percentTest")
+ self.r.set_build_percent(percent)
+ obs = raw_input("\nLook at the interface board for your bot. Does the build percent say that it is %1 percent of the way done? (y/n) " %(percent))
+ self.assertEqual('y', obs)
+
+ @skipIf('queue_song' in unsupported_functions)
+ def test_QueueSong(self):
+ raw_input("\nGetting ready to play a song. Make sure you are listening! Press enter to continue.")
+ self.r.queue_song(1)
+ obs = raw_input("\nDid you hear the song play? (y/n) ")
+ self.assertEqual(obs, 'y')
+
+class s3gSDCardTests(unittest.TestCase):
+
+ def setUp(self):
+ self.r = s3g.s3g()
+ self.r.writer = initWriterComms(port, 115200, timeout=1)
+
+ def tearDown(self):
+ self.r.writer.file.close()
+ self.r = None
+
+ @skipIf('playback_capture' in unsupported_functions)
+ def test_PlaybackCaptureReply(self):
+ self.assertRaises(s3g.SDCardError, self.r.playback_capture, 'aTest')
+
+ @skipIf('capture_to_file' in unsupported_functions)
+ def test_CaptureToFileReply(self):
+ self.r.capture_to_file('test')
+
+ @skipIf('end_capture_to_file' in unsupported_functions)
+ def test_EndCaptureToFileReply(self):
+ self.r.end_capture_to_file()
+
+ @skipIf(any(x in unsupported_functions for x in ['capture_to_file', 'end_capture_to_file']))
+ def test_EndCaptureToFile(self):
+ filename = str(time.clock())+".s3g"
+ self.r.capture_to_file(filename)
+ findAxesMaximums = 8+32+16
+ numCmd = 5
+ totalBytes = findAxesMaximums*numCmd/8 + numCmd
+ #Add some commands to the file
+ for i in range(numCmd):
+ self.r.find_axes_maximums(['x', 'y'], 500, 10)
+ self.assertEqual(totalBytes, self.r.end_capture_to_file())
+
+ @skipIf(any(x in unsupported_functions for x in ['capture_to_file', 'get_next_filename']))
+ def test_CaptureToFile(self):
+ filename = str(time.clock())+".s3g" #We want to keep changing the filename so this test stays nice and fresh
+ self.r.capture_to_file(filename)
+ #Get the filenames off the SD card
+ files = []
+ curFile = ConvertFromNUL(self.r.get_next_filename(True))
+ while curFile != '':
+ curFile = ConvertFromNUL(self.r.get_next_filename(False))
+ files.append(curFile)
+ self.assertTrue(filename in files)
+
+ @skipIf('get_build_name' in unsupported_functions)
+ def test_GetBuildName(self):
+ """
+ Copy the contents of the testFiles directory onto an sd card to do this test
+ """
+ buildName = raw_input("\nPlease load the test SD card into the machine, select one of the files and begin to print it. Then type the name _exactly_ as it appears on the bot's screen. ")
+ name = self.r.get_build_name()
+ self.assertEqual(buildName, ConvertFromNUL(name))
+
+ @skipIf('get_next_filename' in unsupported_functions)
+ def test_GetNextFilename(self):
+ """
+ Copy the contents of the testFiles directory onto an sd card to do this test
+ """
+ raw_input("\nPlease make sure the only files on the SD card plugged into the bot are the files inside the testFiles directory!! Press enter to continue")
+ filename = 'box_1.s3g'
+ volumeName = raw_input("\nPlease type the VOLUME NAME of the replicator's SD card exactly! Press enter to continue")
+ readVolumeName = self.r.get_next_filename(True)
+ self.assertEqual(volumeName, ConvertFromNUL(readVolumeName))
+ readFilename = self.r.get_next_filename(False)
+ self.assertEqual(filename, ConvertFromNUL(readFilename))
+
+ @skipIf('playback_capture' in unsupported_functions)
+ def test_PlaybackCapture(self):
+ filename = 'box_1.s3g'
+ self.r.playback_capture(filename)
+ readName = self.r.get_build_name()
+ self.assertEqual(filename, ConvertFromNUL(readName))
+
+if __name__ == '__main__':
+
+ logging.basicConfig()
+ del sys.argv[1:]
+
+ commonTests = unittest.TestLoader().loadTestsFromTestCase(commonFunctionTests)
+ packetTests = unittest.TestLoader().loadTestsFromTestCase(s3gPacketTests)
+ sendReceiveTests = unittest.TestLoader().loadTestsFromTestCase(s3gSendReceiveTests)
+ functionTests = unittest.TestLoader().loadTestsFromTestCase(s3gFunctionTests)
+ userFunctionTests = unittest.TestLoader().loadTestsFromTestCase(s3gUserFunctionTests)
+ #sdTests = unittest.TestLoader().loadTestsFromTestCase(s3gSDCardTests)
+ #suites = [sendReceiveTests] #packetTests] #, sendReceiveTests, functionTests, sdTests]
+
+ suites = [commonTests, sendReceiveTests] # userFunctionTests]
+
+ for suite in suites:
+ unittest.TextTestRunner(verbosity=2).run(suite)
View
42 tests/s3g_tests/G3Firmware_constants.py
@@ -0,0 +1,42 @@
+"""
+Constants unique to the G3Firmware and used by the G3Firmware tests
+"""
+
+platform = 'mb24-2560'
+# platform = 'mb24'
+# platform = 'rrmbv12'
+
+# EEPROM field offsets and lengths
+
+EEPROM_offsets = {
+
+ # key : ( offset, length, default-value )
+
+ 'read_test' : ( 0x0173, 1, [0x00], '<B' ),
+ # 0x0044, 4, [0x23C1, 0xB404], '<HH'
+ 'machine_name' : ( 0x0020, 32, '\x00' ),
+ 'home_positions' : ( 0x0060, 20, None ),
+ 'x_home' : ( 0x0060, 4, '\x00\x00\x00\x00' ),
+ 'y_home' : ( 0x0064, 4, '\x00\x00\x00\x00' ),
+ 'z_home' : ( 0x0068, 4, '\x00\x00\x00\x00' ),
+ 'a_home' : ( 0x006c, 4, '\x00\x00\x00\x00' ),
+ 'b_home' : ( 0x0070, 4, '\x00\x00\x00\x00' )
+}
+
+# Other firmware constants
+
+constants = {}
+
+# Primary tool head index
+constants['toolhead'] = 0
+
+# For commands which want to control all toolheads (such as ensuring the heaters are off)
+constants['toolheads'] = [ constants['toolhead'] ] # [0, 1] for two tool heads
+
+# Temperature to heat things up to
+constants['target_temp'] = 100
+
+if platform != 'rrmbv12':
+ constants['buffer_size'] = 512
+else:
+ constants['buffer_size'] = 256
View
49 tests/s3g_tests/G3Firmware_unsupported_functions.py
@@ -0,0 +1,49 @@
+"""
+List of the unsupported s3g functions. It's easier to list what isn't
+supported than what is.
+
+These functions can be attempted and will return an "unknown command"
+response if they are not buffered.
+"""
+
+unsupported_functions = [
+
+# Host Query Commands
+
+ 'get_motherboard_status', # 023
+ 'get_build_stats', # 024
+ 'get_advanced_version', # 027
+
+# Host Action Commands (buffered)
+
+ 'set_potentiometer_value', # 145
+ 'set_RGB_LED', # 146
+ 'set_beep', # 147
+ 'wait_for_button', # 148
+ 'display_message', # 149
+ 'set_build_percent', # 150
+ 'queue_song', # 151
+ 'reset_to_factory', # 152
+ 'build_start_notification', # 153
+ 'build_end_notification', # 154
+
+# Tool Query Commands
+
+# Tool Action Commands
+
+]
+
+
+"""
+List of functions to not attempt: they will return a result which is incorrect.
+(Unsupported functions can be tried and they will return an "unknown command" response.)
+
+"""
+buggy_functions = [
+
+ # Bug in v3.6 Extruder Controller code: has a missing return/break statement
+ # in the switch() clause that handles this. It falls through to another query
+ # and thus generates a result with too much data.
+
+ 'get_PID_state'
+]
View
35 tests/s3g_tests/MightyBoard_constants.py
@@ -0,0 +1,35 @@
+"""
+Constants unique to the MightyBoard firmware
+"""
+
+# EEPROM field offsets and lengths
+
+EEPROM_offsets = {
+
+ # key : ( offset, length, default-value )
+
+ 'read_test' : ( 0x0044, 4, [0x23C1, 0xB404], '<HH' ),
+ 'machine_name' : ( 0x0022, 16, '\x00' ),
+ 'home_positions' : ( 0x000E, 20, None ),
+ 'x_home' : ( 0x000E, 4, '\x00\x00\x00\x00' ),
+ 'y_home' : ( 0x0012, 4, '\x00\x00\x00\x00' ),
+ 'z_home' : ( 0x0016, 4, '\x00\x00\x00\x00' ),
+ 'a_home' : ( 0x001A, 4, '\x00\x00\x00\x00' ),
+ 'b_home' : ( 0x001E, 4, '\x00\x00\x00\x00' )
+}
+
+# Other firmware constants
+
+constants = {}
+
+# Primary tool head index
+constants['toolhead'] = 0
+
+# For commands which want to control all toolheads (such as ensuring the heaters are off)
+constants['toolheads'] = [ 0, 1 ] # [0, 1] for two tool heads
+
+# Temperature to heat things up to
+constants['target_temp'] = 100
+
+# Size of the command buffer for reading s3g packets
+constants['buffer_size'] = 512
View
40 tests/s3g_tests/MightyBoard_unsupported_functions.py
@@ -0,0 +1,40 @@
+"""
+List of the unsupported s3g functions. It's easier to list what isn't
+supported than what is.
+
+These functions can be attempted and will return an "unknown command"
+response if they are not buffered.
+"""
+
+unsupported_functions = [
+
+# Host Query Commands
+
+ 'get_position', # 004
+ 'get_communication_stats', # 025
+
+# Host Action Commands (buffered)
+
+ 'queue_point_absolute', # 129
+ 'set_position', # 130
+
+# Tool Query Commands
+
+ 'get_motor1_speed_PWM', # 019
+
+# Tool Action Commands
+
+ 'set_motor1_speed_PWM', # 004
+ 'set_motor1_direction', # 008
+ 'set_servo2_position', # 015
+ 'toggle_abp', # 027
+
+]
+
+
+"""
+List of functions to not attempt: they will return a result which is incorrect.
+(Unsupported functions can be tried and they will return an "unknown command" response.)
+"""
+buggy_functions = [
+]
View
1 tests/s3g_tests/s3g
View
169 tests/s3g_tests/test_s3g_functions.py
@@ -0,0 +1,169 @@
+import os, sys
+lib_path = os.path.abspath('./s3g')
+sys.path.append(lib_path)
+import s3g
+
+"""
+A dictionary of s3g function names and suitable call arguments for testing.
+
+Entries in the dictionary are keyed by the actual name of a method
+function in s3g.py,
+
+ a = s3g.s3g()
+ for f in dir( a ):
+ if inspect.ismethod( getattr( a, f ) ):
+ print "I'm the %s method function of s3g.s3g()" % f
+
+Values in the dictionary take one of three forms:
+
+ None
+ Do not attempt to call the function
+
+ tuple
+ (call-args)
+ Call arguments to pass to the function
+
+ list
+ [ (call-args), 'post-call-statements-string' ]
+ The first list element is the tuple of call argument. The second list element
+ is a string containing an instruction to execute after the function call. A
+ single %s will be replaced with an object of type s3g capable of calling s3g
+ functions.
+
+NOTE BENE: if there's just one call argument AND it is a list, then for the "tuple"
+ case above, use "([a,b,c,...],)". The extra comma after the list preserves the
+ tuple-ness. Without the comma, the value is reduced to "[a,b,c,...]" and becomes
+ indistinguishable from the "list" case above.
+
+Note: Yes, it's possible to design this table with a more uniform value; e.g., use
+ [ (args), 'post-call-string' ] for all entries. However, that's more cumbersome for
+ simple entries which are the majority case. It's hoped that keeping most entries
+ simple will help with maintainability. (OTOH, consistency could be a better choice.)
+"""
+
+s3g_function_calls = {
+
+# Host Query Commands
+
+ 'get_version' : (), # 000
+ 'init' : (), # 001
+ 'get_available_buffer_size' : (), # 002
+ 'clear_buffer' : [ (), 'time.sleep(5)' ], # 003
+ 'get_position' : (), # 004
+ 'abort_immediately' : [ (), 'time.sleep(5)' ], # 007
+ 'pause' : (), # 008
+ 'tool_query' : (0, s3g.slave_query_command_dict['GET_VERSION']), # 010
+ 'is_finished' : (), # 011
+ 'read_from_EEPROM' : (0x00, 1), # 012
+ 'write_to_EEPROM' : (0xFFC, '\xff'), # 013
+ 'capture_to_file' : None, # 014
+ 'end_capture_to_file' : None, # 015
+ 'playback_capture' : None, # 016
+ 'reset' : [ (), 'time.sleep(5)' ], # 017
+ 'get_next_filename' : (False), # 018
+ 'get_build_name' : (), # 020
+ 'get_extended_position' : (), # 021
+ 'extended_stop' : (True, True), # 022
+ 'get_motherboard_status' : (), # 023
+ 'get_build_stats' : (), # 024
+ 'get_communication_stats' : (), # 025
+ 'get_advanced_version' : (), # 027
+
+# Host Action Commands (buffered)
+
+ 'queue_point_absolute' : ([0, 0, 0,], 500), # 129
+ 'set_position' : ([0, 0, 0],), # 130
+ 'find_axes_maximums' : (['x', 'y'], 1, 0), # 131
+ 'find_axes_minimums' : (['z'], 1, 0), # 132
+ 'delay' : (10), # 133
+ 'change_tool' : (0), # 134
+ 'wait_for_tool_ready' : (0, 100, 50), # 135
+ 'tool_action_command' : (0, s3g.slave_action_command_dict['PAUSE']), # 136
+ 'toggle_axes' : (['X', 'Y', 'Z', 'A', 'B'], True), # 137
+ 'queue_extended_point' : ([0, 0, 0, 0, 0], 500), # 139
+ 'set_extended_position' : ([0, 0, 0, 0, 0],), # 140
+ 'wait_for_platform_ready' : (0, 100, 50), # 141
+ 'queue_extended_point_new' : ([0, 0, 0, 0, 0], 1, ['X', 'Y', 'Z', 'A', 'B']), # 142
+ 'store_home_positions' : (['X', 'Y', 'Z', 'A', 'B'],), # 143
+ 'recall_home_positions' : (['X', 'Y', 'Z', 'A', 'B'],), # 144
+ 'set_potentiometer_value' : ('x', 118), # 145
+ 'set_RGB_LED' : (255, 0, 0, 0), # 146
+ 'set_beep' : (1000, 3), # 147
+ 'wait_for_button' : ('up', 0, True, False, False), # 148
+ 'display_message' : (0, 0, "TESTING", 1, False, False, False), # 149
+ 'set_build_percent' : (100), # 150
+ 'queue_song' : (1), # 151
+ 'reset_to_factory' : [ (), 'time.sleep(5)' ], # 152
+ 'build_start_notification' : [ ('aTest'), '%s.build_end_notification()' ], # 153
+ 'build_end_notification' : (), # 154
+
+# Tool Query Commands
+
+ 'get_toolhead_version' : (0), # 000
+ 'get_toolhead_temperature' : (0), # 002
+ 'get_motor1_speed' : (0), # 017
+ 'get_motor1_speed_PWM' : (0), # 019
+ 'is_tool_ready' : (0), # 022
+ 'read_from_toolhead_EEPROM' : (0, 0x00, 0), # 025
+ 'write_to_toolhead_EEPROM' : (0, 0x1FC, '\xff'), # 026
+ 'get_platform_temperature' : (0), # 030
+ 'get_toolhead_target_temperature' : (0), # 032
+ 'get_platform_target_temperature' : (0), # 033
+ 'is_platform_ready' : (0), # 035
+ 'get_tool_status' : (0), # 036
+ 'get_PID_state' : (0), # 037
+
+# Tool Action Commands
+
+ 'toolhead_init' : (0), # 001
+ 'set_toolhead_temperature' : [ (0, 100), '%s.set_toolhead_temperature(0, 0)' ], # 003
+ 'set_motor1_speed_PWM' : (0, 0), # 004
+ 'set_motor1_speed_RPM' : (0, 0), # 006
+ 'set_motor1_direction' : (0, False), # 008
+ 'toggle_motor1' : (0, True, True), # 010
+ 'toggle_fan' : (0, False), # 012
+ 'toggle_extra_output' : (0, False), # 013
+ 'set_servo1_position' : (0, 10), # 014
+ 'set_servo2_position' : (0, 10), # 015
+ 'toolhead_pause' : (0), # 023
+ 'toolhead_abort' : (0), # 024
+ 'toggle_abp' : (0, False), # 027
+ 'set_platform_temperature' : [ (0, 100), '%s.set_platform_temperature(0, 0)' ] # 031
+}
+
+"""
+A list of s3g class methods to ignore.
+"""
+s3g_functions_to_ignore = [ '__init__', 'from_filename' ]
+
+"""
+A list of the s3g functions which are buffered Host Action commands and thus always
+return a success.
+"""
+s3g_buffered_functions = [
+ 'queue_point_absolute', # 129
+ 'set_position', # 130
+ 'find_axes_maximums', # 131
+ 'find_axes_minimums', # 132
+ 'delay', # 133
+ 'change_tool', # 134
+ 'wait_for_tool_ready', # 135
+ 'tool_action_command', # 136
+ 'toggle_axes', # 137
+ 'queue_extended_point', # 139
+ 'set_extended_position', # 140
+ 'wait_for_platform_ready', # 141
+ 'queue_extended_point_new', # 142
+ 'store_home_positions', # 143
+ 'recall_home_positions', # 144
+ 'set_potentiometer_value', # 145
+ 'set_RGB_LED', # 146
+ 'set_beep', # 147
+ 'wait_for_button', # 148
+ 'display_message', # 149
+ 'set_build_percent', # 150
+ 'queue_song', # 151
+ 'reset_to_factory', # 152
+ 'build_start_notification', # 153
+ 'build_end_notification' # 154
+]
View
116 tests/s3g_tests/test_utilities.py
@@ -0,0 +1,116 @@
+import inspect
+import os, sys
+lib_path = os.path.abspath('./s3g')
+sys.path.append(lib_path)
+import s3g
+import serial
+import time
+
+import G3Firmware_constants
+
+"""
+Evaluate the supplied string. The string may contain a '%s' which will be replaced
+with the name of a s3g class object. This allows evaluation of a s3g class member
+function,
+
+ <s3g class object>.<s3g class member function>(args)
+
+For example, to turn a heater off, supply the string
+
+ %s.set_toolhead_temperature(0, 0)
+
+which will get changed to
+
+ obj.set_toolhead_temperature(0, 0)
+
+and then evaluated.
+"""
+def evalStr(obj, string):
+ str = string
+ if str.find('%s') >= 0:
+ str = string % 'obj'
+ try:
+ eval(str)
+ except:
+ print 'Evaluating %s raised an exception, %s' % ( str, sys.exc_info()[0] )
+
+"""
+Call the supplied function with the specified arguments. If isSupported is False,
+then a s3g.CommandNotSupportedError exception will be expected.
+"""
+def callFunc(func, obj, isSupported, args):
+ if isSupported:
+ if isinstance(args, tuple):
+ func(*args)
+ else:
+ func(args)
+ else:
+ if isinstance(args, tuple):
+ obj.assertRaises(s3g.CommandNotSupportedError, func, *args)
+ else:
+ obj.assertRaises(s3g.CommandNotSupportedError, func, args)
+
+"""
+Open a serial connection to the specified port and then clear the serial port's
+I/O buffers.
+"""
+def initSerialComms(port, speed=115200, timeout=1):
+ sp = serial.Serial(port, speed, timeout=timeout)
+ #sp.setRTS() # Helps on Windows
+ #sp.setDTR() # Helps on Windows
+ sp.flushInput()
+ sp.flushOutput()
+ time.sleep(0.1)
+ return sp
+
+"""
+Open a serial connection to the specified port and then instantiate
+a s3g.Writer.StreamWriter() object which uses that serial connection.
+"""
+def initWriterComms(port, speed=115200, timeout=1):
+ return s3g.Writer.StreamWriter(initSerialComms(port, speed, timeout))
+
+"""
+Strip the trailing NUL from the end of a NUL terminated string
+"""
+def ConvertFromNUL(b):
+ if b[-1] != 0:
+ raise TypeError("Cannot convert from non-NUL terminated string")
+ return str(b[:-1])
+
+"""
+Disable the stepper motors
+Set the heater target temperatures to 0
+"""
+def powerDown(obj):
+ if obj is None:
+ return
+ try:
+ # Turn heaters off
+ obj.set_platform_temperature(0, 0)
+ for toolhead in constants['toolheads']:
+ obj.set_toolhead_temperature(toolhead, 0)
+ # Disable stepper motors
+ obj.toggle_axes(['x', 'y', 'z', 'a', 'b'], false)
+ except:
+ pass
+
+"""
+The following functions are used to implement @skipIf and @skipUnless decorators
+"""
+def _id(obj):
+ return obj
+
+def skip(*args, **kwargs):
+ if inspect.isfunction( args[0] ) or inspect.ismethod( args[0] ):
+ print "Skipping " + getattr(args[0], '__name__')
+
+def skipIf(condition):
+ if condition:
+ return skip
+ return _id
+
+def skipUnless(condition):
+ if not condition:
+ return skip
+ return _id

0 comments on commit 2bf291f

Please sign in to comment.