diff --git a/.circleci/config.yml b/.circleci/config.yml index 5cd7ecc8e1..c44c286790 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/python:3.7.2 + - image: circleci/python:3.7.2-browsers steps: - checkout - run: mkdir test-reports @@ -14,16 +14,20 @@ jobs: python3 -m venv venv . venv/bin/activate pip install -r gradio.egg-info/requires.txt + pip install selenium==4.0.0a6.post2 - save_cache: key: deps1-{{ .Branch }}-{{ checksum "gradio.egg-info/requires.txt" }} paths: - "venv" + - run: + command: | + mkdir screenshots - run: command: | . venv/bin/activate python3 -m unittest - store_artifacts: - path: test-reports/ - destination: tr1 + path: /home/circleci/project/test/tmp + destination: screenshots - store_test_results: path: test-reports/ \ No newline at end of file diff --git a/build/lib/gradio/interface.py b/build/lib/gradio/interface.py index b3486a7da0..398499a43c 100644 --- a/build/lib/gradio/interface.py +++ b/build/lib/gradio/interface.py @@ -122,6 +122,8 @@ class Interface: self.flagging_dir = flagging_dir Interface.instances.add(self) self.analytics_enabled=analytics_enabled + self.launch_port = None + self.save_to = None data = {'fn': fn, 'inputs': inputs, @@ -341,7 +343,6 @@ class Interface: server_port, app, thread = networking.start_server( self, self.server_port) path_to_local_server = "http://{}:{}/".format(self.server_name, server_port) - self.server_port = server_port self.status = "RUNNING" self.server = app @@ -365,7 +366,7 @@ class Interface: print("This share link will expire in 6 hours. If you need a " "permanent link, email support@gradio.app") try: - share_url = networking.setup_tunnel(server_port) + share_url = networking.setup_tunnel(self.launch_port) print("Running on External URL:", share_url) except RuntimeError: data = {'error': 'RuntimeError in launch method'} diff --git a/build/lib/gradio/networking.py b/build/lib/gradio/networking.py index ee32b50dd5..78300d9cfb 100644 --- a/build/lib/gradio/networking.py +++ b/build/lib/gradio/networking.py @@ -173,6 +173,8 @@ def start_server(interface, server_port=None): app.cwd = os.getcwd() log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) + if interface.save_to is not None: + interface.save_to["port"] = port process = threading.Thread(target=app.run, kwargs={"port": port}) process.start() return port, app, process diff --git a/build/lib/gradio/static/js/vendor/fabric.js b/build/lib/gradio/static/js/vendor/fabric.js index d565954041..88388be634 100644 --- a/build/lib/gradio/static/js/vendor/fabric.js +++ b/build/lib/gradio/static/js/vendor/fabric.js @@ -930,7 +930,7 @@ fabric.Collection = { /** * Returns true if context has transparent pixel - * at specified location (taking tolerance into account) + * at specified location (taking TOLERANCE into account) * @param {CanvasRenderingContext2D} ctx context * @param {Number} x x coordinate * @param {Number} y y coordinate @@ -938,7 +938,7 @@ fabric.Collection = { */ isTransparent: function(ctx, x, y, tolerance) { - // If tolerance is > 0 adjust start coords to take into account. + // If TOLERANCE is > 0 adjust start coords to take into account. // If moves off Canvas fix to 0 if (tolerance > 0) { if (x > tolerance) { @@ -959,7 +959,7 @@ fabric.Collection = { imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1), l = imageData.data.length; - // Split image data - for tolerance > 1, pixelDataSize = 4; + // Split image data - for TOLERANCE > 1, pixelDataSize = 4; for (i = 3; i < l; i += 4) { temp = imageData.data[i]; _isTransparent = temp <= 0; diff --git a/demo/diff_texts.py b/demo/diff_texts.py index b4d63f1cd8..8a05b5ed91 100644 --- a/demo/diff_texts.py +++ b/demo/diff_texts.py @@ -21,8 +21,9 @@ io = gr.Interface( "+": "lightgreen", "-": "pink", " ": "none", - }) -) + })) io.test_launch() -io.launch() + +if __name__ == "__main__": + io.launch() diff --git a/demo/image_mod.py b/demo/image_mod.py index 871ff3eb82..68512f51de 100644 --- a/demo/image_mod.py +++ b/demo/image_mod.py @@ -6,7 +6,7 @@ import gradio as gr def image_mod(image): return image.rotate(45) - + io = gr.Interface(image_mod, gr.inputs.Image(type="pil"), "image", @@ -15,8 +15,9 @@ io = gr.Interface(image_mod, ["images/cheetah2.jpg"], ["images/lion.jpg"], ], - live=True, - ) + live=True) io.test_launch() -io.launch() + +if __name__ == "__main__": + io.launch() diff --git a/demo/longest_word.py b/demo/longest_word.py index 74f9cd87bb..34a2db57d1 100644 --- a/demo/longest_word.py +++ b/demo/longest_word.py @@ -9,7 +9,10 @@ def longest_word(text): ex = "The quick brown fox jumped over the lazy dog." -io = gr.Interface(longest_word, "textbox", "label", interpretation="default", examples=[[ex]]) +io = gr.Interface(longest_word, "textbox", "label", + interpretation="default", examples=[[ex]]) io.test_launch() -io.launch() \ No newline at end of file + +if __name__ == "__main__": + io.launch() diff --git a/demo/matrix_transpose.py b/demo/matrix_transpose.py index 90987501fc..7293194a76 100644 --- a/demo/matrix_transpose.py +++ b/demo/matrix_transpose.py @@ -14,4 +14,6 @@ io = gr.Interface( ) io.test_launch() -io.launch() + +if __name__ == "__main__": + io.launch() diff --git a/demo/screenshots/diff_texts/1.png b/demo/screenshots/diff_texts/1.png index 7742b7c00a..69936b6e7e 100644 Binary files a/demo/screenshots/diff_texts/1.png and b/demo/screenshots/diff_texts/1.png differ diff --git a/demo/screenshots/image_mod/1.png b/demo/screenshots/image_mod/1.png deleted file mode 100644 index 410e6cff9a..0000000000 Binary files a/demo/screenshots/image_mod/1.png and /dev/null differ diff --git a/demo/screenshots/image_mod/cheetah1.png b/demo/screenshots/image_mod/cheetah1.png new file mode 100644 index 0000000000..2290541b02 Binary files /dev/null and b/demo/screenshots/image_mod/cheetah1.png differ diff --git a/demo/screenshots/image_mod/cheetah2.png b/demo/screenshots/image_mod/cheetah2.png new file mode 100644 index 0000000000..6df68af06d Binary files /dev/null and b/demo/screenshots/image_mod/cheetah2.png differ diff --git a/demo/screenshots/image_mod/lion.png b/demo/screenshots/image_mod/lion.png new file mode 100644 index 0000000000..b141c82a40 Binary files /dev/null and b/demo/screenshots/image_mod/lion.png differ diff --git a/demo/sentence_builder.py b/demo/sentence_builder.py index e7f7c46a88..6b255d59ef 100644 --- a/demo/sentence_builder.py +++ b/demo/sentence_builder.py @@ -24,4 +24,6 @@ io = gr.Interface( ]) io.test_launch() -io.launch() + +if __name__ == "__main__": + io.launch() diff --git a/gradio.egg-info/SOURCES.txt b/gradio.egg-info/SOURCES.txt index ac3e1f3e26..39513a52d0 100644 --- a/gradio.egg-info/SOURCES.txt +++ b/gradio.egg-info/SOURCES.txt @@ -120,6 +120,7 @@ gradio/static/js/vendor/wavesurfer.min.js gradio/static/js/vendor/webcam.min.js gradio/static/js/vendor/white-theme.js gradio/templates/index.html +test/test_diff_texts.py test/test_inputs.py test/test_interfaces.py test/test_interpretation.py diff --git a/gradio/interface.py b/gradio/interface.py index a6e3332d5c..a0c6f000e4 100644 --- a/gradio/interface.py +++ b/gradio/interface.py @@ -119,6 +119,8 @@ class Interface: self.flagging_dir = flagging_dir Interface.instances.add(self) self.analytics_enabled=analytics_enabled + self.launch_port = None + self.save_to = None data = {'fn': fn, 'inputs': inputs, @@ -338,7 +340,6 @@ class Interface: server_port, app, thread = networking.start_server( self, self.server_name, self.server_port) path_to_local_server = "http://{}:{}/".format(self.server_name, server_port) - self.server_port = server_port self.status = "RUNNING" self.server = app @@ -362,7 +363,7 @@ class Interface: print("This share link will expire in 6 hours. If you need a " "permanent link, email support@gradio.app") try: - share_url = networking.setup_tunnel(server_port) + share_url = networking.setup_tunnel(self.launch_port) print("Running on External URL:", share_url) except RuntimeError: data = {'error': 'RuntimeError in launch method'} diff --git a/gradio/networking.py b/gradio/networking.py index 3890db134a..f467ba8cf1 100644 --- a/gradio/networking.py +++ b/gradio/networking.py @@ -181,11 +181,14 @@ def start_server(interface, server_name, server_port=None): app.cwd = os.getcwd() log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) - thread = threading.Thread(target=app.run, kwargs={"port": port, "host": server_name}, daemon=True) + if interface.save_to is not None: + interface.save_to["port"] = port + thread = threading.Thread(target=app.run, + kwargs={"port": port, "host": server_name}, + daemon=True) thread.start() return port, app, thread - def close_server(process): process.terminate() process.join() diff --git a/gradio/static/js/vendor/fabric.js b/gradio/static/js/vendor/fabric.js index d565954041..88388be634 100644 --- a/gradio/static/js/vendor/fabric.js +++ b/gradio/static/js/vendor/fabric.js @@ -930,7 +930,7 @@ fabric.Collection = { /** * Returns true if context has transparent pixel - * at specified location (taking tolerance into account) + * at specified location (taking TOLERANCE into account) * @param {CanvasRenderingContext2D} ctx context * @param {Number} x x coordinate * @param {Number} y y coordinate @@ -938,7 +938,7 @@ fabric.Collection = { */ isTransparent: function(ctx, x, y, tolerance) { - // If tolerance is > 0 adjust start coords to take into account. + // If TOLERANCE is > 0 adjust start coords to take into account. // If moves off Canvas fix to 0 if (tolerance > 0) { if (x > tolerance) { @@ -959,7 +959,7 @@ fabric.Collection = { imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1), l = imageData.data.length; - // Split image data - for tolerance > 1, pixelDataSize = 4; + // Split image data - for TOLERANCE > 1, pixelDataSize = 4; for (i = 3; i < l; i += 4) { temp = imageData.data[i]; _isTransparent = temp <= 0; diff --git a/test/golden/diff_texts/magic_trick.png b/test/golden/diff_texts/magic_trick.png new file mode 100644 index 0000000000..44e0570366 Binary files /dev/null and b/test/golden/diff_texts/magic_trick.png differ diff --git a/test/golden/image_mod/cheetah1.png b/test/golden/image_mod/cheetah1.png new file mode 100644 index 0000000000..9884faa4ff Binary files /dev/null and b/test/golden/image_mod/cheetah1.png differ diff --git a/test/golden/longest_word/wonderful.png b/test/golden/longest_word/wonderful.png new file mode 100644 index 0000000000..ed2e5b1891 Binary files /dev/null and b/test/golden/longest_word/wonderful.png differ diff --git a/test/golden/sentence_builder/two_cats.png b/test/golden/sentence_builder/two_cats.png new file mode 100644 index 0000000000..3d8831d172 Binary files /dev/null and b/test/golden/sentence_builder/two_cats.png differ diff --git a/test/images/cheetah1.jpg b/test/images/cheetah1.jpg new file mode 100644 index 0000000000..c510ff30e0 Binary files /dev/null and b/test/images/cheetah1.jpg differ diff --git a/test/images/cheetah2.jpg b/test/images/cheetah2.jpg new file mode 100644 index 0000000000..1408ea5af6 Binary files /dev/null and b/test/images/cheetah2.jpg differ diff --git a/test/images/lion.jpg b/test/images/lion.jpg new file mode 100644 index 0000000000..e9bf9f5d08 Binary files /dev/null and b/test/images/lion.jpg differ diff --git a/test/test_demos.py b/test/test_demos.py new file mode 100644 index 0000000000..58895dd0f2 --- /dev/null +++ b/test/test_demos.py @@ -0,0 +1,223 @@ +import unittest +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +import multiprocessing +import time +import requests +from matplotlib.testing.compare import compare_images +import random +import os + +current_dir = os.getcwd() + +LOCAL_HOST = "http://localhost:{}" +GOLDEN_PATH = "test/golden/{}/{}.png" +TOLERANCE = 0.1 +TIMEOUT = 10 + + +def wait_for_url(url): + for i in range(TIMEOUT): + try: + requests.get(url) + print("Interface connected.") + break + except: + time.sleep(0.2) + else: + raise ConnectionError("Could not connect to interface.") + + +def hide_latency(driver): + js = "document.getElementsByClassName('loading_time')[" \ + "0].style.visibility = " \ + "'hidden';" + driver.execute_script(js) + + +def diff_texts_thread(return_dict): + from demo.diff_texts import io + io.save_to = return_dict + io.launch() + + +def image_mod_thread(return_dict): + from demo.image_mod import io + io.examples = None + io.save_to = return_dict + io.launch() + + +def longest_word_thread(return_dict): + from demo.longest_word import io + io.save_to = return_dict + io.launch() + + +def sentence_builder_thread(return_dict): + from demo.sentence_builder import io + io.save_to = return_dict + io.launch() + + +class TestDemo(unittest.TestCase): + def start_test(self, target): + manager = multiprocessing.Manager() + return_dict = manager.dict() + self.i_thread = multiprocessing.Process(target=target, + args=(return_dict,)) + self.i_thread.start() + total_sleep = 0 + while not return_dict and total_sleep < TIMEOUT: + time.sleep(0.2) + total_sleep += 0.2 + URL = LOCAL_HOST.format(return_dict["port"]) + wait_for_url(URL) + + driver = webdriver.Chrome() + driver.set_window_size(1200, 800) + driver.get(URL) + + return driver + + def test_diff_texts(self): + driver = self.start_test(target=diff_texts_thread) + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".input_interface[interface_id='0'] .input_text")) + ) + elem.clear() + elem.send_keys("Want to see a magic trick?") + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".input_interface[interface_id='1'] .input_text")) + ) + elem.clear() + elem.send_keys("Let's go see a magic trick!") + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".submit")) + ) + elem.click() + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".output_interface[interface_id='2'] .output_text")) + ) + + total_sleep = 0 + while elem.text == "" and total_sleep < TIMEOUT: + time.sleep(0.2) + total_sleep += 0.2 + + self.assertEqual(elem.text, "LeWant's tgo see a magic trick?!") + golden_img = os.path.join(current_dir, GOLDEN_PATH.format( + "diff_texts", "magic_trick")) + tmp = os.path.join(current_dir, "test/tmp/{}.png".format( + random.getrandbits(32))) + hide_latency(driver) + driver.save_screenshot(tmp) + driver.close() + self.assertIsNone(compare_images(tmp, golden_img, TOLERANCE)) + os.remove(tmp) + + def test_image_mod(self): + driver = self.start_test(target=image_mod_thread) + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".input_interface[" + "interface_id='0'] " + ".hidden_upload")) + ) + hide_latency(driver) + cwd = os.getcwd() + rel = "demo/images/cheetah1.jpg" + elem.send_keys(os.path.join(cwd, rel)) + golden_img = os.path.join(current_dir, GOLDEN_PATH.format( + "image_mod", "cheetah1")) + tmp = os.path.join(current_dir, "test/tmp/{}.png".format( + random.getrandbits(32))) + WebDriverWait(driver, TIMEOUT).until( + EC.visibility_of_element_located((By.CSS_SELECTOR, + ".output_interface[" + "interface_id='1'] " + ".output_image")) + ) + + hide_latency(driver) + driver.save_screenshot(tmp) + self.assertIsNone(compare_images(tmp, golden_img, TOLERANCE)) + os.remove(tmp) + driver.close() + + def test_longest_word(self): + driver = self.start_test(target=longest_word_thread) + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".input_interface[interface_id='0'] .input_text")) + ) + elem.send_keys("This is the most wonderful machine learning " + "library.") + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".submit")) + ) + elem.click() + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".output_interface[" + "interface_id='1'] .output_class")) + ) + + total_sleep = 0 + while elem.text == "" and total_sleep < TIMEOUT: + time.sleep(0.2) + total_sleep += 0.2 + + golden_img = os.path.join(current_dir, GOLDEN_PATH.format( + "longest_word", "wonderful")) + tmp = os.path.join(current_dir, "test/tmp/{}.png".format( + random.getrandbits(32))) + hide_latency(driver) + driver.save_screenshot(tmp) + driver.close() + self.assertIsNone(compare_images(tmp, golden_img, TOLERANCE)) + os.remove(tmp) + + def test_sentence_builder(self): + driver = self.start_test(target=sentence_builder_thread) + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".submit")) + ) + elem.click() + elem = WebDriverWait(driver, TIMEOUT).until( + EC.presence_of_element_located((By.CSS_SELECTOR, + ".output_interface[" + "interface_id='5'] .output_text")) + ) + + total_sleep = 0 + while elem.text == "" and total_sleep < TIMEOUT: + time.sleep(0.2) + total_sleep += 0.2 + + self.assertEqual(elem.text, "The 2 cats went to the park where they until the night") + golden_img = os.path.join(current_dir, GOLDEN_PATH.format( + "sentence_builder", "two_cats")) + tmp = os.path.join(current_dir, "test/tmp/{}.png".format( + random.getrandbits(32))) + hide_latency(driver) + driver.save_screenshot(tmp) + self.assertIsNone(compare_images(tmp, golden_img, TOLERANCE)) + os.remove(tmp) + driver.close() + + def tearDown(self): + self.i_thread.terminate() + self.i_thread.join() + + +if __name__ == '__main__': + unittest.main() diff --git a/test/tmp/tmp.txt b/test/tmp/tmp.txt new file mode 100644 index 0000000000..9df62d3b49 --- /dev/null +++ b/test/tmp/tmp.txt @@ -0,0 +1 @@ +# tmp files saved here \ No newline at end of file