The future of HCI: Multi-Touch UI
By Jeff Han at the NYU Media Research Lab
Update March 2007: A year later
By Jeff Han at the NYU Media Research Lab
Update March 2007: A year later
Posted by
Unknown
at
Thursday, September 14, 2006
0
comments
Bodies are like diskettes with tags. You click on to them and you can see the size and type of file immediately. On people, this labeling occurs on the face.
Posted by
Unknown
at
Monday, September 04, 2006
0
comments
The perfect 'epilogue' for The Design Of Everyday Things
Posted by
Unknown
at
Saturday, September 02, 2006
0
comments
TestNG is a flexible testing framework developed by Cédric Beust and Alexandru Popescu. It is a much more powerful alternative to JUnit that fixes and improves many of its shortcomings.
In this introductory guide I will show how to setup and integrate TestNG with IDEA, hoping to highlight specific gems of TestNG in the process.
TestNG at a Glance
Here's a short list of my favorite features:
IDEA Setup
Figure 1 - Download and install the TestNG plug-in for IDEA [click on image to enlarge]
Figure 2 - Module Settings of project [click on image to enlarge]
Figure 3 - Add TestNG jar to project's classpath [click on image to enlarge]
This completes the integration of the testing library with IDEA, and we are now ready to write and test some code.
Exploring the Next Generation of Testing
Simply put, a TestNG unit test class is a POJO with annotated methods. There is no requirement to extend a specific class or implement a specific interface, just tag methods with Java annotations. A very simple Test class looks like this:
package testng.basic;
import org.testng.annotations.Test;
public class FirstTest {
public FirstTest() {
}
@Test
public void isOneEqualsTwo(){
assert(1 == 2);
}
}
I wrote a simple Java class and will use it to illustrate some key features of TestNG, namely the ability to specify individual method thread pools, the ability to specify certain test methods as dependent on the successful completion of others, and the seamless integration with IDEA.
Create a new class named ThreadUnsafe on IDEA and copy and paste the code below:
import org.testng.annotations.Test;
public class ThreadUnsafe {
private static int[] accounts = new int[] {0, 0};
private static int MAX = 1000;
@Test(threadPoolSize = 1000, invocationCount = 1000, timeOut = 2000)
public void swap(){
int amount = (int) (MAX * Math.random());
accounts[1] += amount;
accounts[0] -= amount;
System.out.println("Account[0]: " + accounts[0]);
System.out.println("Account[1]: " + accounts[1]);
int balance = checkBalance();
assert(balance == 0);
}
public int checkBalance(){
int sum = 0;
for (int i = 0; i < accounts.length; i++){
sum += accounts[i];
}
System.out.println("Balance : " + sum);
return sum;
}
public static void main(String[] args){
ThreadUnsafe tu = new ThreadUnsafe();
tu.swap();
}
}
ThreadUnsafe is a very simple class that swaps random values from one account to another, ensuring the balance remains zero. That is, when we add an amount to one of the accounts, we subtract the same amount from the other.
Compile and run the code. It should exit printing Balance : 0.
Specifying thread pools
The only different and interesting thing on the code above is the annotation @Test on method swap():@Test(threadPoolSize = 1000, invocationCount = 1000, timeOut = 2000)
This annotation is saying "give me a pool of 1000 threads, invoke this 1000 times, and exit if an invocation takes longer than 2000 milliseconds to return".
If a method takes longer than the specified timeout, TestNG will interrupt the method and mark it as unsuccessful.
To debug this sample project select the TestNG tab from the Debug panel. From there we can specify the desired granularity of the test, which can range from the swap() method to the whole package. I chose to test the ThreadUnsafe class itself.
Figure 4 - Debug granularity of TestNG [click on image to enlarge]
The output of running the project in debug mode can be seen below (values will vary):
Figure 5 - TestNG output [click on image to enlarge]
On the output console shown above we can see that after a few successful runs the value of Balance is 0 as expected. However, later on some weird values start showing up. Thus, multiple threads interfered with each other and the test shows the code to be thread unsafe.
This brute force thread safety testing can be useful to confirm a bug report due to improper synchronization, though it can be tricky to come up with a representative number of threads and repetitions.
Specifying method dependencies
The ability to specify certain test methods as dependent on the successful completion of others is a very useful feature. Let's move the initialization into a method of its own. Note changes on the accounts variable declaration and init() method.
import org.testng.annotations.Test;
public class ThreadUnsafe {
private static int[] accounts;
private static int MAX = 1000;
@Test
public void init() {
accounts = new int[]{0, 0};
}
@Test(threadPoolSize = 1000, invocationCount = 1000, timeOut = 2000)
public void swap(){
int amount = (int) (MAX * Math.random());
accounts[1] += amount;
accounts[0] -= amount;
System.out.println("Account[0]: " + accounts[0]);
System.out.println("Account[1]: " + accounts[1]);
int balance = checkBalance();
assert(balance == 0);
}
public int checkBalance(){
int sum = 0;
for (int i = 0; i < accounts.length; i++){
sum += accounts[i];
}
System.out.println("Balance : " + sum);
return sum;
}
public static void main(String[] args){
ThreadUnsafe tu = new ThreadUnsafe();
tu.init();
tu.swap();
}
}
The difference from the previous code is that initialization is now done on the init() method, which is itself a @Test annotated method that will be included in the testing report.
Now running the code in Debug TestNG mode results in an error because the array accounts used by the swap() method has not been initialized.
Figure 6 - NullPointerException caused by non-initialized dependency [click on image to enlarge]
What we want here is the ability to tell that a certain test method, swap(), depends on the successful completion of a previous test method, init().
We want to guarantee that certain methods or groups of methods are always invoked before others.
TestNG let's us do that with the dependsOnMethods annotation.
To specify swap() as being dependent on the successful execution of init(), we use the dependsOnMethods annotation as shown below:
@Test(dependsOnMethods = {"init"}, threadPoolSize = 1000, invocationCount = 1000, timeOut = 2000)
public void swap() {
...
}
Running the code in debug TestNG mode now results in a successful execution:
Figure 7 - Method dependency with TestNG [click on image to enlarge]
For unreliable systems TestNG introduces the notion of partial failure:@Test(timeOut = 10000, invocationCount = 1000, successPercentage = 98)
public void waitForAnswer() {
while (!success){
Thread.sleep(1000);
}
}
The example above instructs TestNG to invoke the method a thousand times, but to consider the overall test passed even if only 98% of them succeed.
All the above are simple yet very powerful examples that are either very hard or impossible to do with JUnit.
Resources
Posted by
Unknown
at
Tuesday, August 22, 2006
1 comments
I'm on the prowl for new gadgets (smart-phone, wide-screen TV, games console), and I think a market like Digg for consumer electronics would help prune the search space.
With the number and combination of brands, specs, makers, designs, features, personal preferences and whatnot involved, such an application would tap into the wisdom of crowds and transform many diverse opinions into a single collective judgement.
This is the central thesis of the Wisdom of Crowds:
"With most things, the average is mediocrity. With decision making, it's often
excellence. We've been programmed to be collectively smart".
"The best I can tell you is Digg as a concept can be applied to other content aside from news. Just wait and see what we’re going to apply it to."
"Now you can clash and compare your best consumer products for low price offers.
Product Clash features cheap cameras, cheap cell phones, cheap computers, cool
gadgets, cheap home entertainment, cheap peripherals and portable media
comparison clashes! Clashing is fun for movies, music, games and DVDs."
Labels: digg, folksonomy, shopping
Posted by
Unknown
at
Thursday, August 17, 2006
0
comments
I agree 100% with these arguments, being an active but often frustrated OO user for over 5 years.
Decoupling the document from the vendor suite (with the Open Document format) was a tremendous achievement, but the road ahead for OO does not look so promising:
Posted by
Unknown
at
Thursday, August 17, 2006
0
comments
Here are a few useful Python tips I’ve learned over time.
1. When using the '%' format operator always put a tuple or a dictionary on the right hand side.
Instead of:
print "output %s" % stuff
Write:
print "output %s" % (stuff,)
With the tuple on the right hand side, if stuff is itself a tuple with more than one element we'll still get its representation instead of an error.
Example:
>>> def output(arg):
print "output %s" % arg
>>> output("one item")
output one item
>>> output(('single tuple',))
output single tuple
>>> output(('tuple','multiple','items'))
Traceback (most recent call last):
File "
output(('tuple','multiple','items'))
File "
print "output %s" % arg
TypeError: not all arguments converted during string formatting
Now, if the function output is changed to:
>>> def output(arg):
print "output %s" % (arg,)
>>> output(('tuple','multiple','items'))
output ('tuple', 'multiple', 'items')
It will always work as intended and expected.
2. Use the built-in timer function proactively and aggressively to avoid "premature pessimization".
Python has a very useful built-in timing framework, the timeit module, which can be used interactively to time the execution of short pieces of code.
Suppose we want to find out if a hypothetical word_count implementation is faster using the split() method or using a loop.
We'd like to implement each variant, call each implementation many times, repeat the entire test a few times, and select the one that took the least time.
Timeit.py to the rescue. Let's test the implementation using split() first.
>>> import timeit
>>> def word_count():
s = "long string with several words to be counted "
return len(s.split())
>>> word_count()
8
>>> t = timeit.Timer(setup ='from __main__ import word_count', stmt='word_count()')
>>> t.repeat(3, 1000000)
[4.6016188913206406, 4.5184541602204717, 4.5227482723247476]
And now let's test a loop variant.
>>> def word_count():
s = "long string with several words to be counted "
return len([c for c in s if c.isspace()])
>>> word_count()
8
>>> t = timeit.Timer(setup ='from __main__ import word_count', stmt='word_count()')
>>> t.repeat(3, 1000000)
[17.766925246011169, 17.784756763845962, 17.890987803859275]
We have our informed answer right there and then.
The first argument of repeat() is the number of times to repeat the entire test, and the second argument is the number of times to execute the timed statement per test.
You can even select the best out of X runs (3 on this example) by using the min function
>>> min(t.repeat(3, 1000000))
17.766925246011169
We can try and compare other implementations such as a loop without the (expensive) call to isspace().
>>> def word_count():
s = "long string with several words to be counted "
return len([c for c in s if c == ' '])
>>> word_count()
8
>>> t = timeit.Timer(setup ='from __main__ import word_count', stmt='word_count()')
>>> t.repeat(3, 1000000)
[8.8144601897920438, 8.7707542444240971, 8.7721205513323639]
Which proves faster than our second implementation but still slower than calling split().
Note:
Instead of repeat() we can call timeit(), which calls the function 1 million times and returns the number of seconds it took to do it.
3. Don't traverse to append, extend instead.
Don't do:
>>> def bad_append():
l1 = ["long","string","with","long"]
l2 = ["elements","and","words","to","be","counted","or","words"]
for item in l2:
l1.append(item)
>>> t = timeit.Timer(setup ='from __main__ import bad_append', stmt='bad_append()')
>>> min(t.repeat(3, 1000000))
5.4943255206744652
Do instead:
>>> def good_append():
l1 = ["long","string","with","long"]
l2 = ["elements","and","words","to","be","counted","or","words"]
l1.extend(l2)
>>> t = timeit.Timer(setup ='from __main__ import good_append', stmt='good_append()')
>>> min(t.repeat(3, 1000000))
2.3049167103836226
Calling extend() results in an almost 60% performance gain.
4. Beware of doing string concatenation using '+'.
Let's see why with "no fluff just stuff" by applying golden rule 2 above.
Bad:
>>> def bad_concat():
s = ""
l = ["items", "to", "append"]
for sub in l:
s += sub
>>> t = timeit.Timer(setup ='from __main__ import bad_concat', stmt='bad_concat()')
>>> min(t.repeat(3, 1000000))
1.6777893348917132
Better:
>>> def good_concat():
s = ""
l = ["items", "to","append"]
s = "".join(l)
>>> t = timeit.Timer(setup ='from __main__ import good_concat', stmt='good_concat()')
>>> min(t.repeat(3, 1000000))
1.3923049870645627
Needless to say all this adds up if these operations are done repeatedly and with bigger lists.
Also avoid:
out = "output: " + output + ", message: " + message + ", param: " + param
Instead, use:
out = "output: %s, message: %s, param: %s" % (output, message ,param, )
Which neatly combines rules 1 and 4.
5. Environment settings and variables are available cross-platform.
This is a very handy feature. Take a close look at os.path.expanduser() and os.environ on Linux and Windows.
*Nix:
>>> import os
>>> os.path.join(os.path.expanduser('~'))
'/home/jcastro/'
Windows:
>>> import os
>>> os.path.join(os.path.expanduser('~'))
'C:'
Useful Online Resources
The Python Coding Conventions
Python Performance Tips
Patterns in Python
Data Structures and Algorithms with Object-Oriented Design Patterns in Python
The Python Tutor Mailing List
My Python links on del.icio.us
Posted by
Unknown
at
Friday, July 21, 2006
1 comments
Shown tonight, during the opening of Superman Returns
More here.
Posted by
Unknown
at
Saturday, July 15, 2006
0
comments
I had a Malay housemate called Kamarun Kamarundil (he would, when introduced, always give the shorter version Nick).
I have a Thai friend called Kamontip Sapphawaht.
I absolutely love their names, and can already see the trailer for a Hollywood romantic blockbuster titled When Kamarun Kamarundil met Kamontip Sapphawaht...
Posted by
Unknown
at
Sunday, June 18, 2006
0
comments
On a letter dated November 2005 that I only had access to yesterday, I found out that my thesis was awarded the "John Long Prize for best research thesis"!
Thank you very much to all those mentioned in my acknowledgments section (and maybe a few others who were sadly forgotten)
If there is any cash involved a promise will be made right here and now to spend (some of) it well and wisely on a nice open BBQ with free drinks for all!
(sadly, no cash prize = no free BBQ+drinks for all)
Now ain't I a happy, lucky chap...
Labels: jiim, research, thesis, ucl
Posted by
Unknown
at
Monday, June 12, 2006
1 comments
Things I am missing from Box.net:
Posted by
Unknown
at
Monday, May 01, 2006
1 comments
There is a group of blogs that I check once daily, labelled "important" on my Bloglines blogroll. When new blogs are promoted to this "elite" group other blogs are demoted to the "Quarentine" one. (helpful taxonomy inspired by this, this, and David Allen's Getting Things Done)
Below are 5 blogs of "elite quality" that I'm really enjoying reading.
All Kinds Of Stuff
John Kricfalusi is no other than the creator of the Ren and Stimpy tv series. With these credentials he didn't need much more to convince me, but you know what, his blog is very refreshing and inundated with precious teachings.
Let The Good Times Roll
Here's how to suck up to a blogger: when I grow up I'd love to be as charismatic, inspirational, charming, and overflowing with wisdom as Guy Kawasaki.
AVC
Fred talks from the Venture Capital world in ways that I can understand.
Enplaned
This is how the blogosphere works: Up until Joel mentioned them, I never thought I'd be interested in reading about the aviation industry. Now I can't stop.
Epsilon-Delta: Mathematics and Computer Programming
Yes, I like mathematics and I yes, maybe I can't stay too far away from computer programming. But I'll be damned if Ted Dziuba isn't a talented writer, able to make these two "scary-for-most-people" subjects attractive.
Runners up: The Dilbert Blog, Funny Cute, Post Secret, Tom Peter's Weblog, Niniane's blog
Related posts:
2005 In Retrospect: Technology
2005 In Retrospect: Music
Posted by
Unknown
at
Wednesday, April 05, 2006
1 comments
I have been following Dav Yaginuma's blog, AkuAku, for a few years now. It is undeniable that he is a proper hacker and coder extraordinaire. To me, he also comes across as an inspirational, passionate, creative, dedicated, and humane individual. In this post, he introduced me to Kiva, a site where people can loan money to small business in developing countries. I like the empowering idea of a loan free of middle man, so I too have opened an account and donated.
Cheers Dav.
Posted by
Unknown
at
Wednesday, April 05, 2006
0
comments
Despite its evident flaws (and the fact that my feminine side is highly exacerbated by their algorithm), I really like MyHeritage.
I uploaded Sunday's pictures to find out a bit more about my friends. It turns out they have a lot to explain...
Deanna's real name is Maggie Cheung, Jia and Zhang Ziyi have never been seen together in the same place (raising suspicions they're one and the same), Peter is a Nicolas Cage clone, and Rachel and Alyson Hannigan are twins separated at birth. [Click on the images to enlarge]
Jang Nara is no one but Rachel in disguise, Willo's part-time-job-we-were-never-allowed-to-know-about is modelling as Song Hye-Kyo, and my good friend Bo goes undercover as the philosopher John Dewey.
Sadly, the system didn't pick up my face :(
Posted by
Unknown
at
Tuesday, April 04, 2006
0
comments
Dinner at my place: not enough plates, wine glasses, chairs, and cutlery (!), but still good fun. Great to see everybody and catch up with what they're doing.
Posted by
Unknown
at
Tuesday, April 04, 2006
0
comments