summaryrefslogtreecommitdiff
path: root/doc/HACKING
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2015-10-09 09:28:24 -0400
committerNick Mathewson <nickm@torproject.org>2015-10-09 10:40:53 -0400
commitc751e5af4a87e9511f2c47725152d4a4ccb171b6 (patch)
tree4c7a39775adb1475249210d06a13a5414766b28f /doc/HACKING
parent4da2f89f9522e41212ad32d52cb7cc1b08bc1b06 (diff)
downloadtor-c751e5af4a87e9511f2c47725152d4a4ccb171b6.tar.gz
tor-c751e5af4a87e9511f2c47725152d4a4ccb171b6.zip
Move hacking documentation into a new subdirectory.
Diffstat (limited to 'doc/HACKING')
-rw-r--r--doc/HACKING/GettingStarted.txt207
-rw-r--r--doc/HACKING/HACKING_old (renamed from doc/HACKING)0
-rw-r--r--doc/HACKING/WritingTests.txt401
3 files changed, 608 insertions, 0 deletions
diff --git a/doc/HACKING/GettingStarted.txt b/doc/HACKING/GettingStarted.txt
new file mode 100644
index 0000000000..1f4b43430d
--- /dev/null
+++ b/doc/HACKING/GettingStarted.txt
@@ -0,0 +1,207 @@
+
+
+(DRAFT)
+
+
+
+Getting started in Tor development
+==================================
+
+Congratulations! You've found this file, and you're reading it! This
+means that you might be interested in getting started in developing Tor.
+
+(This guide is just about Tor itself--the small network program at the
+heart of the Tor network--and not about all the other programs in the
+whole Tor ecosystem.)
+
+
+If you are looking for a more bare-bones, less user-friendly information
+dump of important information, you might like reading doc/HACKING
+instead. You should probably read it before you write your first patch.
+
+
+Required background
+-------------------
+
+First, I'm going to assume that you can build Tor from source, and that
+you know enough of the C language to read and write it. (See the README
+file that comes with the Tor source for more information on building it,
+and any high-quality guide to C for information on programming.)
+
+I'm also going to assume that you know a little bit about how to use
+Git, or that you're able to fillow one of the several excellent guides
+at http://git-scm.org to learn.
+
+Most Tor developers develop using some Unix-based system, such as Linux,
+BSD, or OSX. It's okay to develop on Windows if you want, but you're
+going to have a more difficult time.
+
+
+Getting your first patch into Tor
+---------------------------------
+
+Once you've reached this point, here's what you need to know.
+
+ 1) Get the source.
+
+ We keep our source under version control in Git. To get the latest
+ version, run
+ git clone https://git.torproject.org/git/tor
+
+ This will give you a checkout of the master branch. If you're
+ going to fix a bug that appears in a stable version, check out the
+ appropriate "maint" branch, as in:
+
+ git checkout maint-0.2.7
+
+ 2) Find your way around the source
+
+ Our overall code structure is explained in the "torguts" documents,
+ currently at
+ git clone https://git.torproject.org/user/nickm/torguts.git
+
+ Find a part of the code that looks interesting to you, and start
+ looking around it to see how it fits together!
+
+ We do some unusual things in our codebase. Our testing-related
+ practices and kludges are explained in doc/WritingTests.txt.
+
+ If you see something that doesn't make sense, we love to get
+ questions!
+
+ 3) Find something cool to hack on.
+
+ You may already have a good idea of what you'd like to work on, or
+ you might be looking for a way to contribute.
+
+ Many people have gotten started by looking for an area where they
+ personally felt Tor was underperforming, and investigating ways to
+ fix it. If you're looking for ideas, you can head to our bug
+ tracker at trac.torproject.org and look for tickets that have
+ received the "easy" tag: these are ones that developers think would
+ be pretty simple for a new person to work on. For a bigger
+ challenge, you might want to look for tickets with the "lorax"
+ keyword: these are tickets that the developers think might be a
+ good idea to build, but which we have no time to work on any time
+ soon.
+
+ Or you might find another open ticket that piques your
+ interest. It's all fine!
+
+ For your first patch, it is probably NOT a good idea to make
+ something huge or invasive. In particular, you should probably
+ avoid:
+ * Major changes spread across many parts of the codebase.
+ * Major changes to programming practice or coding style.
+ * Huge new features or protocol changes.
+
+ 4) Meet the developers!
+
+ We discuss stuff on the tor-dev mailing list and on the #tor-dev
+ IRC channel on OFTC. We're generally friendly and approachable,
+ and we like to talk about how Tor fits together. If we have ideas
+ about how something should be implemented, we'll be happy to share
+ them.
+
+ We currently have a patch workshop at least once a week, where
+ people share patches they've made and discuss how to make them
+ better. The time might change in the future, but generally,
+ there's no bad time to talk, and ask us about patch ideas.
+
+ 5) Do you need to write a design proposal?
+
+ If your idea is very large, or it will require a change to Tor's
+ protocols, there needs to be a written design proposal before it
+ can be merged. (We use this process to manage changes in the
+ protocols.) To write one, see the instructions at
+ https://gitweb.torproject.org/torspec.git/tree/proposals/001-process.txt
+ . If you'd like help writing a proposal, just ask! We're happy to
+ help out with good ideas.
+
+ You might also like to look around the rest of that directory, to
+ see more about open and past proposed changes to Tor's behavior.
+
+ 6) Writing your patch
+
+ As you write your code, you'll probably want it to fit in with the
+ standards of the rest of the Tor codebase so it will be easy for us
+ to review and merge. You can learn our coding standards in
+ doc/HACKING.
+
+ If your patch is large and/or is divided into multiple logical
+ components, remember to divide it into a series of Git commits. A
+ series of small changes is much easier to review than one big lump.
+
+ 7) Testing your patch
+
+ We prefer that all new or modified code have unit tests for it to
+ ensure that it runs correctly. Also, all code should actually be
+ _run_ by somebody, to make sure it works.
+
+ See doc/WritingTests.txt for more information on how we test things
+ in Tor. If you'd like any help writing tests, just ask! We're
+ glad to help out.
+
+ 8) Submitting your patch
+
+ We review patches through tickets on our bugtracker at
+ trac.torproject.org. You can either upload your patches there, or
+ put them at a public git repository somewhere we can fetch them
+ (like github or bitbucket) and then paste a link on the appropriate
+ trac ticket.
+
+ Once your patches are available, write a short explanation of what
+ you've done on trac, and then change the status of the ticket to
+ needs_review.
+
+ 9) Review, Revision, and Merge
+
+ With any luck, somebody will review your patch soon! If not, you
+ can ask on the IRC channel; sometimes we get really busy and take
+ longer than we should. But don't let us slow you down: you're the
+ one who's offering help here, and we should respect your time and
+ contributions.
+
+ When your patch is reviewed, one of these things will happen:
+
+ * The reviewer will say "looks good to me" and your
+ patch will get merged right into Tor. [Assuming we're not
+ in the middle of a code-freeze window. If the codebase is
+ frozen, your patch will go into the next release series.]
+
+ * OR the reviewer will say "looks good, just needs some small
+ changes!" And then the reviewer will make those changes,
+ and merge the modified patch into Tor.
+
+ * OR the reviewer will say "Here are some questions and
+ comments," followed by a bunch of stuff that the reviewer
+ thinks should change in your code, or questions that the
+ reviewer has.
+
+ At this point, you might want to make the requested changes
+ yourself, and comment on the trac ticket once you have done
+ so. Or if you disagree with any of the comments, you should
+ say so! And if you won't have time to make some of the
+ changes, you should say that too, so that other developers
+ will be able to pick up the unfinished portion
+
+ Congratulations! You have now written your first patch, and gotten
+ it integrated into mainline Tor.
+
+
+Where do I go from here?
+------------------------
+
+doc/HACKING
+
+doc/WritingTests.txt
+
+torguts.git
+
+torspec.git
+
+The design paper
+
+freehaven.net/anonbib
+
+XXXX describe these and add links.
diff --git a/doc/HACKING b/doc/HACKING/HACKING_old
index 5a1454e65f..5a1454e65f 100644
--- a/doc/HACKING
+++ b/doc/HACKING/HACKING_old
diff --git a/doc/HACKING/WritingTests.txt b/doc/HACKING/WritingTests.txt
new file mode 100644
index 0000000000..977b8369f2
--- /dev/null
+++ b/doc/HACKING/WritingTests.txt
@@ -0,0 +1,401 @@
+
+Writing tests for Tor: an incomplete guide
+==========================================
+
+Tor uses a variety of testing frameworks and methodologies to try to
+keep from introducing bugs. The major ones are:
+
+ 1. Unit tests written in C and shipped with the Tor distribution.
+
+ 2. Integration tests written in Python and shipped with the Tor
+ distribution.
+
+ 3. Integration tests written in Python and shipped with the Stem
+ library. Some of these use the Tor controller protocol.
+
+ 4. System tests written in Python and SH, and shipped with the
+ Chutney package. These work by running many instances of Tor
+ locally, and sending traffic through them.
+
+ 5. The Shadow network simulator.
+
+How to run these tests
+----------------------
+
+=== The easy version
+
+To run all the tests that come bundled with Tor, run "make check"
+
+To run the Stem tests as well, fetch stem from the git repository,
+set STEM_SOURCE_DIR to the checkout, and run "make test-stem".
+
+To run the Chutney tests as well, fetch chutney from the git repository,
+set CHUTNEY_PATH to the checkout, and run "make test-network".
+
+To run all of the above, run "make test-full".
+
+To run all of the above, plus tests that require a working connection to the
+internet, run "make test-full-online".
+
+=== Running particular subtests
+
+The Tor unit tests are divided into separate programs and a couple of
+bundled unit test programs.
+
+Separate programs are easy. For example, to run the memwipe tests in
+isolation, you just run ./src/test/test-memwipe .
+
+To run tests within the unit test programs, you can specify the name
+of the test. The string ".." can be used as a wildcard at the end of the
+test name. For example, to run all the cell format tests, enter
+"./src/test/test cellfmt/..". To run
+
+Many tests that need to mess with global state run in forked subprocesses in
+order to keep from contaminating one another. But when debugging a failing test,
+you might want to run it without forking a subprocess. To do so, use the
+"--no-fork" option with a single test. (If you specify it along with
+multiple tests, they might interfere.)
+
+You can turn on logging in the unit tests by passing one of "--debug",
+"--info", "--notice", or "--warn". By default only errors are displayed.
+
+Unit tests are divided into "./src/test/test" and "./src/test/test-slow".
+The former are those that should finish in a few seconds; the latter tend to
+take more time, and may include CPU-intensive operations, deliberate delays,
+and stuff like that.
+
+=== Finding test coverage
+
+Test coverage is a measurement of which lines your tests actually visit.
+
+When you configure Tor with the --enable-coverage option, it should
+build with support for coverage in the unit tests, and in a special
+"tor-cov" binary.
+
+Then, run the tests you'd like to see coverage from. If you have old
+coverage output, you may need to run "reset-gcov" first.
+
+Now you've got a bunch of files scattered around your build directories
+called "*.gcda". In order to extract the coverage output from them, make a
+temporary directory for them and run "./scripts/test/coverage ${TMPDIR}",
+where ${TMPDIR} is the temporary directory you made. This will create a
+".gcov" file for each source file under tests, containing that file's source
+annotated with the number of times the tests hit each line. (You'll need to
+have gcov installed.)
+
+You can get a summary of the test coverage for each file by running
+"./scripts/test/cov-display ${TMPDIR}/*" . Each line lists the file's name,
+the number of uncovered lines, the number of uncovered lines, and the
+coverage percentage.
+
+For a summary of the test coverage for each _function_, run
+"./scripts/test/cov-display -f ${TMPDIR}/*" .
+
+=== Comparing test coverage
+
+Sometimes it's useful to compare test coverage for a branch you're writing to
+coverage from another branch (such as git master, for example). But you
+can't run "diff" on the two coverage outputs directly, since the actual
+number of times each line is executed aren't so important, and aren't wholly
+deterministic.
+
+Instead, follow the instructions above for each branch, creating a separate
+temporary directory for each. Then, run "./scripts/test/cov-diff ${D1}
+${D2}", where D1 and D2 are the directories you want to compare. This will
+produce a diff of the two directories, with all lines normalized to be either
+covered or uncovered.
+
+To count new or modified uncovered lines in D2, you can run:
+
+ "./scripts/test/cov-diff ${D1} ${D2}" | grep '^+ *\#' |wc -l
+
+
+What kinds of test should I write?
+----------------------------------
+
+Integration testing and unit testing are complementary: it's probably a
+good idea to make sure that your code is hit by both if you can.
+
+If your code is very-low level, and its behavior is easily described in
+terms of a relation between inputs and outputs, or a set of state
+transitions, then it's a natural fit for unit tests. (If not, please
+consider refactoring it until most of it _is_ a good fit for unit
+tests!)
+
+If your code adds new externally visible functionality to Tor, it would
+be great to have a test for that functionality. That's where
+integration tests more usually come in.
+
+Unit and regression tests: Does this function do what it's supposed to?
+-----------------------------------------------------------------------
+
+Most of Tor's unit tests are made using the "tinytest" testing framework.
+You can see a guide to using it in the tinytest manual at
+
+ https://github.com/nmathewson/tinytest/blob/master/tinytest-manual.md
+
+To add a new test of this kind, either edit an existing C file in src/test/,
+or create a new C file there. Each test is a single function that must
+be indexed in the table at the end of the file. We use the label "done:" as
+a cleanup point for all test functions.
+
+(Make sure you read tinytest-manual.md before proceeding.)
+
+I use the term "unit test" and "regression tests" very sloppily here.
+
+=== A simple example
+
+Here's an example of a test function for a simple function in util.c:
+
+ static void
+ test_util_writepid(void *arg)
+ {
+ (void) arg;
+
+ char *contents = NULL;
+ const char *fname = get_fname("tmp_pid");
+ unsigned long pid;
+ char c;
+
+ write_pidfile(fname);
+
+ contents = read_file_to_str(fname, 0, NULL);
+ tt_assert(contents);
+
+ int n = sscanf(contents, "%lu\n%c", &pid, &c);
+ tt_int_op(n, OP_EQ, 1);
+ tt_int_op(pid, OP_EQ, getpid());
+
+ done:
+ tor_free(contents);
+ }
+
+This should look pretty familiar to you if you've read the tinytest
+manual. One thing to note here is that we use the testing-specific
+function "get_fname" to generate a file with respect to a temporary
+directory that the tests use. You don't need to delete the file;
+it will get removed when the tests are done.
+
+Also note our use of OP_EQ instead of == in the tt_int_op() calls.
+We define OP_* macros to use instead of the binary comparison
+operators so that analysis tools can more easily parse our code.
+(Coccinelle really hates to see == used as a macro argument.)
+
+Finally, remember that by convention, all *_free() functions that
+Tor defines are defined to accept NULL harmlessly. Thus, you don't
+need to say "if (contents)" in the cleanup block.
+
+=== Exposing static functions for testing
+
+Sometimes you need to test a function, but you don't want to expose
+it outside its usual module.
+
+To support this, Tor's build system compiles a testing version of
+each module, with extra identifiers exposed. If you want to
+declare a function as static but available for testing, use the
+macro "STATIC" instead of "static". Then, make sure there's a
+macro-protected declaration of the function in the module's header.
+
+For example, crypto_curve25519.h contains:
+
+#ifdef CRYPTO_CURVE25519_PRIVATE
+STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret,
+ const uint8_t *basepoint);
+#endif
+
+The crypto_curve25519.c file and the test_crypto.c file both define
+CRYPTO_CURVE25519_PRIVATE, so they can see this declaration.
+
+=== Mock functions for testing in isolation
+
+Often we want to test that a function works right, but the function to
+be tested depends on other functions whose behavior is hard to observe,
+or which require a working Tor network, or something like that.
+
+To write tests for this case, you can replace the underlying functions
+with testing stubs while your unit test is running. You need to declare
+the underlying function as 'mockable', as follows:
+
+ MOCK_DECL(returntype, functionname, (argument list));
+
+and then later implement it as:
+
+ MOCK_IMPL(returntype, functionname, (argument list))
+ {
+ /* implementation here */
+ }
+
+For example, if you had a 'connect to remote server' function, you could
+declare it as:
+
+
+ MOCK_DECL(int, connect_to_remote, (const char *name, status_t *status));
+
+When you declare a function this way, it will be declared as normal in
+regular builds, but when the module is built for testing, it is declared
+as a function pointer initialized to the actual implementation.
+
+In your tests, if you want to override the function with a temporary
+replacement, you say:
+
+ MOCK(functionname, replacement_function_name);
+
+And later, you can restore the original function with:
+
+ UNMOCK(functionname);
+
+For more information, see the definitions of this mocking logic in
+testsupport.h.
+
+=== Okay but what should my tests actually do?
+
+We talk above about "test coverage" -- making sure that your tests visit
+every line of code, or every branch of code. But visiting the code isn't
+enough: we want to verify that it's correct.
+
+So when writing tests, try to make tests that should pass with any correct
+implementation of the code, and that should fail if the code doesn't do what
+it's supposed to do.
+
+You can write "black-box" tests or "glass-box" tests. A black-box test is
+one that you write without looking at the structure of the function. A
+glass-box one is one you implement while looking at how the function is
+implemented.
+
+In either case, make sure to consider common cases *and* edge cases; success
+cases and failure csaes.
+
+For example, consider testing this function:
+
+ /** Remove all elements E from sl such that E==element. Preserve
+ * the order of any elements before E, but elements after E can be
+ * rearranged.
+ */
+ void smartlist_remove(smartlist_t *sl, const void *element);
+
+In order to test it well, you should write tests for at least all of the
+following cases. (These would be black-box tests, since we're only looking
+at the declared behavior for the function:
+
+ * Remove an element that is in the smartlist.
+ * Remove an element that is not in the smartlist.
+ * Remove an element that appears in the smartlist more than once.
+
+And your tests should verify that it behaves correct. At minimum, you should
+test:
+
+ * That other elements before E are in the same order after you call the
+ functions.
+ * That the target element is really removed.
+ * That _only_ the target element is removed.
+
+When you consider edge cases, you might try:
+
+ * Remove an element from an empty list.
+ * Remove an element from a singleton list containing that element.
+ * Remove an element for a list containing several instances of that
+ element, and nothing else.
+
+Now let's look at the implementation:
+
+ void
+ smartlist_remove(smartlist_t *sl, const void *element)
+ {
+ int i;
+ if (element == NULL)
+ return;
+ for (i=0; i < sl->num_used; i++)
+ if (sl->list[i] == element) {
+ sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
+ i--; /* so we process the new i'th element */
+ sl->list[sl->num_used] = NULL;
+ }
+ }
+
+Based on the implementation, we now see three more edge cases to test:
+
+ * Removing NULL from the list.
+ * Removing an element from the end of the list
+ * Removing an element from a position other than the end of the list.
+
+
+=== What should my tests NOT do?
+
+Tests shouldn't require a network connection.
+
+Whenever possible, tests shouldn't take more than a second. Put the test
+into test/slow if it genuinely needs to be run.
+
+Tests should not alter global state unless they run with TT_FORK: Tests
+should not require other tests to be run before or after them.
+
+Tests should not leak memory or other resources.
+
+When possible, tests should not be over-fit to the implementation. That is,
+the test should verify that the documented behavior is implemented, but
+should not break if other permissible behavior is later implemented.
+
+
+=== Advanced techniques: Namespaces
+
+Sometimes, when you're doing a lot of mocking at once, it's convenient to
+isolate your identifiers within a single namespace. If this were C++, we'd
+already have namespaces, but for C, we do the best we can with macros and
+token-pasting.
+
+We have some macros defined for this purpose in src/test/test.h. To use
+them, you define NS_MODULE to a prefix to be used for your identifiers, and
+then use other macros in place of identifier names. See src/test/test.h for
+more documentation.
+
+
+Integration tests: Calling Tor from the outside
+-----------------------------------------------
+
+Some tests need to invoke Tor from the outside, and shouldn't run from the
+same process as the Tor test program. Reasons for doing this might include:
+
+ * Testing the actual behavior of Tor when run from the command line
+ * Testing that a crash-handler correctly logs a stack trace
+ * Verifying that a violating a sandbox or capability requirement will
+ actually crash the program.
+ * Needing to run as root in order to test capability inheritance or
+ user switching.
+
+To add one of these, you generally want a new C program in src/test. Add it
+to TESTS and noinst_PROGRAMS if it can run on its own and return success or
+failure. If it needs to be invoked multiple times, or it needs to be
+wrapped, add a new shell script to TESTS, and the new program to
+noinst_PROGRAMS. If you need access to any environment variable from the
+makefile (eg ${PYTHON} for a python interpreter), then make sure that the
+makefile exports them.
+
+Writing integration tests with Stem
+-----------------------------------
+
+The 'stem' library includes extensive unit tests for the Tor controller
+protocol.
+
+For more information on writing new tests for stem, have a look around
+the tst/* directory in stem, and find a good example to emulate. You
+might want to start with
+https://gitweb.torproject.org/stem.git/tree/test/integ/control/controller.py
+to improve Tor's test coverage.
+
+You can run stem tests from tor with "make test-stem", or see
+https://stem.torproject.org/faq.html#how-do-i-run-the-tests .
+
+System testing with Chutney
+---------------------------
+
+The 'chutney' program configures and launches a set of Tor relays,
+authorities, and clients on your local host. It has a 'test network'
+functionality to send traffic through them and verify that the traffic
+arrives correctly.
+
+You can write new test networks by adding them to 'networks'. To add
+them to Tor's tests, add them to the test-network or test-network-all
+targets in Makefile.am.
+
+(Adding new kinds of program to chutney will still require hacking the
+code.)