A Python library for interacting with the experiment framework.

Adapted from the existing library in /bin/wifi, which now uses this
instead.

Change-Id: I045119c7c8c7c12774a4b6b79682f4889183769f
diff --git a/Makefile b/Makefile
index f433d89..0c5089a 100644
--- a/Makefile
+++ b/Makefile
@@ -89,7 +89,7 @@
 LIBDIR=$(DESTDIR)$(PREFIX)/lib
 
 
-all:     $(addsuffix /all,$(DIRS)) build-optionspy
+all:     $(addsuffix /all,$(DIRS)) build-commonpy
 test:    $(addsuffix /test,$(DIRS))
 clean:   $(addsuffix /clean,$(DIRS))
 	find \( -name '*.pyc' -o -name '*~' \) -exec rm -fv {} \;
@@ -102,7 +102,7 @@
 # other to write site-packages/easy-install.pth.
 install:
 	set -e; for d in $(DIRS); do $(MAKE) -C $$d install; done
-	$(MAKE) install-optionspy
+	$(MAKE) install-commonpy
 	mkdir -p $(BINDIR)
 	rm -fv $(BINDIR)/hnvram
 ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME), gfmn110)
@@ -129,11 +129,11 @@
 %/install:
 	$(MAKE) -C $* install
 
-build-optionspy:
+build-commonpy:
 	PYTHONPATH=$(HOSTPYTHONPATH) $(HOSTDIR)/usr/bin/python setup.py build
 	PYTHONPATH=$(TARGETPYTHONPATH) $(HOSTDIR)/usr/bin/python setup.py build
 
-install-optionspy:
+install-commonpy:
 	PYTHONPATH=$(HOSTPYTHONPATH) $(HOSTDIR)/usr/bin/python setup.py install --prefix=$(HOSTDIR)$(PREFIX)
 	PYTHONPATH=$(TARGETPYTHONPATH) $(HOSTDIR)/usr/bin/python setup.py install --prefix=$(DESTDIR)$(PREFIX)
 
diff --git a/wifi/experiment.py b/experiment.py
similarity index 64%
rename from wifi/experiment.py
rename to experiment.py
index 6f142d6..7ec45ef 100644
--- a/wifi/experiment.py
+++ b/experiment.py
@@ -2,13 +2,13 @@
 
 """Python implementation of the experiment framework."""
 
+import logging
 import os
 import subprocess
 
-import utils
 
-_EXPERIMENTS_TMP_DIR = '/tmp/experiments'
-_EXPERIMENTS_DIR = '/config/experiments'
+EXPERIMENTS_TMP_DIR = '/tmp/experiments'
+EXPERIMENTS_DIR = '/config/experiments'
 
 _experiment_warned = set()
 _experiment_enabled = set()
@@ -18,10 +18,10 @@
   try:
     rv = subprocess.call(['register_experiment', name])
   except OSError as e:
-    utils.log('register_experiment: %s', e)
+    logging.info('register_experiment: %s', e)
   else:
     if rv:
-      utils.log('Failed to register experiment %s.', name)
+      logging.error('Failed to register experiment %s.', name)
 
 
 def enabled(name):
@@ -35,18 +35,18 @@
   Returns:
     Whether the experiment is enabled.
   """
-  if not os.path.exists(os.path.join(_EXPERIMENTS_TMP_DIR,
+  if not os.path.exists(os.path.join(EXPERIMENTS_TMP_DIR,
                                      name + '.available')):
     if name not in _experiment_warned:
       _experiment_warned.add(name)
-      utils.log('Warning: experiment %r not registered.', name)
+      logging.warning('Warning: experiment %r not registered.', name)
   else:
-    is_enabled = os.path.exists(os.path.join(_EXPERIMENTS_DIR,
+    is_enabled = os.path.exists(os.path.join(EXPERIMENTS_DIR,
                                              name + '.active'))
     if is_enabled and name not in _experiment_enabled:
       _experiment_enabled.add(name)
-      utils.log('Notice: using experiment %r.', name)
+      logging.info('Notice: using experiment %r.', name)
     elif not is_enabled and name in _experiment_enabled:
       _experiment_enabled.remove(name)
-      utils.log('Notice: stopping experiment %r.', name)
+      logging.info('Notice: stopping experiment %r.', name)
     return is_enabled
diff --git a/experiment_testutils.py b/experiment_testutils.py
new file mode 100644
index 0000000..f5886e5
--- /dev/null
+++ b/experiment_testutils.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python -S
+
+"""Unit test utils for experiment.py."""
+
+import logging
+import os
+import shutil
+import tempfile
+
+import experiment
+
+
+def enable(name):
+  """Enable an experiment.  For unit tests only."""
+  open(os.path.join(experiment.EXPERIMENTS_TMP_DIR, name + '.available'), 'w')
+  open(os.path.join(experiment.EXPERIMENTS_DIR, name + '.active'), 'w')
+  logging.debug('Enabled %s for unit tests', name)
+
+
+def disable(name):
+  """Enable an experiment.  For unit tests only."""
+  filename = os.path.join(experiment.EXPERIMENTS_DIR, name + '.active')
+  if os.path.exists(filename):
+    os.unlink(filename)
+  logging.debug('Disabled %s for unit tests', name)
+
+
+class MakeExperimentDirs(object):
+  """RAII class for tests which involve experiments.
+
+  Creates temporary experiment directories, and removes them
+  upon deletion.
+  """
+
+  def __init__(self):
+    # pylint: disable=protected-access,missing-docstring
+    experiment.EXPERIMENTS_DIR = tempfile.mkdtemp()
+    experiment.EXPERIMENTS_TMP_DIR = tempfile.mkdtemp()
+
+  def __del__(self):
+    shutil.rmtree(experiment.EXPERIMENTS_DIR)
+    shutil.rmtree(experiment.EXPERIMENTS_TMP_DIR)
diff --git a/setup.py b/setup.py
index e7a3a0e..a09807d 100755
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
 from distutils.core import setup
 
-LONGDESC="""Command-line options parser.
+OPTIONS_LONGDESC="""Command-line options parser.
 With the help of an options spec string, easily parse command-line options.
 
 An options spec is made up of two parts, separated by a line with two dashes.
@@ -32,15 +32,26 @@
 space. The text on that line will be output after an empty line.
 """
 
+options_url = 'https://github.com/apenwarr/bup/blob/master/lib/bup/options.py'
 setup(name='options',
-      version='1.0', 
+      version='1.0',
       description='A command-line options parser.',
       license='BSD',
       author='Avery Pennarun',
       author_email='apenwarr@google.com',
       url='git://github.com/apenwarr/bup.git',
-      download_url='https://github.com/apenwarr/bup/blob/master/lib/bup/options.py',
-      py_modules=["options"],
-      long_description=LONGDESC,
-      keywords="options",
-      )
+      download_url=options_url,
+      py_modules=['options'],
+      long_description=OPTIONS_LONGDESC,
+      keywords='options',
+     )
+
+
+setup(name='experiment',
+      version='1.0',
+      description='Python implementation of the GFiber experiment framework.',
+      author='Richard Frankel',
+      author_email='rofrankel@google.com',
+      py_modules=['experiment'],
+      keywords='experiment',
+     )
diff --git a/wifi/Makefile b/wifi/Makefile
index c6aa330..422b056 100644
--- a/wifi/Makefile
+++ b/wifi/Makefile
@@ -9,6 +9,7 @@
       echo 'echo "(gpylint-missing)" >&2'; \
     fi \
 )
+NOINSTALL=%_test.py options.py experiment.py experiment_testutils.py
 
 all:
 
@@ -34,7 +35,7 @@
 
 install:
 	mkdir -p $(LIBDIR) $(BINDIR)
-	$(INSTALL) -m 0644 $(filter-out %_test.py, $(wildcard *.py)) $(LIBDIR)/
+	$(INSTALL) -m 0644 $(filter-out $(NOINSTALL), $(wildcard *.py)) $(LIBDIR)/
 	$(INSTALL) -m 0755 wifi.py $(LIBDIR)/
 	rm -f $(BINDIR)/wifi
 	ln -s /usr/wifi/wifi.py $(BINDIR)/wifi
diff --git a/wifi/bandsteering_test.py b/wifi/bandsteering_test.py
index 589a6af..507f15a 100755
--- a/wifi/bandsteering_test.py
+++ b/wifi/bandsteering_test.py
@@ -7,16 +7,14 @@
 import tempfile
 
 import bandsteering
-import experiment
+import experiment_testutils
 from wvtest import wvtest
 
 
 def bandsteering_test(f):
   """Decorator for bandsteering tests.
 
-  Creates temporary experiment and bandsteering directories, and removes them
-  after the test.  The test function should take two arguments, experiments_dir
-  and bandsteering_dir.
+  Creates a temporary bandsteering directory, and removes it after the test.
 
   Args:
     f: The function to decorate.
@@ -26,24 +24,21 @@
   """
   # pylint: disable=protected-access,missing-docstring
   def inner():
-    experiments_dir = tempfile.mkdtemp()
-    experiment._EXPERIMENTS_TMP_DIR = experiments_dir
-    experiment._EXPERIMENTS_DIR = experiments_dir
-    bandsteering_dir = tempfile.mkdtemp()
-    bandsteering._BANDSTEERING_DIR = bandsteering_dir
+    bandsteering._BANDSTEERING_DIR = tempfile.mkdtemp()
 
-    f(experiments_dir, bandsteering_dir)
+    try:
+      f()
+    finally:
+      shutil.rmtree(bandsteering._BANDSTEERING_DIR)
 
-    shutil.rmtree(experiments_dir)
-    shutil.rmtree(bandsteering_dir)
-
+  inner.func_name = f.func_name
   return inner
 
 
 @wvtest.wvtest
 @bandsteering_test
 # pylint: disable=unused-argument
-def hostapd_options_no_bandsteering_test(experiments_dir, bandsteering_dir):
+def hostapd_options_no_bandsteering_test():
   """Test bandsteering.hostapd_options when not bandsteering."""
   wvtest.WVPASSEQ([], bandsteering.hostapd_options('2.4', 'my_ssid'))
   wvtest.WVPASSEQ([], bandsteering.hostapd_options('5', 'my_ssid'))
@@ -51,12 +46,12 @@
 
 @wvtest.wvtest
 @bandsteering_test
-def hostapd_options_bandsteering_test(experiments_dir, bandsteering_dir):
+def hostapd_options_bandsteering_test():
   """Test bandsteering.hostapd_options when normally bandsteering."""
-  open(os.path.join(experiments_dir, 'WifiBandsteering.available'), 'a').close()
-  open(os.path.join(experiments_dir, 'WifiBandsteering.active'), 'a').close()
+  unused_raii = experiment_testutils.MakeExperimentDirs()
+  experiment_testutils.enable('WifiBandsteering')
+  bandsteering_dir = bandsteering._BANDSTEERING_DIR
 
-  wvtest.WVPASS(experiment.enabled('WifiBandsteering'))
   wvtest.WVPASSEQ(['-L', os.path.join(bandsteering_dir, '2.4_30abcc9ec8'),
                    '-S', os.path.join(bandsteering_dir, '5_30abcc9ec8')],
                   bandsteering.hostapd_options('2.4', 'my_ssid'))
@@ -66,15 +61,12 @@
 
 @wvtest.wvtest
 @bandsteering_test
-def hostapd_options_reverse_bandsteering_test(experiments_dir,
-                                              bandsteering_dir):
+def hostapd_options_reverse_bandsteering_test():
   """Test bandsteering.hostapd_options when reverse bandsteering."""
-  open(os.path.join(experiments_dir,
-                    'WifiReverseBandsteering.available'), 'a').close()
-  open(os.path.join(experiments_dir,
-                    'WifiReverseBandsteering.active'), 'a').close()
+  unused_raii = experiment_testutils.MakeExperimentDirs()
+  experiment_testutils.enable('WifiReverseBandsteering')
+  bandsteering_dir = bandsteering._BANDSTEERING_DIR
 
-  wvtest.WVPASS(experiment.enabled('WifiReverseBandsteering'))
   wvtest.WVPASSEQ(['-L', os.path.join(bandsteering_dir, '2.4_30abcc9ec8')],
                   bandsteering.hostapd_options('2.4', 'my_ssid'))
   wvtest.WVPASSEQ(['-L', os.path.join(bandsteering_dir, '5_30abcc9ec8'),
@@ -84,11 +76,11 @@
 
 @wvtest.wvtest
 @bandsteering_test
-def hostapd_options_preexisting_dir_test(experiments_dir, bandsteering_dir):
+def hostapd_options_preexisting_dir_test():
   """Test normal bandsteering when there is a preexisting directory."""
-  open(os.path.join(experiments_dir, 'WifiBandsteering.available'), 'a').close()
-  open(os.path.join(experiments_dir, 'WifiBandsteering.active'), 'a').close()
-  wvtest.WVPASS(experiment.enabled('WifiBandsteering'))
+  unused_raii = experiment_testutils.MakeExperimentDirs()
+  experiment_testutils.enable('WifiBandsteering')
+  bandsteering_dir = bandsteering._BANDSTEERING_DIR
 
   # Create a preexisting 2.4 GHz bandsteering directory with a file in it.
   os.makedirs(os.path.join(bandsteering_dir, '2.4_xxxxxxxxxx'))
@@ -108,13 +100,12 @@
 
 @wvtest.wvtest
 @bandsteering_test
-def hostapd_options_logging_test(experiments_dir, bandsteering_dir):
+def hostapd_options_logging_test():
   """Test bandsteering.hostapd_options when when logging only."""
-  open(os.path.join(experiments_dir,
-                    'WifiHostapdLogging.available'), 'a').close()
-  open(os.path.join(experiments_dir, 'WifiHostapdLogging.active'), 'a').close()
+  unused_raii = experiment_testutils.MakeExperimentDirs()
+  experiment_testutils.enable('WifiHostapdLogging')
+  bandsteering_dir = bandsteering._BANDSTEERING_DIR
 
-  wvtest.WVPASS(experiment.enabled('WifiHostapdLogging'))
   wvtest.WVPASSEQ(['-L', os.path.join(bandsteering_dir, '2.4_30abcc9ec8')],
                   bandsteering.hostapd_options('2.4', 'my_ssid'))
   wvtest.WVPASSEQ(['-L', os.path.join(bandsteering_dir, '5_30abcc9ec8')],
diff --git a/wifi/configs_test.py b/wifi/configs_test.py
index ece4ca9..684c45f 100755
--- a/wifi/configs_test.py
+++ b/wifi/configs_test.py
@@ -5,7 +5,7 @@
 import subprocess
 
 import configs
-import experiment
+import experiment_testutils
 import utils
 from wvtest import wvtest
 
@@ -399,6 +399,8 @@
 @wvtest.wvtest
 def generate_hostapd_config_test():
   """Tests generate_hostapd_config."""
+  unused_raii = experiment_testutils.MakeExperimentDirs()
+
   opt = FakeOptDict()
 
   # Test a simple case.
@@ -443,10 +445,7 @@
   opt.encryption = default_encryption
 
   # Now enable extra short timeout intervals and the Wifi80211k experiment.
-  experiment._EXPERIMENTS_TMP_DIR = '/tmp'
-  experiment._EXPERIMENTS_DIR = '/tmp'
-  open('/tmp/Wifi80211k.available', 'a').close()
-  open('/tmp/Wifi80211k.active', 'a').close()
+  experiment_testutils.enable('Wifi80211k')
   opt.extra_short_timeouts = 2
   new_config = '\n'.join((
       _HOSTAPD_CONFIG,
diff --git a/wifi/experiment.py b/wifi/experiment.py
new file mode 120000
index 0000000..b9d7638
--- /dev/null
+++ b/wifi/experiment.py
@@ -0,0 +1 @@
+../experiment.py
\ No newline at end of file
diff --git a/wifi/experiment_testutils.py b/wifi/experiment_testutils.py
new file mode 120000
index 0000000..cca000f
--- /dev/null
+++ b/wifi/experiment_testutils.py
@@ -0,0 +1 @@
+../experiment_testutils.py
\ No newline at end of file